Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Support for envoy gRPC v3 external authorization API #469

Merged
merged 77 commits into from
Mar 6, 2023
Merged
Changes from 1 commit
Commits
Show all changes
77 commits
Select commit Hold shift + click to select a range
dba4695
initial commit for envoy grpc support
dadrus Jan 29, 2023
dd46c22
access context subject and error handling moved to its own package
dadrus Jan 30, 2023
137bd75
further implementation
dadrus Jan 31, 2023
39cf037
package structure refactored
dadrus Jan 31, 2023
362f41c
small updates in descriptions
dadrus Jan 31, 2023
b0cf884
metris implementation updated to take tunneled http requests from env…
dadrus Jan 31, 2023
602e667
type label added
dadrus Jan 31, 2023
e4fc676
type label changed
dadrus Jan 31, 2023
478ae0d
Merge branch 'main' into feat/envoy_grpc
dadrus Jan 31, 2023
94f47b0
linter warnings resolved
dadrus Feb 1, 2023
c9a30ad
tests fixed
dadrus Feb 1, 2023
8c40761
additional labels for metrics removed
dadrus Feb 1, 2023
e35ec75
namcpace set to grpc
dadrus Feb 1, 2023
5fbda2e
Merge branch 'main' into feat/envoy_grpc
dadrus Feb 1, 2023
94bdc35
access logger implementation simplified
dadrus Feb 2, 2023
a65c95e
further simplifications
dadrus Feb 2, 2023
63ebc47
unused parameters
dadrus Feb 2, 2023
8465e26
log message changed
dadrus Feb 3, 2023
9b5d5f0
grpc error handler middleware implementation enhanced
dadrus Feb 3, 2023
0a19758
Merge branch 'main' into feat/envoy_grpc
dadrus Feb 6, 2023
30b7565
idle timeout configured
dadrus Feb 8, 2023
3d0c2f7
Merge branch 'main' into feat/envoy_grpc
dadrus Feb 8, 2023
17a7491
Merge branch 'main' into feat/envoy_grpc
dadrus Feb 8, 2023
3eb4e2c
method renamed
dadrus Feb 8, 2023
81ea03c
header canonicalization and some simplifications
dadrus Feb 8, 2023
fa36179
tests for grpc access log interceptor
dadrus Feb 9, 2023
45cc2e0
some renamings & more tests
dadrus Feb 9, 2023
d103adb
linter warnings disabled for specific places as errors are irrelevant
dadrus Feb 9, 2023
2facb5f
small refactorings & simplifications
dadrus Feb 9, 2023
134f418
some renaming and more tests
dadrus Feb 9, 2023
12d9fb4
functionality related to format negotiation on verbose errors moved t…
dadrus Feb 9, 2023
66dffeb
comment added
dadrus Feb 9, 2023
9fb141d
linter enabled again
dadrus Feb 9, 2023
485891b
file renamed
dadrus Feb 9, 2023
6472ea4
error content negotiation simplified & more tests
dadrus Feb 10, 2023
494e2cb
forgotten go.mod & sum
dadrus Feb 10, 2023
0f4adcf
http label names updated to be more meaningfull especially, when the …
dadrus Feb 10, 2023
3ada333
fixes and more tests
dadrus Feb 10, 2023
6254ab6
license header added
dadrus Feb 11, 2023
71c3a8d
Merge branch 'main' into feat/envoy_grpc
dadrus Feb 22, 2023
5a16c5b
more tests and handling of unknown services/methods in access log and…
dadrus Mar 3, 2023
ae7fb34
Merge branch 'main' into feat/envoy_grpc
dadrus Mar 3, 2023
1969e41
linter warnings resolved
dadrus Mar 3, 2023
d2906d8
license header added
dadrus Mar 3, 2023
7664ff9
some updates to old tests
dadrus Mar 3, 2023
8aba38a
small test refactorings to simplify them
dadrus Mar 3, 2023
ebd7632
new tests and fixes for found bugs
dadrus Mar 3, 2023
438f18d
more tests
dadrus Mar 3, 2023
42ec809
new tests and fixes for identified bugs
dadrus Mar 3, 2023
b9691c2
test enhanced
dadrus Mar 3, 2023
0b40826
new test to bootstrap envoyproxy decision service
dadrus Mar 3, 2023
fd8bfd3
metrics configured in test
dadrus Mar 3, 2023
96c5a5b
mock moved
dadrus Mar 3, 2023
37762a3
Merge branch 'main' into feat/envoy_grpc
dadrus Mar 3, 2023
6d2328d
new test
dadrus Mar 3, 2023
e125e59
Merge branch 'main' into feat/envoy_grpc
dadrus Mar 4, 2023
fb319b8
logged accesslog events updated
dadrus Mar 4, 2023
4bc7a8a
additional check to avoid nil map access
dadrus Mar 4, 2023
dee4c86
using x-forwarded-for set by envoy to retrieve ips of the hops
dadrus Mar 4, 2023
d7814be
Merge branch 'main' into feat/envoy_grpc
dadrus Mar 4, 2023
25da4e1
linter warning resolved
dadrus Mar 5, 2023
78adaa1
using rpc status code instead of an HTTP one as required by the API
dadrus Mar 5, 2023
af8bb4c
test updated to check for rpc status code instead of HTTP one
dadrus Mar 5, 2023
c2c6e60
using codes from google package
dadrus Mar 5, 2023
a77ae26
proper use of grpc error codes
dadrus Mar 5, 2023
2266317
linter warnings resolved
dadrus Mar 5, 2023
25af22d
docker-compose quickstarts updateds to cover integration with envoy u…
dadrus Mar 5, 2023
8b29e0f
logging grpc status code
dadrus Mar 5, 2023
d5cdd5a
comment added
dadrus Mar 5, 2023
6b85527
linter warning resolved
dadrus Mar 5, 2023
8e38c3e
flag description updated
dadrus Mar 5, 2023
78ecabb
documentation added
dadrus Mar 5, 2023
b01429e
fla renamed
dadrus Mar 5, 2023
2dbaefd
documentation update
dadrus Mar 5, 2023
2c6c6ba
documentation update
dadrus Mar 5, 2023
fea779e
documentation update
dadrus Mar 5, 2023
06f196e
docker-compose quickstarts updated
dadrus Mar 6, 2023
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
Prev Previous commit
Next Next commit
access context subject and error handling moved to its own package
  • Loading branch information
