diff --git a/.github/codecov.yml b/.github/codecov.yml index 36dcfddd..ab9da736 100644 --- a/.github/codecov.yml +++ b/.github/codecov.yml @@ -1,5 +1,3 @@ -# Copyright (c) Tetrate, Inc 2024 All Rights Reserved. - codecov: # we build and upload only a single coverage file, so we don't need to wait for other CI # jobs to complete for us to see the coverage results diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 570e70a9..241b7967 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -1,5 +1,3 @@ -# Copyright (c) Tetrate, Inc 2024 All Rights Reserved. - name: CI on: @@ -111,16 +109,9 @@ jobs: with: token: ${{ secrets.CODECOV_TOKEN }} - docker: + e2e: needs: check runs-on: ubuntu-latest - services: - registry: - image: registry:2 - ports: - - 5000:5000 - env: - DOCKER_HUB: localhost:5000 steps: - uses: actions/setup-go@v3 with: @@ -134,4 +125,4 @@ jobs: key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} restore-keys: | ${{ runner.os }}-go- - - run: make docker + - run: make docker e2e diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index af1b837d..a232b3c6 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -1,5 +1,3 @@ -# Copyright (c) Tetrate, Inc 2024 All Rights Reserved. - name: Publish on: @@ -36,4 +34,4 @@ jobs: shell: bash - run: make check - - run: make docker + - run: make docker-push diff --git a/.gitignore b/.gitignore index a992d339..c5cba90b 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ bin/ .idea .makerc .vimrc +logs/ diff --git a/.licenserignore b/.licenserignore index bcef27a5..49a6bcc3 100644 --- a/.licenserignore +++ b/.licenserignore @@ -7,3 +7,5 @@ go.sum config/**/*.go bin/ .vimrc +*.json +LICENSE diff --git a/Makefile b/Makefile index 9d8463aa..48b4fc1e 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,7 @@ NAME ?= authservice PKG ?= ./cmd BUILD_OPTS ?= TEST_OPTS ?= -TEST_PKGS ?= ./... +TEST_PKGS ?= $(shell go list ./... | grep -v /e2e) OUTDIR ?= bin TARGETS ?= linux-amd64 linux-arm64 #darwin-amd64 darwin-arm64 @@ -65,7 +65,7 @@ $(OUTDIR)/$(NAME)-static-%: $(OUTDIR) -o $@ $(PKG) .PHONY: clean -clean: ## Clean the build artifacts +clean: clean/e2e ## Clean the build artifacts @rm -rf $(OUTDIR) .PHONY: clean/coverage @@ -73,9 +73,13 @@ clean/coverage: ## Clean the coverage report @rm -rf $(OUTDIR)/coverage .PHONY: clean/all -clean/all: clean config/clean ## Clean everything +clean/all: clean config/clean ## Clean everything @rm -rf $(OUTDIR) +.PHONY: clean/e2e +clean/e2e: ## Clean the e2e test artifacts + @$(MAKE) -C $(@F) $(@D) + ##@ Config Proto targets @@ -111,11 +115,15 @@ coverage: ## Creates coverage report for all projects $(COVERAGE_PACKAGES) @go tool cover -html="$(OUTDIR)/$@/coverage.out" -o "$(OUTDIR)/$@/coverage.html" +.PHONY: e2e +e2e: + @$(MAKE) -C e2e e2e + ##@ Docker targets .PHONY: docker-pre -docker-pre: +docker-pre: $(DOCKER_TARGETS:%=$(OUTDIR)/$(NAME)-static-%) @docker buildx inspect $(DOCKER_BUILDER_NAME) || \ docker buildx create --name $(DOCKER_BUILDER_NAME) \ --driver docker-container --driver-opt network=host \ @@ -125,10 +133,29 @@ comma := , space := $(empty) $(empty) PLATFORMS := $(subst -,/,$(subst $(space),$(comma),$(DOCKER_TARGETS))) INSECURE_REGISTRY_ARG := --output=type=registry,registry.insecure=true + .PHONY: docker -docker: $(DOCKER_TARGETS:%=$(OUTDIR)/$(NAME)-static-%) docker-pre ## Build and push the multi-arch Docker images +docker: $(DOCKER_TARGETS:%=docker-%) ## Build the docker images + +docker-%: PLATFORM=$(subst -,/,$(*)) +docker-%: ARCH=$(notdir $(subst -,/,$(PLATFORM))) +docker-%: docker-pre $(OUTDIR)/$(NAME)-static-% + @echo "Building Docker image $(DOCKER_HUB)/$(NAME):$(DOCKER_TAG)-$(ARCH)" + @docker buildx build \ + $(DOCKER_BUILD_ARGS) \ + --builder $(DOCKER_BUILDER_NAME) \ + --load \ + -f Dockerfile \ + --platform $(PLATFORM) \ + -t $(DOCKER_HUB)/$(NAME):latest-$(ARCH) \ + -t $(DOCKER_HUB)/$(NAME):$(DOCKER_TAG)-$(ARCH) \ + . + +.PHONY: docker-push +docker-push: docker-pre ## Build and push the multi-arch Docker images @echo "Pushing Docker image $(DOCKER_HUB)/$(NAME):$(DOCKER_TAG)" - @docker buildx build $(DOCKER_BUILD_ARGS) \ + @docker buildx build \ + $(DOCKER_BUILD_ARGS) \ --builder $(DOCKER_BUILDER_NAME) \ $(if $(USE_INSECURE_REGISTRY),$(INSECURE_REGISTRY_ARG),--push) \ -f Dockerfile \ diff --git a/cmd/main.go b/cmd/main.go index 042fa6ab..f69462ab 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -18,11 +18,12 @@ import ( "fmt" "os" - "github.com/tetrateio/authservice-go/internal" - "github.com/tetrateio/authservice-go/internal/server" "github.com/tetratelabs/log" "github.com/tetratelabs/run" "github.com/tetratelabs/run/pkg/signal" + + "github.com/tetrateio/authservice-go/internal" + "github.com/tetrateio/authservice-go/internal/server" ) func main() { diff --git a/config/gen/go/v1/config.pb.go b/config/gen/go/v1/config.pb.go index 5b9609a0..38325575 100644 --- a/config/gen/go/v1/config.pb.go +++ b/config/gen/go/v1/config.pb.go @@ -21,13 +21,15 @@ package configv1 import ( + reflect "reflect" + sync "sync" + _ "github.com/envoyproxy/protoc-gen-validate/validate" - mock "github.com/tetrateio/authservice-go/config/gen/go/v1/mock" - oidc "github.com/tetrateio/authservice-go/config/gen/go/v1/oidc" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" + + mock "github.com/tetrateio/authservice-go/config/gen/go/v1/mock" + oidc "github.com/tetrateio/authservice-go/config/gen/go/v1/oidc" ) const ( diff --git a/config/gen/go/v1/mock/config.pb.go b/config/gen/go/v1/mock/config.pb.go index 5b934e39..c8b9a8b9 100644 --- a/config/gen/go/v1/mock/config.pb.go +++ b/config/gen/go/v1/mock/config.pb.go @@ -21,10 +21,11 @@ package mock import ( - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" + + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" ) const ( diff --git a/config/gen/go/v1/oidc/config.pb.go b/config/gen/go/v1/oidc/config.pb.go index c4ea704c..e6bc8fcb 100644 --- a/config/gen/go/v1/oidc/config.pb.go +++ b/config/gen/go/v1/oidc/config.pb.go @@ -21,11 +21,12 @@ package oidc import ( + reflect "reflect" + sync "sync" + _ "github.com/envoyproxy/protoc-gen-validate/validate" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" ) const ( diff --git a/e2e/Makefile b/e2e/Makefile new file mode 100644 index 00000000..ea3f4051 --- /dev/null +++ b/e2e/Makefile @@ -0,0 +1,28 @@ +# Copyright 2024 Tetrate +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +SUITES := mock + +.PHONY: e2e +e2e: $(SUITES:%=e2e/%) ## Run all e2e tests + +e2e/%: + @$(MAKE) -C $(@F) $(@D) + +.PHONY: clean +clean: $(SUITES:%=clean/%) + +clean/%: + @$(MAKE) -C $(@F) $(@D) diff --git a/e2e/mock/Makefile b/e2e/mock/Makefile new file mode 100644 index 00000000..5771aa1c --- /dev/null +++ b/e2e/mock/Makefile @@ -0,0 +1,39 @@ +# Copyright 2024 Tetrate +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +.PHONY: e2e +e2e: e2e-pre + @go test $(E2E_TEST_OPTS) ./... || ( $(MAKE) e2e-test-post-error; exit 1 ) + @$(MAKE) e2e-test-post + +.PHONY: e2e-pre +e2e-pre: + @docker compose up --detach --wait --force-recreate --remove-orphans || ($(MAKE) e2e-test-post-error; exit 1) + +.PHONY: e2e-test-post +e2e-test-post: + @docker compose down + +.PHONY: e2e-test-post-error +e2e-test-post-error: capture-logs + +.PHONY: capture-logs +capture-logs: + @mkdir -p ./logs + @docker compose logs > logs/docker-compose-logs.log + +.PHONY: clean +clean: + @rm -rf ./logs diff --git a/e2e/mock/authz-config.json b/e2e/mock/authz-config.json new file mode 100644 index 00000000..d698ad5d --- /dev/null +++ b/e2e/mock/authz-config.json @@ -0,0 +1,43 @@ +{ + "log_level": "debug", + "chains": [ + { + "name": "mock-allow", + "match": { + "header": "X-Authz-Decision", + "equality": "allow" + }, + "filters": [ + { + "mock": { + "allow": true + } + } + ] + }, + { + "name": "mock-allow-prefix", + "match": { + "header": "X-Authz-Decision", + "prefix": "ok" + }, + "filters": [ + { + "mock": { + "allow": true + } + } + ] + }, + { + "name": "mock-deny", + "filters": [ + { + "mock": { + "allow": false + } + } + ] + } + ] +} diff --git a/e2e/mock/docker-compose.yaml b/e2e/mock/docker-compose.yaml new file mode 100644 index 00000000..c1bb9b12 --- /dev/null +++ b/e2e/mock/docker-compose.yaml @@ -0,0 +1,35 @@ +# Copyright 2024 Tetrate +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +version: "3.9" + +services: + envoy: + image: envoyproxy/envoy:v1.29-latest + platform: linux/arm64 + command: -c /etc/envoy/envoy-config.yaml --log-level warning + ports: + - "8080:80" # Make it accessible from the host (HTTP traffic) + volumes: + - type: bind + source: envoy-config.yaml + target: /etc/envoy/envoy-config.yaml + + ext-authz: + image: gcr.io/tetrate-internal-containers/authservice:latest-${ARCH:-amd64} + platform: linux/${ARCH:-amd64} + volumes: + - type: bind + source: authz-config.json + target: /etc/authservice/config.json diff --git a/e2e/mock/envoy-config.yaml b/e2e/mock/envoy-config.yaml new file mode 100644 index 00000000..1c429acf --- /dev/null +++ b/e2e/mock/envoy-config.yaml @@ -0,0 +1,76 @@ +# Copyright 2024 Tetrate +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +bootstrap_extensions: + - name: envoy.bootstrap.internal_listener + typed_config: + "@type": type.googleapis.com/envoy.extensions.bootstrap.internal_listener.v3.InternalListener + +static_resources: + listeners: + - name: http + address: + socket_address: + address: 0.0.0.0 + port_value: 80 + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: http + access_log: + - name: envoy.access_loggers.stdout + typed_config: + "@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog + route_config: + name: http + virtual_hosts: + - name: http + domains: ["*"] + routes: + - match: + prefix: "/" + direct_response: + status: 200 + body: + inline_string: "Access allowed\n" + http_filters: + - name: envoy.filters.http.ext_authz + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz + transport_api_version: V3 + grpc_service: + envoy_grpc: + cluster_name: ext_authz + timeout: 300s + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + + clusters: + - name: ext_authz + connect_timeout: 0.25s + type: LOGICAL_DNS + lb_policy: ROUND_ROBIN + http2_protocol_options: {} + load_assignment: + cluster_name: ext_authz + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: ext-authz + port_value: 10004 diff --git a/e2e/mock/mock_test.go b/e2e/mock/mock_test.go new file mode 100644 index 00000000..ac8247a4 --- /dev/null +++ b/e2e/mock/mock_test.go @@ -0,0 +1,66 @@ +// Copyright 2024 Tetrate +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package mock + +import ( + "net/http" + "testing" + + "github.com/stretchr/testify/require" +) + +const ( + testEnvoyURL = "http://localhost:8080" + testAuthzHeader = "X-Authz-Decision" +) + +func TestMock(t *testing.T) { + t.Run("allow-equality", func(t *testing.T) { + req, err := http.NewRequest("GET", testEnvoyURL, nil) + require.NoError(t, err) + req.Header.Set(testAuthzHeader, "allow") + + res, err := http.DefaultClient.Do(req) + require.NoError(t, err) + require.Equal(t, 200, res.StatusCode) + }) + + t.Run("allow-prefix", func(t *testing.T) { + req, err := http.NewRequest("GET", testEnvoyURL, nil) + require.NoError(t, err) + req.Header.Set(testAuthzHeader, "ok-prefix") + + res, err := http.DefaultClient.Do(req) + require.NoError(t, err) + require.Equal(t, 200, res.StatusCode) + }) + + t.Run("deny-header", func(t *testing.T) { + req, err := http.NewRequest("GET", testEnvoyURL, nil) + require.NoError(t, err) + req.Header.Set(testAuthzHeader, "non-match") + + res, err := http.DefaultClient.Do(req) + require.NoError(t, err) + require.Equal(t, 403, res.StatusCode) + }) + + t.Run("deny", func(t *testing.T) { + res, err := http.Get(testEnvoyURL) + + require.NoError(t, err) + require.Equal(t, 403, res.StatusCode) + }) +} diff --git a/internal/config.go b/internal/config.go index fabe6de1..2219d70b 100644 --- a/internal/config.go +++ b/internal/config.go @@ -1,4 +1,16 @@ -// Copyright (c) Tetrate, Inc 2024 All Rights Reserved. +// Copyright 2024 Tetrate +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package internal @@ -6,9 +18,10 @@ import ( "errors" "os" - configv1 "github.com/tetrateio/authservice-go/config/gen/go/v1" "github.com/tetratelabs/run" - "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/encoding/protojson" + + configv1 "github.com/tetrateio/authservice-go/config/gen/go/v1" ) var _ run.Config = (*LocalConfigFile)(nil) @@ -44,5 +57,5 @@ func (l *LocalConfigFile) Validate() error { return err } - return proto.Unmarshal(content, &l.Config) + return protojson.Unmarshal(content, &l.Config) } diff --git a/internal/config_test.go b/internal/config_test.go new file mode 100644 index 00000000..3ab2aefb --- /dev/null +++ b/internal/config_test.go @@ -0,0 +1,51 @@ +// Copyright 2024 Tetrate +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package internal + +import ( + "os" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestLoadConfig(t *testing.T) { + tests := []struct { + name string + path string + err error + }{ + {"empty", "", ErrInvalidPath}, + {"invalid", "unexisting", os.ErrNotExist}, + {"valid", "testdata/mock.json", nil}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cfg := LocalConfigFile{path: tt.path} + require.ErrorIs(t, cfg.Validate(), tt.err) + }) + } +} + +func TestLoadMock(t *testing.T) { + cfg := LocalConfigFile{path: "testdata/mock.json"} + + require.NoError(t, cfg.Validate()) + require.Len(t, cfg.Config.Chains, 1) + require.Equal(t, "mock", cfg.Config.Chains[0].Name) + require.Len(t, cfg.Config.Chains[0].Filters, 1) + require.True(t, cfg.Config.Chains[0].Filters[0].GetMock().Allow) +} diff --git a/internal/logging.go b/internal/logging.go index 1098e0ba..76123095 100644 --- a/internal/logging.go +++ b/internal/logging.go @@ -19,10 +19,11 @@ import ( "fmt" "strings" - configv1 "github.com/tetrateio/authservice-go/config/gen/go/v1" "github.com/tetratelabs/run" "github.com/tetratelabs/telemetry" "github.com/tetratelabs/telemetry/scope" + + configv1 "github.com/tetrateio/authservice-go/config/gen/go/v1" ) const ( diff --git a/internal/logging_test.go b/internal/logging_test.go index 61b9b5d2..a1b080f8 100644 --- a/internal/logging_test.go +++ b/internal/logging_test.go @@ -18,10 +18,11 @@ import ( "testing" "github.com/stretchr/testify/require" - configv1 "github.com/tetrateio/authservice-go/config/gen/go/v1" "github.com/tetratelabs/run" "github.com/tetratelabs/telemetry" "github.com/tetratelabs/telemetry/scope" + + configv1 "github.com/tetrateio/authservice-go/config/gen/go/v1" ) func TestLoggingSetup(t *testing.T) { diff --git a/internal/server/authz.go b/internal/server/authz.go index ec89bc5c..db528e17 100644 --- a/internal/server/authz.go +++ b/internal/server/authz.go @@ -1,20 +1,34 @@ -// Copyright (c) Tetrate, Inc 2024 All Rights Reserved. +// Copyright 2024 Tetrate +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package server import ( "context" "fmt" + "strings" envoy "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3" - configv1 "github.com/tetrateio/authservice-go/config/gen/go/v1" - mockv1 "github.com/tetrateio/authservice-go/config/gen/go/v1/mock" - oidcv1 "github.com/tetrateio/authservice-go/config/gen/go/v1/oidc" - "github.com/tetrateio/authservice-go/internal" "github.com/tetratelabs/telemetry" "google.golang.org/genproto/googleapis/rpc/status" "google.golang.org/grpc" "google.golang.org/grpc/codes" + + configv1 "github.com/tetrateio/authservice-go/config/gen/go/v1" + mockv1 "github.com/tetrateio/authservice-go/config/gen/go/v1/mock" + oidcv1 "github.com/tetrateio/authservice-go/config/gen/go/v1/oidc" + "github.com/tetrateio/authservice-go/internal" ) var ( @@ -58,41 +72,45 @@ func (e *ExtAuthZFilter) Register(server *grpc.Server) { // Check is the implementation of the Envoy AuthorizationServer interface. func (e *ExtAuthZFilter) Check(ctx context.Context, req *envoy.CheckRequest) (response *envoy.CheckResponse, err error) { - log := e.log.Context(ctx) - for _, c := range e.cfg.Chains { - if matches(c.Match, req) { - log = log.With("chain", c.Name) - log.Debug("matched filter chain") - - // Inside a filter chain, all filters must match - for i, f := range c.Filters { - var ( - ok bool - err error - ) - - switch ft := f.Type.(type) { - case *configv1.Filter_Mock: - e.log.Debug("applying filter", "type", "mock", "index", i) - ok, err = e.checkMock(context.Background(), req, ft.Mock) - case *configv1.Filter_Oidc: - e.log.Debug("applying filter", "type", "oidc", "index", i) - ok, err = e.checkOidc(context.Background(), req, ft.Oidc) - case *configv1.Filter_OidcOverride: - e.log.Debug("applying filter", "type", "oidc_override", "index", i) - ok, err = e.checkOidc(context.Background(), req, ft.OidcOverride) - } - - // If there is an error just return it without a verdict, and let the Envoy ext_authz - // failure policy decide if the request can continue or not. - if err != nil { - return nil, err - } - - if !ok { - return deny(codes.PermissionDenied, fmt.Sprintf("%s[%d] filter denied the request", c.Name, i)), nil - } + match := matches(c.Match, req) + + log := e.log.Context(ctx).With("chain", c.Name) + log.Debug("evaluate filter chain", "match", match) + + if !match { + continue + } + + // Inside a filter chain, all filters must match + for i, f := range c.Filters { + var ( + ok bool + err error + ) + + switch ft := f.Type.(type) { + case *configv1.Filter_Mock: + e.log.Debug("applying filter", "type", "mock", "index", i) + ok, err = e.checkMock(context.Background(), req, ft.Mock) + case *configv1.Filter_Oidc: + e.log.Debug("applying filter", "type", "oidc", "index", i) + ok, err = e.checkOidc(context.Background(), req, ft.Oidc) + case *configv1.Filter_OidcOverride: + e.log.Debug("applying filter", "type", "oidc_override", "index", i) + ok, err = e.checkOidc(context.Background(), req, ft.OidcOverride) + } + + // If there is an error just return it without a verdict, and let the Envoy ext_authz + // failure policy decide if the request can continue or not. + if err != nil { + return nil, err + } + + log.Debug("filter evaluation", "index", i, "result", ok, "error", err) + + if !ok { + return deny(codes.PermissionDenied, fmt.Sprintf("%s[%d] filter denied the request", c.Name, i)), nil } // Use the first filter chain that matches @@ -119,7 +137,13 @@ func (e *ExtAuthZFilter) checkOidc(_ context.Context, _ *envoy.CheckRequest, _ * } // matches returns true if the given request matches the given match configuration -func matches(_ *configv1.Match, _ *envoy.CheckRequest) bool { - // TODO - return true +func matches(m *configv1.Match, req *envoy.CheckRequest) bool { + if m == nil { + return true + } + headerValue := req.GetAttributes().GetRequest().GetHttp().GetHeaders()[strings.ToLower(m.Header)] + if m.GetEquality() != "" { + return headerValue == m.GetEquality() + } + return strings.HasPrefix(headerValue, m.GetPrefix()) } diff --git a/internal/server/logging.go b/internal/server/logging.go index 95fdc08e..5571bb5a 100644 --- a/internal/server/logging.go +++ b/internal/server/logging.go @@ -1,4 +1,16 @@ -// Copyright (c) Tetrate, Inc 2024 All Rights Reserved. +// Copyright 2024 Tetrate +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package server diff --git a/internal/server/server.go b/internal/server/server.go index 93061999..15813b61 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -1,4 +1,16 @@ -// Copyright (c) Tetrate, Inc 2024 All Rights Reserved. +// Copyright 2024 Tetrate +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package server @@ -7,10 +19,11 @@ import ( "fmt" "net" - "github.com/tetrateio/authservice-go/internal" "github.com/tetratelabs/run" "github.com/tetratelabs/telemetry" "google.golang.org/grpc" + + "github.com/tetrateio/authservice-go/internal" ) // RegisterGrpc is an interface for registering gRPC registerHandlers. @@ -55,7 +68,7 @@ func (s *Server) Name() string { return "gRPC Server" } // FlagSet returns the flags used to customize the server. func (s *Server) FlagSet() *run.FlagSet { flags := run.NewFlagSet("gRPC Server flags") - flags.StringVar(&s.addr, "listen-address", ":9090", "listen address") + flags.StringVar(&s.addr, "listen-address", ":10004", "listen address") return flags } diff --git a/internal/server/server_test.go b/internal/server/server_test.go index 655e465d..3865a654 100644 --- a/internal/server/server_test.go +++ b/internal/server/server_test.go @@ -1,4 +1,16 @@ -// Copyright (c) Tetrate, Inc 2024 All Rights Reserved. +// Copyright 2024 Tetrate +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package server diff --git a/internal/testdata/mock.json b/internal/testdata/mock.json new file mode 100644 index 00000000..eee09347 --- /dev/null +++ b/internal/testdata/mock.json @@ -0,0 +1,15 @@ +{ + "log_level": "debug", + "chains": [ + { + "name": "mock", + "filters": [ + { + "mock": { + "allow": true + } + } + ] + } + ] +}