Skip to content

Commit

Permalink
feat: instrument shield proxy (#69)
Browse files Browse the repository at this point in the history
* feat: instrument shield proxy

* refactor: ioutil

* fix: test

* fix: enrich test
  • Loading branch information
mabdh authored Jun 11, 2024
1 parent cdd69cd commit 8b13e15
Show file tree
Hide file tree
Showing 166 changed files with 4,191 additions and 1,517 deletions.
47 changes: 46 additions & 1 deletion .mockery.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,54 @@ packages:
RelationTransformer:
config:
filename: "relation_transformer.go"
github.com/goto/shield/core/action:
config:
dir: "core/action/mocks"
outpkg: "mocks"
mockname: "{{.InterfaceName}}"
interfaces:
Repository:
config:
filename: "action_repository.go"
UserService:
config:
filename: "user_service.go"
ActivityService:
config:
filename: "activity_service.go"
github.com/goto/shield/core/policy:
config:
dir: "core/policy/mocks"
outpkg: "mocks"
mockname: "{{.InterfaceName}}"
interfaces:
Repository:
config:
filename: "policy_repository.go"
UserService:
config:
filename: "user_service.go"
ActivityService:
config:
filename: "activity_service.go"
github.com/goto/shield/core/role:
config:
dir: "core/role/mocks"
outpkg: "mocks"
mockname: "{{.InterfaceName}}"
interfaces:
Repository:
config:
filename: "role_repository.go"
UserService:
config:
filename: "user_service.go"
ActivityService:
config:
filename: "activity_service.go"
github.com/goto/shield/core/user:
config:
dir: "core/mocks"
dir: "core/user/mocks"
outpkg: "mocks"
mockname: "{{.InterfaceName}}"
interfaces:
Expand Down
16 changes: 10 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,24 @@ generate: ## run all go generate in the code base (including generating mock fil
@mockery

lint: ## Run linters
golangci-lint run
@golangci-lint run

format:
@echo "Running gofumpt..."
@gofumpt -l -w .

# TODO: create separate command for integration tests
test: ## Run tests
go test -race $(shell go list ./... | grep -v /vendor/ | grep -v /test/) -coverprofile=coverage.out
go test -race $(shell go list ./... | grep -v /mocks | grep -v /test/ | grep -v /proto/) -covermode=atomic -coverprofile=coverage.out

e2e-test: ## Run all e2e tests
go test -v -race ./test/e2e_test/... -coverprofile=coverage.out
go test -v -race ./test/e2e_test/... -covermode=atomic -coverprofile=coverage.out

e2e-smoke-test: ## Run smoke tests
go test -v -race ./test/e2e_test/smoke -coverprofile=coverage.out
go test -v -race ./test/e2e_test/smoke -covermode=atomic -coverprofile=coverage.out

e2e-regression-test: ## Run regression tests
go test -v -race ./test/e2e_test/regression -coverprofile=coverage.out
go test -v -race ./test/e2e_test/regression -covermode=atomic -coverprofile=coverage.out

benchmark: ## Run benchmarks
go test -run=XX -bench=Benchmark. -count 3 -benchtime=1s github.com/goto/shield/integration
Expand Down Expand Up @@ -62,4 +66,4 @@ doc: clean-doc ## Generate api and cli documentation

doc-build: ## Run documentation locally
@echo "> building docs"
@cd $(CURDIR)/docs/docs; yarn start
@cd $(CURDIR)/docs/docs; yarn start
2 changes: 1 addition & 1 deletion cmd/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func configInitCommand() *cobra.Command {
}

func configListCommand() *cobra.Command {
var cmd = &cobra.Command{
cmd := &cobra.Command{
Use: "list",
Short: "List client configuration settings",
Example: heredoc.Doc(`
Expand Down
2 changes: 1 addition & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
)

func New(cliConfig *Config) *cli.Command {
var cmd = &cli.Command{
cmd := &cli.Command{
Use: "shield <command> <subcommand> [flags]",
Short: "A cloud native role-based authorization aware reverse-proxy service",
Long: heredoc.Doc(`
Expand Down
48 changes: 26 additions & 22 deletions cmd/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (

_ "github.com/authzed/authzed-go/proto/authzed/api/v0"
_ "github.com/jackc/pgx/v4/stdlib"
newrelic "github.com/newrelic/go-agent"
"github.com/newrelic/go-agent/v3/newrelic"
"go.uber.org/zap"

"github.com/goto/shield/config"
Expand All @@ -39,13 +39,12 @@ import (
"github.com/goto/shield/pkg/db"

"github.com/goto/salt/log"
"github.com/goto/salt/telemetry"
"github.com/pkg/profile"
"google.golang.org/grpc/codes"
)

var (
ruleCacheRefreshDelay = time.Minute * 2
)
var ruleCacheRefreshDelay = time.Minute * 2

func StartServer(logger *log.Zap, cfg *config.Shield) error {
if profiling := os.Getenv("SHIELD_PROFILE"); profiling == "true" || profiling == "1" {
Expand All @@ -56,6 +55,13 @@ func StartServer(logger *log.Zap, cfg *config.Shield) error {
ctx, cancelFunc := signal.NotifyContext(context.Background(), syscall.SIGTERM, syscall.SIGINT)
defer cancelFunc()

cleanUpTelemetry, err := telemetry.Init(ctx, cfg.App.Telemetry, logger)
if err != nil {
return err
}

defer cleanUpTelemetry()

dbClient, err := setupDB(cfg.DB, logger)
if err != nil {
return err
Expand Down Expand Up @@ -244,26 +250,24 @@ func BuildAPIDependencies(
return dependencies, nil
}

func setupNewRelic(cfg config.NewRelic, logger log.Logger) (newrelic.Application, error) {
nrCfg := newrelic.NewConfig(cfg.AppName, cfg.License)
nrCfg.Enabled = cfg.Enabled
nrCfg.ErrorCollector.IgnoreStatusCodes = []int{
http.StatusNotFound,
http.StatusUnauthorized,
int(codes.Unauthenticated),
int(codes.PermissionDenied),
int(codes.InvalidArgument),
int(codes.AlreadyExists),
}

if nrCfg.Enabled {
nrApp, err := newrelic.NewApplication(nrCfg)
if err != nil {
return nil, errors.New("failed to load Newrelic Application")
func setupNewRelic(cfg config.NewRelic, logger log.Logger) (*newrelic.Application, error) {
nrApp, err := newrelic.NewApplication(func(nrCfg *newrelic.Config) {
nrCfg.Enabled = cfg.Enabled
nrCfg.AppName = cfg.AppName
nrCfg.ErrorCollector.IgnoreStatusCodes = []int{
http.StatusNotFound,
http.StatusUnauthorized,
int(codes.Unauthenticated),
int(codes.PermissionDenied),
int(codes.InvalidArgument),
int(codes.AlreadyExists),
}
return nrApp, nil
nrCfg.License = cfg.License
})
if err != nil {
return nil, errors.New("failed to load Newrelic Application")
}
return nil, nil
return nrApp, nil
}

func setupDB(cfg db.Config, logger log.Logger) (dbc *db.Client, err error) {
Expand Down
20 changes: 18 additions & 2 deletions cmd/serve_proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ import (
"github.com/goto/shield/internal/proxy/middleware/prefix"
"github.com/goto/shield/internal/proxy/middleware/rulematch"
"github.com/goto/shield/internal/store/blob"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
semconv "go.opentelemetry.io/otel/semconv/v1.20.0"
"go.opentelemetry.io/otel/trace"
)

func serveProxies(
Expand Down Expand Up @@ -83,7 +86,8 @@ func buildHookPipeline(
resourceService v1beta1.ResourceService,
relationService v1beta1.RelationService,
relationAdapter *adapter.Relation,
identityProxyHeaderKey string) hook.Service {
identityProxyHeaderKey string,
) hook.Service {
rootHook := hook.New()
return authz_hook.New(log, rootHook, rootHook, resourceService, relationService, relationAdapter, identityProxyHeaderKey)
}
Expand All @@ -104,7 +108,19 @@ func buildMiddlewarePipeline(
casbinAuthz := authz.New(logger, prefixWare, userIDHeaderKey, resourceService, userService, groupService)
basicAuthn := basic_auth.New(logger, casbinAuthz)
attributeExtractor := attributes.New(logger, basicAuthn, identityProxyHeaderKey, projectService)
matchWare := rulematch.New(logger, attributeExtractor, rulematch.NewRouteMatcher(ruleService))
otelMiddleware := otelhttp.NewHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
route := r.URL.Path
attr := semconv.HTTPRouteKey.String(route)

span := trace.SpanFromContext(r.Context())
span.SetAttributes(attr)

labeler, _ := otelhttp.LabelerFromContext(r.Context())
labeler.Add(attr)

attributeExtractor.ServeHTTP(w, r)
}), "")
matchWare := rulematch.New(logger, otelMiddleware, rulematch.NewRouteMatcher(ruleService))
observability := observability.New(logger, matchWare)
return observability
}
19 changes: 9 additions & 10 deletions config/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package config
import (
"embed"
"errors"
"io/ioutil"
"net/url"
"os"
"path"
Expand Down Expand Up @@ -61,11 +60,11 @@ func Init(resourcesURL, rulesURL, configFile string) error {

if _, err := os.Stat(configFile); os.IsNotExist(err) {
if !file.DirExists(configFile) {
_ = os.MkdirAll(filepath.Dir(configFile), 0755)
_ = os.MkdirAll(filepath.Dir(configFile), 0o755)
}
}

if err := ioutil.WriteFile(configFile, data, 0655); err != nil {
if err := os.WriteFile(configFile, data, 0o655); err != nil {
return err
}

Expand All @@ -85,10 +84,10 @@ func initResourcesPath(resURL string) error {

resourcesPath := resourceURL.Path
if !file.DirExists(resourcesPath) {
_ = os.MkdirAll(resourcesPath, 0755)
_ = os.MkdirAll(resourcesPath, 0o755)
}

files, err := ioutil.ReadDir(resourcesPath)
files, err := os.ReadDir(resourcesPath)
if err != nil {
return err
}
Expand All @@ -105,7 +104,7 @@ func initResourcesPath(resURL string) error {
return err
}

if err := ioutil.WriteFile(path.Join(resourcesPath, "resources.yaml"), resourceYaml, 0655); err != nil {
if err := os.WriteFile(path.Join(resourcesPath, "resources.yaml"), resourceYaml, 0o655); err != nil {
return err
}

Expand All @@ -126,10 +125,10 @@ func initRulesPath(rURL string) error {
rulesPath := rulesURL.Path

if !file.DirExists(rulesPath) {
_ = os.MkdirAll(rulesPath, 0755)
_ = os.MkdirAll(rulesPath, 0o755)
}

files, err := ioutil.ReadDir(rulesPath)
files, err := os.ReadDir(rulesPath)
if err != nil {
return err
}
Expand All @@ -146,7 +145,7 @@ func initRulesPath(rURL string) error {
return err
}

if err := ioutil.WriteFile(path.Join(rulesPath, "sample.rest.yaml"), ruleRestYaml, 0655); err != nil {
if err := os.WriteFile(path.Join(rulesPath, "sample.rest.yaml"), ruleRestYaml, 0o655); err != nil {
return err
}

Expand All @@ -155,7 +154,7 @@ func initRulesPath(rURL string) error {
return err
}

if err := ioutil.WriteFile(path.Join(rulesPath, "sample.grpc.yaml"), ruleRestGrpc, 0655); err != nil {
if err := os.WriteFile(path.Join(rulesPath, "sample.grpc.yaml"), ruleRestGrpc, 0o655); err != nil {
return err
}

Expand Down
12 changes: 6 additions & 6 deletions core/action/action.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import (
"time"
)

const auditEntity = "action"
const AuditEntity = "action"

type Repository interface {
Get(ctx context.Context, id string) (Action, error)
Create(ctx context.Context, action Action) (Action, error)
Upsert(ctx context.Context, action Action) (Action, error)
List(ctx context.Context) ([]Action, error)
Update(ctx context.Context, action Action) (Action, error)
}
Expand All @@ -22,16 +22,16 @@ type Action struct {
UpdatedAt time.Time
}

type ActionLogData struct {
type LogData struct {
Entity string `mapstructure:"entity"`
ID string `mapstructure:"id"`
Name string `mapstructure:"name"`
NamespaceID string `mapstructure:"namespace_id"`
}

func (action Action) ToActionLogData() ActionLogData {
return ActionLogData{
Entity: auditEntity,
func (action Action) ToLogData() LogData {
return LogData{
Entity: AuditEntity,
ID: action.ID,
Name: action.Name,
NamespaceID: action.NamespaceID,
Expand Down
Loading

0 comments on commit 8b13e15

Please sign in to comment.