dadrus committed Jan 30, 2023
commit dd46c228ce2102c369e0197cdd5e1edda0bf424e
58 changes: 58 additions & 0 deletions internal/accesscontext/access_context.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Copyright 2022 Dimitrij Drus <dadrus@gmx.de>
//
// 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.
//
// SPDX-License-Identifier: Apache-2.0

package accesscontext

import "context"

type ctxKey struct{}

type accessContext struct {
err error
subject string
}

func New(ctx context.Context) context.Context {
return context.WithValue(ctx, ctxKey{}, &accessContext{})
}

func Error(ctx context.Context) error {
if c, ok := ctx.Value(ctxKey{}).(*accessContext); ok {
return c.err
}

return nil
}

func SetError(ctx context.Context, err error) {
if c, ok := ctx.Value(ctxKey{}).(*accessContext); ok {
c.err = err
}
}

func Subject(ctx context.Context) string {
if c, ok := ctx.Value(ctxKey{}).(*accessContext); ok {
return c.subject
}

return ""
}

func SetSubject(ctx context.Context, subject string) {
if c, ok := ctx.Value(ctxKey{}).(*accessContext); ok {
c.subject = subject
}
}
38 changes: 0 additions & 38 deletions internal/fiber/middleware/accesslog/access_context.go

This file was deleted.

221 changes: 110 additions & 111 deletions internal/fiber/middleware/accesslog/accesslog_handler.go
Original file line number Diff line number Diff line change
@@ -17,132 +17,131 @@
package accesslog

import (
"context"
"time"
"time"

"github.com/gofiber/fiber/v2"
"github.com/rs/zerolog"
"github.com/gofiber/fiber/v2"
"github.com/rs/zerolog"

"github.com/dadrus/heimdall/internal/x/opentelemetry/tracecontext"
"github.com/dadrus/heimdall/internal/accesscontext"
"github.com/dadrus/heimdall/internal/x/opentelemetry/tracecontext"
)

func New(logger zerolog.Logger) fiber.Handler {
return func(c *fiber.Ctx) error {
start := time.Now()
traceCtx := tracecontext.Extract(c.UserContext())
alc := &accessContext{}
return func(c *fiber.Ctx) error {
start := time.Now()
traceCtx := tracecontext.Extract(c.UserContext())
c.SetUserContext(accesscontext.New(c.UserContext()))

c.SetUserContext(context.WithValue(c.UserContext(), ctxKey{}, alc))
accLog := createAccessLogger(c, logger, start, traceCtx)
accLog.Info().Msg("TX started")

accLog := createAccessLogger(c, logger, start, traceCtx)
accLog.Info().Msg("TX started")
err := c.Next()

err := c.Next()
createAccessLogFinalizationEvent(c, accLog, err, start, traceCtx).Msg("TX finished")

createAccessLogFinalizationEvent(c, accLog, err, start, alc, traceCtx).Msg("TX finished")

return err
}
return err
}
}

func createAccessLogger(
c *fiber.Ctx,
logger zerolog.Logger,
start time.Time,
traceCtx *tracecontext.TraceContext,
c *fiber.Ctx,
logger zerolog.Logger,
start time.Time,
traceCtx *tracecontext.TraceContext,
) zerolog.Logger {
startTime := start.Unix()

logCtx := logger.Level(zerolog.InfoLevel).With().
Int64("_tx_start", startTime).
Str("_client_ip", c.IP()).
Str("_http_method", c.Method()).
Str("_http_path", c.Path()).
Str("_http_user_agent", c.Get("User-Agent")).
Str("_http_host", string(c.Request().URI().Host())).
Str("_http_scheme", string(c.Request().URI().Scheme()))

if traceCtx != nil {
logCtx = logCtx.
Str("_trace_id", traceCtx.TraceID).
Str("_span_id", traceCtx.SpanID)

if len(traceCtx.ParentID) != 0 {
logCtx = logCtx.Str("_parent_id", traceCtx.ParentID)
}
}

if c.IsProxyTrusted() { // nolint: nestif
if headerValue := c.Get("X-Forwarded-Proto"); len(headerValue) != 0 {
logCtx = logCtx.Str("_http_x_forwarded_proto", headerValue)
}

if headerValue := c.Get("X-Forwarded-Host"); len(headerValue) != 0 {
logCtx = logCtx.Str("_http_x_forwarded_host", headerValue)
}

if headerValue := c.Get("X-Forwarded-Path"); len(headerValue) != 0 {
logCtx = logCtx.Str("_http_x_forwarded_path", headerValue)
}

if headerValue := c.Get("X-Forwarded-Uri"); len(headerValue) != 0 {
logCtx = logCtx.Str("_http_x_forwarded_uri", headerValue)
}

if headerValue := c.Get("X-Forwarded-For"); len(headerValue) != 0 {
logCtx = logCtx.Str("_http_x_forwarded_for", headerValue)
}

if headerValue := c.Get("Forwarded"); len(headerValue) != 0 {
logCtx = logCtx.Str("_http_forwarded", headerValue)
}
}

return logCtx.Logger()
startTime := start.Unix()

logCtx := logger.Level(zerolog.InfoLevel).With().
Int64("_tx_start", startTime).
Str("_client_ip", c.IP()).
Str("_http_method", c.Method()).
Str("_http_path", c.Path()).
Str("_http_user_agent", c.Get("User-Agent")).
Str("_http_host", string(c.Request().URI().Host())).
Str("_http_scheme", string(c.Request().URI().Scheme()))

if traceCtx != nil {
logCtx = logCtx.
Str("_trace_id", traceCtx.TraceID).
Str("_span_id", traceCtx.SpanID)

if len(traceCtx.ParentID) != 0 {
logCtx = logCtx.Str("_parent_id", traceCtx.ParentID)
}
}

if c.IsProxyTrusted() { // nolint: nestif
if headerValue := c.Get("X-Forwarded-Proto"); len(headerValue) != 0 {
logCtx = logCtx.Str("_http_x_forwarded_proto", headerValue)
}

if headerValue := c.Get("X-Forwarded-Host"); len(headerValue) != 0 {
logCtx = logCtx.Str("_http_x_forwarded_host", headerValue)
}

if headerValue := c.Get("X-Forwarded-Path"); len(headerValue) != 0 {
logCtx = logCtx.Str("_http_x_forwarded_path", headerValue)
}

if headerValue := c.Get("X-Forwarded-Uri"); len(headerValue) != 0 {
logCtx = logCtx.Str("_http_x_forwarded_uri", headerValue)
}

if headerValue := c.Get("X-Forwarded-For"); len(headerValue) != 0 {
logCtx = logCtx.Str("_http_x_forwarded_for", headerValue)
}

if headerValue := c.Get("Forwarded"); len(headerValue) != 0 {
logCtx = logCtx.Str("_http_forwarded", headerValue)
}
}

return logCtx.Logger()
}

func createAccessLogFinalizationEvent(
c *fiber.Ctx,
accessLogger zerolog.Logger,
err error,
start time.Time,
alc *accessContext,
traceCtx *tracecontext.TraceContext,
c *fiber.Ctx,
accessLogger zerolog.Logger,
err error,
start time.Time,
traceCtx *tracecontext.TraceContext,
) *zerolog.Event {
end := time.Now()
duration := end.Sub(start)

event := accessLogger.Info().
Int("_body_bytes_sent", len(c.Response().Body())).
Int("_http_status_code", c.Response().StatusCode()).
Int64("_tx_duration_ms", duration.Milliseconds())

if traceCtx != nil {
event = event.
Str("_trace_id", traceCtx.TraceID).
Str("_span_id", traceCtx.SpanID)

if len(traceCtx.ParentID) != 0 {
event = event.Str("_parent_id", traceCtx.ParentID)
}
}

switch {
case err != nil:
if len(alc.subject) != 0 {
event = event.Str("_subject", alc.subject)
}

event = event.Err(err).Bool("_access_granted", false)
case alc.err != nil:
if len(alc.subject) != 0 {
event = event.Str("_subject", alc.subject)
}

event = event.Err(alc.err).Bool("_access_granted", false)
default:
event = event.Str("_subject", alc.subject).Bool("_access_granted", true)
}

return event
end := time.Now()
duration := end.Sub(start)
subject := accesscontext.Subject(c.UserContext())
accessErr := accesscontext.Error(c.UserContext())

event := accessLogger.Info().
Int("_body_bytes_sent", len(c.Response().Body())).
Int("_http_status_code", c.Response().StatusCode()).
Int64("_tx_duration_ms", duration.Milliseconds())

if traceCtx != nil {
event = event.
Str("_trace_id", traceCtx.TraceID).
Str("_span_id", traceCtx.SpanID)

if len(traceCtx.ParentID) != 0 {
event = event.Str("_parent_id", traceCtx.ParentID)
}
}

switch {
case err != nil:
if len(subject) != 0 {
event = event.Str("_subject", subject)
}

event = event.Err(err).Bool("_access_granted", false)
case accessErr != nil:
if len(subject) != 0 {
event = event.Str("_subject", subject)
}

event = event.Err(accessErr).Bool("_access_granted", false)
default:
event = event.Str("_subject", subject).Bool("_access_granted", true)
}

return event
}
Loading