From 5edfa7b03f373148e980c52c3389c6855220e59c Mon Sep 17 00:00:00 2001 From: Bobby DeSimone Date: Wed, 24 Jul 2019 09:20:16 -0700 Subject: [PATCH] telemetry: add tracing - telemetry/tace: add traces throughout code - telemetry/metrics: nest metrics and trace under telemetry - telemetry/tace: add service name span to HTTPMetricsHandler. - telemetry/metrics: removed chain dependency middleware_tests. - telemetry/metrics: wrap and encapsulate variatic view registration. - telemetry/tace: add jaeger support for tracing. - cmd/pomerium: move `parseOptions` to internal/config. - cmd/pomerium: offload server handling to httputil and sub pkgs. - httputil: standardize creation/shutdown of http listeners. - httputil: prefer curve X25519 to P256 when negotiating TLS. - fileutil: use standardized Getw Signed-off-by: Bobby DeSimone --- CHANGELOG.md | 38 ++-- authenticate/grpc.go | 12 +- authorize/authorize.go | 2 +- authorize/gprc.go | 6 + cmd/pomerium/main.go | 197 ++++++---------- cmd/pomerium/main_test.go | 192 ++++++++-------- docs/reference/readme.md | 40 +++- docs/reference/tracing/jaeger.png | Bin 0 -> 250605 bytes go.mod | 1 + go.sum | 37 +++ internal/config/helpers.go | 11 - internal/config/options.go | 73 +++++- internal/config/options_test.go | 96 ++++++++ internal/fileutil/fileutil.go | 14 ++ internal/fileutil/fileutil_test.go | 23 +- internal/httputil/errors.go | 14 +- internal/httputil/errors_test.go | 49 ++++ internal/httputil/http.go | 76 +++++++ internal/httputil/http_test.go | 49 ++++ internal/httputil/options.go | 87 ++++++++ internal/httputil/test_data/cert.pem | 10 + internal/httputil/test_data/privkey.pem | 5 + internal/httputil/{https.go => tls.go} | 92 ++------ internal/httputil/tls_test.go | 210 ++++++++++++++++++ internal/identity/providers.go | 9 +- internal/metrics/exporter.go | 30 --- internal/metrics/middleware.go | 151 ------------- internal/metrics/tags.go | 14 -- internal/metrics/view.go | 32 --- internal/metrics/view_test.go | 25 --- internal/middleware/grpc.go | 5 + internal/middleware/middleware.go | 30 ++- internal/middleware/reverse_proxy.go | 10 +- internal/telemetry/metrics/const.go | 41 ++++ .../metrics/grpc.go} | 78 ++++--- .../metrics/grpc_test.go} | 11 +- .../{ => telemetry}/metrics/helpers_test.go | 31 +-- internal/telemetry/metrics/http.go | 157 +++++++++++++ .../metrics/http_test.go} | 39 +++- internal/{ => telemetry}/metrics/info.go | 50 +++-- internal/{ => telemetry}/metrics/info_test.go | 8 +- internal/telemetry/metrics/providers.go | 39 ++++ .../metrics/providers_test.go} | 10 +- internal/telemetry/trace/trace.go | 74 ++++++ internal/telemetry/trace/trace_test.go | 23 ++ proxy/clients/authenticate_client.go | 36 ++- proxy/clients/authorize_client.go | 16 +- proxy/clients/clients.go | 3 +- proxy/proxy.go | 26 ++- 49 files changed, 1524 insertions(+), 758 deletions(-) create mode 100644 docs/reference/tracing/jaeger.png create mode 100644 internal/httputil/errors_test.go create mode 100644 internal/httputil/http.go create mode 100644 internal/httputil/http_test.go create mode 100644 internal/httputil/options.go create mode 100644 internal/httputil/test_data/cert.pem create mode 100644 internal/httputil/test_data/privkey.pem rename internal/httputil/{https.go => tls.go} (67%) create mode 100644 internal/httputil/tls_test.go delete mode 100644 internal/metrics/exporter.go delete mode 100644 internal/metrics/middleware.go delete mode 100644 internal/metrics/tags.go delete mode 100644 internal/metrics/view.go delete mode 100644 internal/metrics/view_test.go create mode 100644 internal/telemetry/metrics/const.go rename internal/{metrics/interceptors.go => telemetry/metrics/grpc.go} (68%) rename internal/{metrics/interceptors_test.go => telemetry/metrics/grpc_test.go} (97%) rename internal/{ => telemetry}/metrics/helpers_test.go (53%) create mode 100644 internal/telemetry/metrics/http.go rename internal/{metrics/middleware_test.go => telemetry/metrics/http_test.go} (91%) rename internal/{ => telemetry}/metrics/info.go (75%) rename internal/{ => telemetry}/metrics/info_test.go (95%) create mode 100644 internal/telemetry/metrics/providers.go rename internal/{metrics/exporter_test.go => telemetry/metrics/providers_test.go} (81%) create mode 100644 internal/telemetry/trace/trace.go create mode 100644 internal/telemetry/trace/trace_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index b6b051c42..426977b87 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,27 +4,33 @@ ### New +#### Telemetry [GH-35] + +- **Tracing** [GH-230] aka distributed tracing, provides insight into the full lifecycles, aka traces, of requests to the system, allowing you to pinpoint failures and performance issues. + + - Add [Jaeger](https://opencensus.io/exporters/supported-exporters/go/jaeger/) support. [GH-230] + +- **Metrics** provide quantitative information about processes running inside the system, including counters, gauges, and histograms. + + - Add informational metrics. [GH-227] + - GRPC Metrics Implementation. [GH-218] + + - Additional GRPC server metrics and request sizes + - Improved GRPC metrics implementation internals + - The GRPC method label is now 'grpc_method' and GRPC status is now `grpc_client_status` and `grpc_server_status` + + - HTTP Metrics Implementation. [GH-220] + + - Support HTTP request sizes on client and server side of proxy + - Improved HTTP metrics implementation internals + - The HTTP method label is now `http_method`, and HTTP status label is now `http_status` + ### Changed -- GRPC Metrics Implementation [GH-218] - - - Additional GRPC server metrics and request sizes - - Improved GRPC metrics implementation internals - - The GRPC method label is now 'grpc_method' and GRPC status is now `grpc_client_status` and `grpc_server_status` - - GRPC version upgraded to v1.22 [GH-219] - -- HTTP Metrics Implementation [GH-220] - - - Support HTTP request sizes on client and server side of proxy - - Improved HTTP metrics implementation internals - - The HTTP method label is now `http_method`, and HTTP status label is now `http_status` - - Add support for large cookie sessions by chunking. [GH-211] - - Prefer [curve](https://wiki.mozilla.org/Security/Server_Side_TLS) X25519 to P256 for TLS connections. [GH-233] - -- Add informational metrics. [GH-227] +- Pomerium and its services will gracefully shutdown on [interrupt signal](http://man7.org/linux/man-pages/man7/signal.7.html). [GH-230] - [Google](https://developers.google.com/identity/protocols/OpenIDConnect) now prompts the user to select a user account (by adding `select_account` to the sign in url). This allows a user who has multiple accounts at the authorization server to select amongst the multiple accounts that they may have current sessions for. ## v0.1.0 diff --git a/authenticate/grpc.go b/authenticate/grpc.go index 232dabad0..c0e2e2801 100644 --- a/authenticate/grpc.go +++ b/authenticate/grpc.go @@ -6,11 +6,14 @@ import ( "fmt" "github.com/pomerium/pomerium/internal/sessions" + "github.com/pomerium/pomerium/internal/telemetry/trace" pb "github.com/pomerium/pomerium/proto/authenticate" ) // Authenticate takes an encrypted code, and returns the authentication result. func (p *Authenticate) Authenticate(ctx context.Context, in *pb.AuthenticateRequest) (*pb.Session, error) { + _, span := trace.StartSpan(ctx, "authenticate.grpc.Validate") + defer span.End() session, err := sessions.UnmarshalSession(in.Code, p.cipher) if err != nil { return nil, fmt.Errorf("authenticate/grpc: authenticate %v", err) @@ -25,6 +28,9 @@ func (p *Authenticate) Authenticate(ctx context.Context, in *pb.AuthenticateRequ // Validate locally validates a JWT id_token; does NOT do nonce or revokation validation. // https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation func (p *Authenticate) Validate(ctx context.Context, in *pb.ValidateRequest) (*pb.ValidateReply, error) { + ctx, span := trace.StartSpan(ctx, "authenticate.grpc.Validate") + defer span.End() + isValid, err := p.provider.Validate(ctx, in.IdToken) if err != nil { return &pb.ValidateReply{IsValid: false}, fmt.Errorf("authenticate/grpc: validate %v", err) @@ -35,10 +41,8 @@ func (p *Authenticate) Validate(ctx context.Context, in *pb.ValidateRequest) (*p // Refresh renews a user's session checks if the session has been revoked using an access token // without reprompting the user. func (p *Authenticate) Refresh(ctx context.Context, in *pb.Session) (*pb.Session, error) { - // todo(bdd): add request id from incoming context - // md, _ := metadata.FromIncomingContext(ctx) - // sublogger := log.With().Str("req_id", md.Get("req_id")[0]).WithContext(ctx) - // sublogger.Info().Msg("tracing sucks!") + ctx, span := trace.StartSpan(ctx, "authenticate.grpc.Refresh") + defer span.End() if in == nil { return nil, fmt.Errorf("authenticate/grpc: session cannot be nil") } diff --git a/authorize/authorize.go b/authorize/authorize.go index bc5fdb6f2..e259dc4dc 100644 --- a/authorize/authorize.go +++ b/authorize/authorize.go @@ -6,7 +6,7 @@ import ( "github.com/pomerium/pomerium/internal/config" "github.com/pomerium/pomerium/internal/log" - "github.com/pomerium/pomerium/internal/metrics" + "github.com/pomerium/pomerium/internal/telemetry/metrics" ) // ValidateOptions checks to see if configuration values are valid for the diff --git a/authorize/gprc.go b/authorize/gprc.go index f66bc9df6..e878bf7cf 100644 --- a/authorize/gprc.go +++ b/authorize/gprc.go @@ -4,12 +4,16 @@ package authorize // import "github.com/pomerium/pomerium/authorize" import ( "context" + "github.com/pomerium/pomerium/internal/telemetry/trace" pb "github.com/pomerium/pomerium/proto/authorize" ) // Authorize validates the user identity, device, and context of a request for // a given route. Currently only checks identity. func (a *Authorize) Authorize(ctx context.Context, in *pb.Identity) (*pb.AuthorizeReply, error) { + _, span := trace.StartSpan(ctx, "authorize.grpc.Authorize") + defer span.End() + ok := a.ValidIdentity(in.Route, &Identity{ User: in.User, @@ -23,6 +27,8 @@ func (a *Authorize) Authorize(ctx context.Context, in *pb.Identity) (*pb.Authori // IsAdmin validates the user is an administrative user. func (a *Authorize) IsAdmin(ctx context.Context, in *pb.Identity) (*pb.IsAdminReply, error) { + _, span := trace.StartSpan(ctx, "authorize.grpc.IsAdmin") + defer span.End() ok := a.identityAccess.IsAdmin( &Identity{ Email: in.Email, diff --git a/cmd/pomerium/main.go b/cmd/pomerium/main.go index 4891304fe..10822b975 100644 --- a/cmd/pomerium/main.go +++ b/cmd/pomerium/main.go @@ -1,12 +1,10 @@ package main // import "github.com/pomerium/pomerium/cmd/pomerium" import ( - "errors" "flag" "fmt" "net/http" "os" - "strconv" "time" "github.com/fsnotify/fsnotify" @@ -18,8 +16,9 @@ import ( "github.com/pomerium/pomerium/internal/config" "github.com/pomerium/pomerium/internal/httputil" "github.com/pomerium/pomerium/internal/log" - "github.com/pomerium/pomerium/internal/metrics" "github.com/pomerium/pomerium/internal/middleware" + "github.com/pomerium/pomerium/internal/telemetry/metrics" + "github.com/pomerium/pomerium/internal/telemetry/trace" "github.com/pomerium/pomerium/internal/urlutil" "github.com/pomerium/pomerium/internal/version" pbAuthenticate "github.com/pomerium/pomerium/proto/authenticate" @@ -36,17 +35,18 @@ func main() { fmt.Println(version.FullVersion()) os.Exit(0) } - opt, err := parseOptions(*configFile) + opt, err := config.ParseOptions(*configFile) if err != nil { log.Fatal().Err(err).Msg("cmd/pomerium: options") } log.Info().Str("version", version.FullVersion()).Msg("cmd/pomerium") - grpcAuth := middleware.NewSharedSecretCred(opt.SharedKey) - grpcOpts := []grpc.ServerOption{grpc.UnaryInterceptor(grpcAuth.ValidateRequest), grpc.StatsHandler(metrics.NewGRPCServerStatsHandler(opt.Services))} - grpcServer := grpc.NewServer(grpcOpts...) + + setupMetrics(opt) + setupTracing(opt) + setupHTTPRedirectServer(opt) mux := http.NewServeMux() - + grpcServer := setupGRPCServer(opt) _, err = newAuthenticateService(*opt, mux, grpcServer) if err != nil { log.Fatal().Err(err).Msg("cmd/pomerium: authenticate") @@ -61,65 +61,23 @@ func main() { if err != nil { log.Fatal().Err(err).Msg("cmd/pomerium: proxy") } + defer proxy.AuthenticateClient.Close() + defer proxy.AuthorizeClient.Close() + go viper.WatchConfig() viper.OnConfigChange(func(e fsnotify.Event) { - log.Info(). - Str("file", e.Name). - Msg("cmd/pomerium: configuration file changed") - - opt = handleConfigUpdate(opt, []config.OptionsUpdater{authz, proxy}) + log.Info().Str("file", e.Name).Msg("cmd/pomerium: config file changed") + opt = config.HandleConfigUpdate(*configFile, opt, []config.OptionsUpdater{authz, proxy}) }) - // defer statements ignored anyway : https://stackoverflow.com/a/17888654 - // defer proxyService.AuthenticateClient.Close() - // defer proxyService.AuthorizeClient.Close() - httpOpts := &httputil.Options{ - Addr: opt.Addr, - Cert: opt.Cert, - Key: opt.Key, - CertFile: opt.CertFile, - KeyFile: opt.KeyFile, - ReadTimeout: opt.ReadTimeout, - WriteTimeout: opt.WriteTimeout, - ReadHeaderTimeout: opt.ReadHeaderTimeout, - IdleTimeout: opt.IdleTimeout, + srv, err := httputil.NewTLSServer(configToServerOptions(opt), mainHandler(opt, mux), grpcServer) + if err != nil { + log.Fatal().Err(err).Msg("cmd/pomerium: couldn't start pomerium") } + httputil.Shutdown(srv) - if opt.MetricsAddr != "" { - go newPromListener(opt.MetricsAddr) - metrics.SetBuildInfo(opt.Services) - } - - if srv, err := startRedirectServer(opt.HTTPRedirectAddr); err != nil { - log.Debug().Str("cause", err.Error()).Msg("cmd/pomerium: http redirect server not started") - } else { - defer srv.Close() - } - - if err := httputil.ListenAndServeTLS(httpOpts, wrapMiddleware(opt, mux), grpcServer); err != nil { - log.Fatal().Err(err).Msg("cmd/pomerium: https server") - } -} - -// startRedirectServer starts a http server that redirect HTTP to HTTPS traffic -func startRedirectServer(addr string) (*http.Server, error) { - if addr == "" { - return nil, errors.New("no http redirect addr provided") - } - srv := &http.Server{ - Addr: addr, - ReadTimeout: 5 * time.Second, - WriteTimeout: 5 * time.Second, - Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Connection", "close") - url := fmt.Sprintf("https://%s%s", urlutil.StripPort(r.Host), r.URL.String()) - http.Redirect(w, r, url, http.StatusMovedPermanently) - }), - } - log.Info().Str("Addr", addr).Msg("cmd/pomerium: http redirect server started") - go func() { log.Error().Err(srv.ListenAndServe()).Msg("cmd/pomerium: http server closed") }() - return srv, nil + os.Exit(0) } func newAuthenticateService(opt config.Options, mux *http.ServeMux, rpc *grpc.Server) (*authenticate.Authenticate, error) { @@ -159,21 +117,9 @@ func newProxyService(opt config.Options, mux *http.ServeMux) (*proxy.Proxy, erro return service, nil } -func newPromListener(addr string) { - metrics.RegisterView(metrics.HTTPClientViews) - metrics.RegisterView(metrics.HTTPServerViews) - metrics.RegisterView(metrics.GRPCClientViews) - metrics.RegisterView(metrics.GRPCServerViews) - metrics.RegisterInfoMetrics() - metrics.RegisterView(metrics.InfoViews) - - log.Info().Str("MetricsAddr", addr).Msg("cmd/pomerium: starting prometheus endpoint") - log.Error().Err(metrics.NewPromHTTPListener(addr)).Str("MetricsAddr", addr).Msg("cmd/pomerium: could not start metrics exporter") -} - -func wrapMiddleware(o *config.Options, mux http.Handler) http.Handler { +func mainHandler(o *config.Options, mux http.Handler) http.Handler { c := middleware.NewChain() - c = c.Append(metrics.HTTPMetricsHandler("proxy")) + c = c.Append(metrics.HTTPMetricsHandler(o.Services)) c = c.Append(log.NewHandler(log.Logger)) c = c.Append(log.AccessHandler(func(r *http.Request, status, size int, duration time.Duration) { log.FromRequest(r).Debug(). @@ -199,60 +145,61 @@ func wrapMiddleware(o *config.Options, mux http.Handler) http.Handler { return c.Then(mux) } -func parseOptions(configFile string) (*config.Options, error) { - o, err := config.OptionsFromViper(configFile) - if err != nil { - return nil, err +func configToServerOptions(opt *config.Options) *httputil.ServerOptions { + return &httputil.ServerOptions{ + Addr: opt.Addr, + Cert: opt.Cert, + Key: opt.Key, + CertFile: opt.CertFile, + KeyFile: opt.KeyFile, + ReadTimeout: opt.ReadTimeout, + WriteTimeout: opt.WriteTimeout, + ReadHeaderTimeout: opt.ReadHeaderTimeout, + IdleTimeout: opt.IdleTimeout, } - if o.Debug { - log.SetDebugMode() - } - if o.LogLevel != "" { - log.SetLevel(o.LogLevel) - } - metrics.AddPolicyCountCallback(o.Services, func() int64 { - return int64(len(o.Policies)) - }) - - checksumDec, err := strconv.ParseUint(o.Checksum(), 16, 64) - if err != nil { - log.Warn().Err(err).Msg("Could not parse config checksum into decimal") - } - metrics.SetConfigChecksum(o.Services, checksumDec) - - return o, nil } -func handleConfigUpdate(opt *config.Options, services []config.OptionsUpdater) *config.Options { - newOpt, err := parseOptions(*configFile) - if err != nil { - log.Error().Err(err).Msg("cmd/pomerium: could not reload configuration") - metrics.SetConfigInfo(opt.Services, false, "") - return opt - } - optChecksum := opt.Checksum() - newOptChecksum := newOpt.Checksum() - - log.Debug(). - Str("old-checksum", optChecksum). - Str("new-checksum", newOptChecksum). - Msg("cmd/pomerium: configuration file changed") - - if newOptChecksum == optChecksum { - log.Debug().Msg("cmd/pomerium: loaded configuration has not changed") - return opt - } - - log.Info().Str("checksum", newOptChecksum).Msg("cmd/pomerium: checksum changed") - for _, service := range services { - if err := service.UpdateOptions(*newOpt); err != nil { - log.Error().Err(err).Msg("cmd/pomerium: could not update options") - metrics.SetConfigInfo(opt.Services, false, "") +func setupMetrics(opt *config.Options) { + if opt.MetricsAddr != "" { + if handler, err := metrics.PrometheusHandler(); err != nil { + log.Error().Err(err).Msg("cmd/pomerium: couldn't start metrics server") + } else { + serverOpts := &httputil.ServerOptions{Addr: opt.MetricsAddr} + srv := httputil.NewHTTPServer(serverOpts, handler) + go httputil.Shutdown(srv) } } - metrics.AddPolicyCountCallback(newOpt.Services, func() int64 { - return int64(len(newOpt.Policies)) - }) - metrics.SetConfigInfo(newOpt.Services, true, newOptChecksum) - return newOpt +} + +func setupTracing(opt *config.Options) { + if opt.TracingProvider != "" { + tracingOpts := &trace.TracingOptions{ + Provider: opt.TracingProvider, + Service: opt.Services, + Debug: opt.TracingDebug, + JaegerAgentEndpoint: opt.TracingJaegerAgentEndpoint, + JaegerCollectorEndpoint: opt.TracingJaegerCollectorEndpoint, + } + if err := trace.RegisterTracing(tracingOpts); err != nil { + log.Error().Err(err).Msg("cmd/pomerium: couldn't register tracing") + } else { + log.Info().Interface("options", tracingOpts).Msg("cmd/pomerium: metrics configured") + } + } +} + +func setupHTTPRedirectServer(opt *config.Options) { + if opt.HTTPRedirectAddr != "" { + serverOpts := httputil.ServerOptions{Addr: opt.HTTPRedirectAddr} + srv := httputil.NewHTTPServer(&serverOpts, httputil.RedirectHandler()) + go httputil.Shutdown(srv) + } +} + +func setupGRPCServer(opt *config.Options) *grpc.Server { + grpcAuth := middleware.NewSharedSecretCred(opt.SharedKey) + grpcOpts := []grpc.ServerOption{ + grpc.UnaryInterceptor(grpcAuth.ValidateRequest), + grpc.StatsHandler(metrics.NewGRPCServerStatsHandler(opt.Services))} + return grpc.NewServer(grpcOpts...) } diff --git a/cmd/pomerium/main_test.go b/cmd/pomerium/main_test.go index 2ec946c85..68f2a653b 100644 --- a/cmd/pomerium/main_test.go +++ b/cmd/pomerium/main_test.go @@ -7,48 +7,19 @@ import ( "net/http/httptest" "net/url" "os" + "os/signal" "reflect" - "strings" + "syscall" "testing" + "time" + "github.com/google/go-cmp/cmp" "github.com/pomerium/pomerium/internal/config" + "github.com/pomerium/pomerium/internal/httputil" "github.com/pomerium/pomerium/internal/middleware" "google.golang.org/grpc" ) -func Test_startRedirectServer(t *testing.T) { - - tests := []struct { - name string - addr string - want string - wantErr bool - }{ - {"empty", "", "", true}, - {":http", ":http", ":http", false}, - {"localhost:80", "localhost:80", "localhost:80", false}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := startRedirectServer(tt.addr) - if (err != nil) != tt.wantErr { - t.Errorf("startRedirectServer() error = %v, wantErr %v", err, tt.wantErr) - return - } - if got != nil { - defer got.Close() - ts := httptest.NewServer(got.Handler) - defer ts.Close() - _, err := http.Get(ts.URL) - if !strings.Contains(err.Error(), "https") { - t.Errorf("startRedirectServer() = %v, want %v", err, tt.want) - return - } - } - }) - } -} - func Test_newAuthenticateService(t *testing.T) { grpcAuth := middleware.NewSharedSecretCred("test") grpcOpts := []grpc.ServerOption{grpc.UnaryInterceptor(grpcAuth.ValidateRequest)} @@ -193,7 +164,7 @@ func Test_newProxyeService(t *testing.T) { } } -func Test_wrapMiddleware(t *testing.T) { +func Test_mainHandler(t *testing.T) { o := config.Options{ Services: "all", Headers: map[string]string{ @@ -214,7 +185,7 @@ func Test_wrapMiddleware(t *testing.T) { }) mux.Handle("/404", h) - out := wrapMiddleware(&o, mux) + out := mainHandler(&o, mux) out.ServeHTTP(rr, req) expected := fmt.Sprintf("OK") body := rr.Body.String() @@ -223,87 +194,106 @@ func Test_wrapMiddleware(t *testing.T) { t.Errorf("handler returned unexpected body: got %v want %v", body, expected) } } -func Test_parseOptions(t *testing.T) { + +func Test_configToServerOptions(t *testing.T) { tests := []struct { - name string - envKey string - envValue string - servicesEnvKey string - servicesEnvValue string - wantSharedKey string - wantErr bool + name string + opt *config.Options + want *httputil.ServerOptions }{ - {"no shared secret", "", "", "SERVICES", "authenticate", "skip", true}, - {"no shared secret in all mode", "", "", "", "", "", false}, - {"good", "SHARED_SECRET", "YixWi1MYh77NMECGGIJQevoonYtVF+ZPRkQZrrmeRqM=", "", "", "YixWi1MYh77NMECGGIJQevoonYtVF+ZPRkQZrrmeRqM=", false}, + {"simple convert", &config.Options{Addr: ":http"}, &httputil.ServerOptions{Addr: ":http"}}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - os.Setenv(tt.servicesEnvKey, tt.servicesEnvValue) - os.Setenv(tt.envKey, tt.envValue) - defer os.Unsetenv(tt.envKey) - defer os.Unsetenv(tt.servicesEnvKey) - - got, err := parseOptions("") - if (err != nil) != tt.wantErr { - t.Errorf("parseOptions() error = %v, wantErr %v", err, tt.wantErr) - return - } - if got != nil && got.Services != "all" && got.SharedKey != tt.wantSharedKey { - t.Errorf("parseOptions()\n") - t.Errorf("got: %+v\n", got.SharedKey) - t.Errorf("want: %+v\n", tt.wantSharedKey) - + if diff := cmp.Diff(configToServerOptions(tt.opt), tt.want); diff != "" { + t.Errorf("configToServerOptions() = \n %s", diff) } }) } } -type mockService struct { - fail bool - Updated bool -} - -func (m *mockService) UpdateOptions(o config.Options) error { - - m.Updated = true - if m.fail { - return fmt.Errorf("failed") - } - return nil -} - -func Test_handleConfigUpdate(t *testing.T) { - os.Clearenv() - os.Setenv("SHARED_SECRET", "foo") - defer os.Unsetenv("SHARED_SECRET") - - blankOpts, err := config.NewOptions("https://authenticate.example", "https://authorize.example") - if err != nil { - t.Fatal(err) - } - - goodOpts, err := config.OptionsFromViper("") - if err != nil { - t.Fatal(err) - } +func Test_setupGRPCServer(t *testing.T) { tests := []struct { - name string - service *mockService - oldOpts config.Options - wantUpdate bool + name string + opt *config.Options + dontWant *grpc.Server }{ - {"good", &mockService{fail: false}, *blankOpts, true}, - {"bad", &mockService{fail: true}, *blankOpts, true}, - {"no change", &mockService{fail: false}, *goodOpts, false}, + {"good", &config.Options{SharedKey: "test"}, nil}, } - for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - handleConfigUpdate(&tt.oldOpts, []config.OptionsUpdater{tt.service}) - if tt.service.Updated != tt.wantUpdate { - t.Errorf("Failed to update config on service") + if diff := cmp.Diff(setupGRPCServer(tt.opt), tt.dontWant); diff == "" { + t.Errorf("setupGRPCServer() = \n %s", diff) } }) } } + +func Test_setupTracing(t *testing.T) { + tests := []struct { + name string + opt *config.Options + }{ + {"good jaeger", &config.Options{TracingProvider: "jaeger", TracingJaegerAgentEndpoint: "localhost:0", TracingJaegerCollectorEndpoint: "localhost:0"}}, + {"dont register aything", &config.Options{}}, + {"bad provider", &config.Options{TracingProvider: "bad provider"}}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + setupTracing(tt.opt) + }) + } +} + +func Test_setupMetrics(t *testing.T) { + tests := []struct { + name string + opt *config.Options + }{ + {"dont register aything", &config.Options{}}, + {"good metrics server", &config.Options{MetricsAddr: "localhost:0"}}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := make(chan os.Signal, 1) + signal.Notify(c, syscall.SIGINT) + defer signal.Stop(c) + setupMetrics(tt.opt) + syscall.Kill(syscall.Getpid(), syscall.SIGINT) + waitSig(t, c, syscall.SIGINT) + + }) + } +} + +func Test_setupHTTPRedirectServer(t *testing.T) { + tests := []struct { + name string + opt *config.Options + }{ + {"dont register aything", &config.Options{}}, + {"good redirect server", &config.Options{HTTPRedirectAddr: "localhost:0"}}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := make(chan os.Signal, 1) + signal.Notify(c, syscall.SIGINT) + defer signal.Stop(c) + setupHTTPRedirectServer(tt.opt) + syscall.Kill(syscall.Getpid(), syscall.SIGINT) + waitSig(t, c, syscall.SIGINT) + + }) + } +} + +func waitSig(t *testing.T, c <-chan os.Signal, sig os.Signal) { + select { + case s := <-c: + if s != sig { + t.Fatalf("signal was %v, want %v", s, sig) + } + case <-time.After(1 * time.Second): + t.Fatalf("timeout waiting for %v", sig) + } +} diff --git a/docs/reference/readme.md b/docs/reference/readme.md index e7a04b049..f9fa8b75b 100644 --- a/docs/reference/readme.md +++ b/docs/reference/readme.md @@ -154,13 +154,17 @@ If set, the HTTP Redirect Address specifies the host and port to redirect http t - Environmental Variable: `METRICS_ADDRESS` - Config File Key: `metrics_address` - Type: `string` -- Example: `:8080`, `127.0.0.1:9090`, `` +- Example: `:9090`, `127.0.0.1:9090` - Default: `disabled` - Optional Expose a prometheus format HTTP endpoint on the specified port. Disabled by default. -**Use with caution:** the endpoint can expose frontend and backend server names or addresses. Do not expose the metrics port if this is sensitive information. +:::warning + +**Use with caution:** the endpoint can expose frontend and backend server names or addresses. Do not externally expose the metrics if this is sensitive information. + +::: #### Metrics tracked @@ -187,6 +191,38 @@ pomerium_config_last_reload_success | Gauge | Whether the last con pomerium_config_last_reload_success_timestamp | Gauge | The timestamp of the last successful configuration reload by service pomerium_build_info | Gauge | Pomerium build metadata by git revision, service, version and goversion +### Tracing + +Tracing tracks the progression of a single user request as it is handled by Pomerium. + +Each unit work is called a Span in a trace. Spans include metadata about the work, including the time spent in the step (latency), status, time events, attributes, links. You can use tracing to debug errors and latency issues in your applications, including in downstream connections. + +#### Shared Tracing Settings + +Config Key | Description | Required +:--------------- | :---------------------------------------------------------------- | -------- +tracing_provider | The name of the tracing provider. (e.g. jaeger) | ✅ +tracing_debug | Will disable [sampling](https://opencensus.io/tracing/sampling/). | ❌ + +#### Jaeger + +[Jaeger](https://www.jaegertracing.io/) is a distributed tracing system released as open source by Uber Technologies. It is used for monitoring and troubleshooting microservices-based distributed systems, including: + +- Distributed context propagation +- Distributed transaction monitoring +- Root cause analysis +- Service dependency analysis +- Performance / latency optimization + +Config Key | Description | Required +:-------------------------------- | :------------------------------------------ | -------- +tracing_jaeger_collector_endpoint | Url to the Jaeger HTTP Thrift collector. | ✅ +tracing_jaeger_agent_endpoint | Send spans to jaeger-agent at this address. | ✅ + +##### Example + +![jaeger example trace](./tracing/jaeger.png) pomerium_config_last_reload_success_timestamp | Gauge | The timestamp of the last successful configuration reload by service pomerium_build_info | Gauge | Pomerium build metadata by git revision, service, version and goversion + ### Policy - Environmental Variable: `POLICY` diff --git a/docs/reference/tracing/jaeger.png b/docs/reference/tracing/jaeger.png new file mode 100644 index 0000000000000000000000000000000000000000..a60289c334367f57ec83bf046cfbeacfead30c54 GIT binary patch literal 250605 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⪼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 literal 0 HcmV?d00001 diff --git a/go.mod b/go.mod index efc2de1fc..0dcdc8412 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.12 require ( cloud.google.com/go v0.40.0 // indirect + contrib.go.opencensus.io/exporter/jaeger v0.1.0 contrib.go.opencensus.io/exporter/prometheus v0.1.0 github.com/fsnotify/fsnotify v1.4.7 github.com/golang/mock v1.3.1 diff --git a/go.sum b/go.sum index 0c0d3c340..e06dfd01c 100644 --- a/go.sum +++ b/go.sum @@ -4,13 +4,19 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.40.0 h1:FjSY7bOj+WzJe6TZRVtXI2b9kAYvtNg4lMbcH2+MUkk= cloud.google.com/go v0.40.0/go.mod h1:Tk58MuI9rbLMKlAjeO/bDnteAx7tX2gJIXw4T5Jwlro= +contrib.go.opencensus.io/exporter/jaeger v0.1.0 h1:WNc9HbA38xEQmsI40Tjd/MNU/g8byN2Of7lwIjv0Jdc= +contrib.go.opencensus.io/exporter/jaeger v0.1.0/go.mod h1:VYianECmuFPwU37O699Vc1GOcy+y8kOsfaxHRImmjbA= contrib.go.opencensus.io/exporter/prometheus v0.1.0 h1:SByaIoWwNgMdPSgl5sMqM2KDE5H/ukPWBRo314xiDvg= contrib.go.opencensus.io/exporter/prometheus v0.1.0/go.mod h1:cGFniUXGZlKRjzOyuZJ6mgB+PgBcCIa79kEKR8YCW+A= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= +github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/apache/thrift v0.12.0 h1:pODnxUFNcjP9UTLZGTdeh+j16A8lJbRvD3rOtrk/7bs= +github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0= @@ -28,6 +34,9 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= +github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= +github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -36,6 +45,7 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= @@ -49,6 +59,7 @@ github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFU github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -57,6 +68,8 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 h1:Iju5GlWwrvL6UBg4zJJt3btmonfrMlCDdsejg4CZE7c= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= @@ -67,6 +80,7 @@ github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= @@ -91,10 +105,15 @@ github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQz github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.4.0 h1:u3Z1r+oOXJIkxqw34zVhyPgjBsm6X2wn21NWs/HfSeg= github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo= +github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -105,20 +124,25 @@ github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 h1:J9b7z+QKAm github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= +github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= github.com/prometheus/client_golang v0.9.3 h1:9iH4JKXLzFbOAdtqv/a+j8aewx2Y8lAjAydhbaScPF8= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.0 h1:7etb9YClo3a6HjLzfl6rIQaU+FDfi0VSX39io3aQ+DM= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084 h1:sofwID9zm4tzrgykg80hfFph1mryUeLRsUfoocVVmRY= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/zerolog v1.14.3 h1:4EGfSkR2hJDB0s3oFfrlPqjU1e4WLncergLil3nEKW0= @@ -151,6 +175,7 @@ github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= @@ -171,10 +196,12 @@ golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -195,11 +222,14 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -216,6 +246,7 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -226,6 +257,8 @@ golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= +google.golang.org/api v0.3.2/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.6.0 h1:2tJEkRfnZL5g1GeBUlITh/rqT5HG3sFcoVCUUxmgJ2g= google.golang.org/api v0.6.0/go.mod h1:btoxGiFvQNVUZQ8W08zLtrVS08CNpINPEfxXxgJL1Q4= @@ -243,6 +276,7 @@ google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRn google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= google.golang.org/genproto v0.0.0-20190611190212-a7e196e89fd3 h1:0LGHEA/u5XLibPOx6D7D8FBT/ax6wT57vNKY0QckCwo= google.golang.org/genproto v0.0.0-20190611190212-a7e196e89fd3/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= +google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -253,13 +287,16 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.3.1 h1:SK5KegNXmKmqE342YYN2qPHEnUYeoMiXXl1poUlI+o4= gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099 h1:XJP7lxbSxWLOMNdBE4B/STaqVy6L73o0knwj2vIlxnw= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/internal/config/helpers.go b/internal/config/helpers.go index d916c55ce..2a23ae2f2 100644 --- a/internal/config/helpers.go +++ b/internal/config/helpers.go @@ -1,16 +1,5 @@ package config // import "github.com/pomerium/pomerium/internal/config" -import "os" - -// findPwd returns best guess at current working directory -func findPwd() string { - p, err := os.Getwd() - if err != nil { - return "." - } - return p -} - // IsValidService checks to see if a service is a valid service mode func IsValidService(s string) bool { switch s { diff --git a/internal/config/options.go b/internal/config/options.go index 2ac8239ce..4162ae154 100644 --- a/internal/config/options.go +++ b/internal/config/options.go @@ -7,11 +7,14 @@ import ( "net/url" "path/filepath" "reflect" + "strconv" "strings" "time" "github.com/pomerium/pomerium/internal/cryptutil" + "github.com/pomerium/pomerium/internal/fileutil" "github.com/pomerium/pomerium/internal/log" + "github.com/pomerium/pomerium/internal/telemetry/metrics" "github.com/pomerium/pomerium/internal/urlutil" "github.com/mitchellh/hashstructure" @@ -129,6 +132,19 @@ type Options struct { // Address/Port to bind to for prometheus metrics MetricsAddr string `mapstructure:"metrics_address"` + + // Tracing shared settings + TracingProvider string `mapstructure:"tracing_provider"` + TracingDebug bool `mapstructure:"tracing_debug"` + + // Jaeger + + // CollectorEndpoint is the full url to the Jaeger HTTP Thrift collector. + // For example, http://localhost:14268/api/traces + TracingJaegerCollectorEndpoint string `mapstructure:"tracing_jaeger_collector_endpoint"` + // AgentEndpoint instructs exporter to send spans to jaeger-agent at this address. + // For example, localhost:6831. + TracingJaegerAgentEndpoint string `mapstructure:"tracing_jaeger_agent_endpoint"` } var defaultOptions = Options{ @@ -148,8 +164,8 @@ var defaultOptions = Options{ "Strict-Transport-Security": "max-age=31536000; includeSubDomains; preload", }, Addr: ":https", - CertFile: filepath.Join(findPwd(), "cert.pem"), - KeyFile: filepath.Join(findPwd(), "privkey.pem"), + CertFile: filepath.Join(fileutil.Getwd(), "cert.pem"), + KeyFile: filepath.Join(fileutil.Getwd(), "privkey.pem"), ReadHeaderTimeout: 10 * time.Second, ReadTimeout: 30 * time.Second, WriteTimeout: 0, // support streaming by default @@ -339,3 +355,56 @@ func (o *Options) Checksum() string { } return fmt.Sprintf("%x", hash) } + +func ParseOptions(configFile string) (*Options, error) { + o, err := OptionsFromViper(configFile) + if err != nil { + return nil, err + } + if o.Debug { + log.SetDebugMode() + } + if o.LogLevel != "" { + log.SetLevel(o.LogLevel) + } + metrics.AddPolicyCountCallback(o.Services, func() int64 { + return int64(len(o.Policies)) + }) + + checksumDec, err := strconv.ParseUint(o.Checksum(), 16, 64) + if err != nil { + log.Warn().Err(err).Msg("Could not parse config checksum into decimal") + } + metrics.SetConfigChecksum(o.Services, checksumDec) + + return o, nil +} + +func HandleConfigUpdate(configFile string, opt *Options, services []OptionsUpdater) *Options { + newOpt, err := ParseOptions(configFile) + if err != nil { + log.Error().Err(err).Msg("cmd/pomerium: could not reload configuration") + return opt + } + optChecksum := opt.Checksum() + newOptChecksum := newOpt.Checksum() + + log.Debug(). + Str("old-checksum", optChecksum). + Str("new-checksum", newOptChecksum). + Msg("cmd/pomerium: configuration file changed") + + if newOptChecksum == optChecksum { + log.Debug().Msg("cmd/pomerium: loaded configuration has not changed") + return opt + } + + log.Info().Str("checksum", newOptChecksum).Msg("cmd/pomerium: checksum changed") + for _, service := range services { + if err := service.UpdateOptions(*newOpt); err != nil { + log.Error().Err(err).Msg("cmd/pomerium: could not update options") + } + } + + return newOpt +} diff --git a/internal/config/options_test.go b/internal/config/options_test.go index fa982daed..69f3e132e 100644 --- a/internal/config/options_test.go +++ b/internal/config/options_test.go @@ -408,3 +408,99 @@ func TestOptionsFromViper(t *testing.T) { }) } } + +func Test_parseOptions(t *testing.T) { + viper.Reset() + + tests := []struct { + name string + envKey string + envValue string + servicesEnvKey string + servicesEnvValue string + wantSharedKey string + wantErr bool + }{ + {"no shared secret", "", "", "SERVICES", "authenticate", "skip", true}, + {"no shared secret in all mode", "", "", "", "", "", false}, + {"good", "SHARED_SECRET", "YixWi1MYh77NMECGGIJQevoonYtVF+ZPRkQZrrmeRqM=", "", "", "YixWi1MYh77NMECGGIJQevoonYtVF+ZPRkQZrrmeRqM=", false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + os.Setenv(tt.servicesEnvKey, tt.servicesEnvValue) + os.Setenv(tt.envKey, tt.envValue) + defer os.Unsetenv(tt.envKey) + defer os.Unsetenv(tt.servicesEnvKey) + + got, err := ParseOptions("") + if (err != nil) != tt.wantErr { + t.Errorf("ParseOptions() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != nil && got.Services != "all" && got.SharedKey != tt.wantSharedKey { + t.Errorf("ParseOptions()\n") + t.Errorf("got: %+v\n", got.SharedKey) + t.Errorf("want: %+v\n", tt.wantSharedKey) + + } + }) + } +} + +type mockService struct { + fail bool + Updated bool +} + +func (m *mockService) UpdateOptions(o Options) error { + + m.Updated = true + if m.fail { + return fmt.Errorf("failed") + } + return nil +} + +func Test_HandleConfigUpdate(t *testing.T) { + os.Clearenv() + os.Setenv("SHARED_SECRET", "foo") + defer os.Unsetenv("SHARED_SECRET") + + blankOpts, err := NewOptions("https://authenticate.example", "https://authorize.example") + if err != nil { + t.Fatal(err) + } + + goodOpts, err := OptionsFromViper("") + if err != nil { + t.Fatal(err) + } + tests := []struct { + name string + envarKey string + envarValue string + service *mockService + oldOpts Options + wantUpdate bool + }{ + {"good", "", "", &mockService{fail: false}, *blankOpts, true}, + {"good set debug", "POMERIUM_DEBUG", "true", &mockService{fail: false}, *blankOpts, true}, + {"bad", "", "", &mockService{fail: true}, *blankOpts, true}, + {"no change", "", "", &mockService{fail: false}, *goodOpts, false}, + {"bad policy file unmarshal error", "POLICY", base64.StdEncoding.EncodeToString([]byte("{json:}")), &mockService{fail: false}, *blankOpts, false}, + {"bad header key", "SERVICES", "error", &mockService{fail: false}, *blankOpts, false}, + {"bad header header value", "HEADERS", "x;y;z", &mockService{fail: false}, *blankOpts, false}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + os.Setenv(tt.envarKey, tt.envarValue) + defer os.Unsetenv(tt.envarKey) + + HandleConfigUpdate("", &tt.oldOpts, []OptionsUpdater{tt.service}) + if tt.service.Updated != tt.wantUpdate { + t.Errorf("Failed to update config on service") + } + }) + } +} diff --git a/internal/fileutil/fileutil.go b/internal/fileutil/fileutil.go index 904dd38e0..a7b0bba7e 100644 --- a/internal/fileutil/fileutil.go +++ b/internal/fileutil/fileutil.go @@ -30,3 +30,17 @@ func IsReadableFile(path string) (bool, error) { fd.Close() return true, nil // Item exists and is readable. } + +// Getwd returns a rooted path name corresponding to the +// current directory. If the current directory can be +// reached via multiple paths (due to symbolic links), +// Getwd may return any one of them. +// +// On failure, will return "." +func Getwd() string { + p, err := os.Getwd() + if err != nil { + return "." + } + return p +} diff --git a/internal/fileutil/fileutil_test.go b/internal/fileutil/fileutil_test.go index 9a8e10380..4ec0f5c9a 100644 --- a/internal/fileutil/fileutil_test.go +++ b/internal/fileutil/fileutil_test.go @@ -1,6 +1,9 @@ -package fileutil // import "github.com/pomerium/pomerium/internal/fileutil" +package fileutil -import "testing" +import ( + "strings" + "testing" +) func TestIsReadableFile(t *testing.T) { @@ -27,3 +30,19 @@ func TestIsReadableFile(t *testing.T) { }) } } + +func TestGetwd(t *testing.T) { + tests := []struct { + name string + want string + }{ + {"most basic example", "internal/fileutil"}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := Getwd(); strings.Contains(tt.want, got) { + t.Errorf("Getwd() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/internal/httputil/errors.go b/internal/httputil/errors.go index 607ad7e21..527d6d9fe 100644 --- a/internal/httputil/errors.go +++ b/internal/httputil/errors.go @@ -23,21 +23,11 @@ func (h Error) Error() string { return fmt.Sprintf("%d %s: %s", h.Code, http.StatusText(h.Code), h.Message) } -// CodeForError maps an error type and returns a corresponding http.Status -func CodeForError(err error) int { - switch err { - case ErrTokenRevoked: - return http.StatusUnauthorized - } - return http.StatusInternalServerError -} - // ErrorResponse renders an error page for errors given a message and a status code. // If no message is passed, defaults to the text of the status code. func ErrorResponse(rw http.ResponseWriter, r *http.Request, e *Error) { - requestID := "" - id, ok := log.IDFromRequest(r) - if ok { + var requestID string + if id, ok := log.IDFromRequest(r); ok { requestID = id } if r.Header.Get("Accept") == "application/json" { diff --git a/internal/httputil/errors_test.go b/internal/httputil/errors_test.go new file mode 100644 index 000000000..7134109f9 --- /dev/null +++ b/internal/httputil/errors_test.go @@ -0,0 +1,49 @@ +package httputil + +import ( + "net/http" + "net/http/httptest" + "testing" +) + +func TestErrorResponse(t *testing.T) { + tests := []struct { + name string + rw http.ResponseWriter + r *http.Request + e *Error + }{ + {"good", httptest.NewRecorder(), &http.Request{Method: http.MethodGet}, &Error{Code: http.StatusBadRequest, Message: "missing id token"}}, + {"good json", httptest.NewRecorder(), &http.Request{Method: http.MethodGet, Header: http.Header{"Accept": []string{"application/json"}}}, &Error{Code: http.StatusBadRequest, Message: "missing id token"}}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ErrorResponse(tt.rw, tt.r, tt.e) + }) + } +} + +func TestError_Error(t *testing.T) { + + tests := []struct { + name string + Message string + Code int + CanDebug bool + want string + }{ + {"good", "short and stout", http.StatusTeapot, false, "418 I'm a teapot: short and stout"}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + h := Error{ + Message: tt.Message, + Code: tt.Code, + CanDebug: tt.CanDebug, + } + if got := h.Error(); got != tt.want { + t.Errorf("Error.Error() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/internal/httputil/http.go b/internal/httputil/http.go new file mode 100644 index 000000000..289cb792f --- /dev/null +++ b/internal/httputil/http.go @@ -0,0 +1,76 @@ +package httputil // import "github.com/pomerium/pomerium/internal/httputil" + +import ( + "context" + "fmt" + stdlog "log" + "net/http" + "os" + "os/signal" + "syscall" + "time" + + "github.com/pomerium/pomerium/internal/log" + "github.com/pomerium/pomerium/internal/urlutil" +) + +// NewHTTPServer starts a http server given a set of options and a handler. +// +// It is the caller's responsibility to Close() or Shutdown() the returned +// server. +func NewHTTPServer(opt *ServerOptions, h http.Handler) *http.Server { + if opt == nil { + opt = defaultHTTPServerOptions + } else { + opt.applyHTTPDefaults() + } + sublogger := log.With().Str("addr", opt.Addr).Logger() + srv := http.Server{ + Addr: opt.Addr, + ReadHeaderTimeout: opt.ReadHeaderTimeout, + ReadTimeout: opt.ReadTimeout, + WriteTimeout: opt.WriteTimeout, + IdleTimeout: opt.IdleTimeout, + Handler: h, + ErrorLog: stdlog.New(&log.StdLogWrapper{Logger: &sublogger}, "", 0), + } + + go func() { + if err := srv.ListenAndServe(); err != http.ErrServerClosed { + log.Error().Str("addr", opt.Addr).Err(err).Msg("internal/httputil: unexpected shutdown") + } + }() + return &srv +} + +func RedirectHandler() http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Connection", "close") + url := fmt.Sprintf("https://%s%s", urlutil.StripPort(r.Host), r.URL.String()) + http.Redirect(w, r, url, http.StatusMovedPermanently) + }) +} + +// Shutdown attempts to shut down the server when a os interrupt or sigterm +// signal are received without interrupting any +// active connections. Shutdown works by first closing all open +// listeners, then closing all idle connections, and then waiting +// indefinitely for connections to return to idle and then shut down. +// If the provided context expires before the shutdown is complete, +// Shutdown returns the context's error, otherwise it returns any +// error returned from closing the Server's underlying Listener(s). +// +// When Shutdown is called, Serve, ListenAndServe, and +// ListenAndServeTLS immediately return ErrServerClosed. +func Shutdown(srv *http.Server) { + sigint := make(chan os.Signal, 1) + signal.Notify(sigint, os.Interrupt) + signal.Notify(sigint, syscall.SIGTERM) + rec := <-sigint + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + log.Info().Str("signal", rec.String()).Msg("internal/httputil: shutting down servers") + if err := srv.Shutdown(ctx); err != nil { + log.Error().Err(err).Msg("internal/httputil: shutdown failed") + } +} diff --git a/internal/httputil/http_test.go b/internal/httputil/http_test.go new file mode 100644 index 000000000..ec67af50b --- /dev/null +++ b/internal/httputil/http_test.go @@ -0,0 +1,49 @@ +package httputil + +import ( + "fmt" + "io/ioutil" + "log" + "net/http" + "net/http/httptest" + "testing" +) + +func TestNewHTTPServer(t *testing.T) { + tests := []struct { + name string + opts *ServerOptions + // wantErr bool + }{ + {"localhost:9232", &ServerOptions{Addr: "localhost:9232"}}, + {"localhost:65536", &ServerOptions{Addr: "localhost:-1"}}, // will fail, but won't err + {"empty", &ServerOptions{}}, + {"empty", nil}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + srv := NewHTTPServer(tt.opts, RedirectHandler()) + + defer srv.Close() + + // we cheat a little bit here and use the httptest server to test the client + ts := httptest.NewServer(srv.Handler) + defer ts.Close() + client := ts.Client() + client.CheckRedirect = func(req *http.Request, via []*http.Request) error { + return http.ErrUseLastResponse + } + res, err := client.Get(ts.URL) + if err != nil { + log.Fatal(err) + } + greeting, err := ioutil.ReadAll(res.Body) + res.Body.Close() + if err != nil { + log.Fatal(err) + } + fmt.Printf("%s", greeting) + + }) + } +} diff --git a/internal/httputil/options.go b/internal/httputil/options.go new file mode 100644 index 000000000..4af4df913 --- /dev/null +++ b/internal/httputil/options.go @@ -0,0 +1,87 @@ +package httputil // import "github.com/pomerium/pomerium/internal/httputil" + +import ( + "path/filepath" + "time" + + "github.com/pomerium/pomerium/internal/fileutil" +) + +// ServerOptions contains the configurations settings for a http server. +type ServerOptions struct { + // Addr specifies the host and port on which the server should serve + // HTTPS requests. If empty, ":https" is used. + Addr string + + // TLS certificates to use. + Cert string + Key string + CertFile string + KeyFile string + + // Timeouts + ReadHeaderTimeout time.Duration + ReadTimeout time.Duration + WriteTimeout time.Duration + IdleTimeout time.Duration +} + +var defaultTLSServerOptions = &ServerOptions{ + Addr: ":https", + CertFile: filepath.Join(fileutil.Getwd(), "cert.pem"), + KeyFile: filepath.Join(fileutil.Getwd(), "privkey.pem"), + ReadHeaderTimeout: 10 * time.Second, + ReadTimeout: 30 * time.Second, + WriteTimeout: 0, // support streaming by default + IdleTimeout: 5 * time.Minute, +} + +func (o *ServerOptions) applyTLSDefaults() { + if o.Addr == "" { + o.Addr = defaultTLSServerOptions.Addr + } + if o.Cert == "" && o.CertFile == "" { + o.CertFile = defaultTLSServerOptions.CertFile + } + if o.Key == "" && o.KeyFile == "" { + o.KeyFile = defaultTLSServerOptions.KeyFile + } + if o.ReadHeaderTimeout == 0 { + o.ReadHeaderTimeout = defaultTLSServerOptions.ReadHeaderTimeout + } + if o.ReadTimeout == 0 { + o.ReadTimeout = defaultTLSServerOptions.ReadTimeout + } + if o.WriteTimeout == 0 { + o.WriteTimeout = defaultTLSServerOptions.WriteTimeout + } + if o.IdleTimeout == 0 { + o.IdleTimeout = defaultTLSServerOptions.IdleTimeout + } +} + +var defaultHTTPServerOptions = &ServerOptions{ + Addr: ":http", + ReadHeaderTimeout: 10 * time.Second, + ReadTimeout: 5 * time.Second, + WriteTimeout: 5 * time.Second, + IdleTimeout: 5 * time.Minute, +} + +func (o *ServerOptions) applyHTTPDefaults() { + if o.Addr == "" { + o.Addr = defaultHTTPServerOptions.Addr + } + if o.ReadHeaderTimeout == 0 { + o.ReadHeaderTimeout = defaultHTTPServerOptions.ReadHeaderTimeout + } + if o.ReadTimeout == 0 { + o.ReadTimeout = defaultHTTPServerOptions.ReadTimeout + } + if o.WriteTimeout == 0 { + o.WriteTimeout = defaultHTTPServerOptions.WriteTimeout + } + if o.IdleTimeout == 0 { + o.IdleTimeout = defaultHTTPServerOptions.IdleTimeout + } +} diff --git a/internal/httputil/test_data/cert.pem b/internal/httputil/test_data/cert.pem new file mode 100644 index 000000000..87a6bf824 --- /dev/null +++ b/internal/httputil/test_data/cert.pem @@ -0,0 +1,10 @@ +-----BEGIN CERTIFICATE----- +MIIBeDCCAR+gAwIBAgIUUGE8w2S7XzpkVLbNq5QUxyVOwqEwCgYIKoZIzj0EAwIw +ETEPMA0GA1UEAwwGdW51c2VkMCAXDTE5MDcxNTIzNDQyOVoYDzQ3NTcwNjExMjM0 +NDI5WjARMQ8wDQYDVQQDDAZ1bnVzZWQwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC +AAQW6Z1KsR712c8RRTcu7ILyXowzo9582ClKxEvgasPbZchMyOoMoWuOolN/QWjV +labi/4R2zqzzyuwvMQL5wotFo1MwUTAdBgNVHQ4EFgQURYdcaniRqBHXeaM79LtV +pyJ4EwAwHwYDVR0jBBgwFoAURYdcaniRqBHXeaM79LtVpyJ4EwAwDwYDVR0TAQH/ +BAUwAwEB/zAKBggqhkjOPQQDAgNHADBEAiBHbhVnGbwXqaMZ1dB8eBAK56jyeWDZ +2PWXmFMTu7+RywIgaZ7UwVNB2k7KjEEBiLm0PIRcpJmczI2cP9+ZMIkPHHw= +-----END CERTIFICATE----- diff --git a/internal/httputil/test_data/privkey.pem b/internal/httputil/test_data/privkey.pem new file mode 100644 index 000000000..287f2b874 --- /dev/null +++ b/internal/httputil/test_data/privkey.pem @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIMQiDy26/R4ca/OdnjIf8OEDeHcw8yB5SDV9FD500CW5oAoGCCqGSM49 +AwEHoUQDQgAEFumdSrEe9dnPEUU3LuyC8l6MM6PefNgpSsRL4GrD22XITMjqDKFr +jqJTf0Fo1ZWm4v+Eds6s88rsLzEC+cKLRQ== +-----END EC PRIVATE KEY----- diff --git a/internal/httputil/https.go b/internal/httputil/tls.go similarity index 67% rename from internal/httputil/https.go rename to internal/httputil/tls.go index 215c94ba9..600ebf572 100644 --- a/internal/httputil/https.go +++ b/internal/httputil/tls.go @@ -7,83 +7,20 @@ import ( stdlog "log" "net" "net/http" - "os" - "path/filepath" "strings" - "time" "github.com/pomerium/pomerium/internal/fileutil" "github.com/pomerium/pomerium/internal/log" ) -// Options contains the configurations settings for a TLS http server. -type Options struct { - // Addr specifies the host and port on which the server should serve - // HTTPS requests. If empty, ":https" is used. - Addr string - - // TLS certificates to use. - Cert string - Key string - CertFile string - KeyFile string - - // Timeouts - ReadHeaderTimeout time.Duration - ReadTimeout time.Duration - WriteTimeout time.Duration - IdleTimeout time.Duration -} - -var defaultOptions = &Options{ - Addr: ":https", - CertFile: filepath.Join(findKeyDir(), "cert.pem"), - KeyFile: filepath.Join(findKeyDir(), "privkey.pem"), - ReadHeaderTimeout: 10 * time.Second, - ReadTimeout: 30 * time.Second, - WriteTimeout: 0, // support streaming by default - IdleTimeout: 5 * time.Minute, -} - -func findKeyDir() string { - p, err := os.Getwd() - if err != nil { - return "." - } - return p -} - -func (o *Options) applyDefaults() { - if o.Addr == "" { - o.Addr = defaultOptions.Addr - } - if o.Cert == "" && o.CertFile == "" { - o.CertFile = defaultOptions.CertFile - } - if o.Key == "" && o.KeyFile == "" { - o.KeyFile = defaultOptions.KeyFile - } - if o.ReadHeaderTimeout == 0 { - o.ReadHeaderTimeout = defaultOptions.ReadHeaderTimeout - } - if o.ReadTimeout == 0 { - o.ReadTimeout = defaultOptions.ReadTimeout - } - if o.WriteTimeout == 0 { - o.WriteTimeout = defaultOptions.WriteTimeout - } - if o.IdleTimeout == 0 { - o.IdleTimeout = defaultOptions.IdleTimeout - } -} - -// ListenAndServeTLS serves the provided handlers by HTTPS -// using the provided options. -func ListenAndServeTLS(opt *Options, httpHandler http.Handler, grpcHandler http.Handler) error { +// NewTLSServer creates a new TLS server given a set of options, handlers, and +// optionally a set of gRPC endpoints as well. +// It is the callers responsibility to close the resturned server. +func NewTLSServer(opt *ServerOptions, httpHandler http.Handler, grpcHandler http.Handler) (*http.Server, error) { if opt == nil { - opt = defaultOptions + opt = defaultTLSServerOptions } else { - opt.applyDefaults() + opt.applyTLSDefaults() } var cert *tls.Certificate var err error @@ -93,12 +30,12 @@ func ListenAndServeTLS(opt *Options, httpHandler http.Handler, grpcHandler http. cert, err = readCertificateFile(opt.CertFile, opt.KeyFile) } if err != nil { - return fmt.Errorf("https: failed loading x509 certificate: %v", err) + return nil, fmt.Errorf("internal/httputil: failed loading x509 certificate: %v", err) } config := newDefaultTLSConfig(cert) ln, err := net.Listen("tcp", opt.Addr) if err != nil { - return err + return nil, err } ln = tls.NewListener(ln, config) @@ -112,7 +49,7 @@ func ListenAndServeTLS(opt *Options, httpHandler http.Handler, grpcHandler http. sublogger := log.With().Str("addr", opt.Addr).Logger() // Set up the main server. - server := &http.Server{ + srv := &http.Server{ ReadHeaderTimeout: opt.ReadHeaderTimeout, ReadTimeout: opt.ReadTimeout, WriteTimeout: opt.WriteTimeout, @@ -121,8 +58,13 @@ func ListenAndServeTLS(opt *Options, httpHandler http.Handler, grpcHandler http. Handler: h, ErrorLog: stdlog.New(&log.StdLogWrapper{Logger: &sublogger}, "", 0), } + go func() { + if err := srv.Serve(ln); err != http.ErrServerClosed { + log.Error().Err(err).Msg("internal/httputil: tls server crashed") + } + }() - return server.Serve(ln) + return srv, nil } func decodeCertificate(cert, key string) (*tls.Certificate, error) { @@ -189,8 +131,8 @@ func newDefaultTLSConfig(cert *tls.Certificate) *tls.Config { return tlsConfig } -// grpcHandlerFunc splits request serving between gRPC and HTTPS depending on the request type. -// Requires HTTP/2. +// grpcHandlerFunc splits request serving between gRPC and HTTPS depending on +// the request type. Requires HTTP/2 to be enabled. func grpcHandlerFunc(rpcServer http.Handler, other http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ct := r.Header.Get("Content-Type") diff --git a/internal/httputil/tls_test.go b/internal/httputil/tls_test.go new file mode 100644 index 000000000..d9a2c22e2 --- /dev/null +++ b/internal/httputil/tls_test.go @@ -0,0 +1,210 @@ +package httputil + +import ( + "encoding/base64" + "fmt" + "io/ioutil" + "log" + "net/http" + "net/http/httptest" + "os" + "os/signal" + "syscall" + "testing" + "time" +) + +const privKey = `-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIMQiDy26/R4ca/OdnjIf8OEDeHcw8yB5SDV9FD500CW5oAoGCCqGSM49 +AwEHoUQDQgAEFumdSrEe9dnPEUU3LuyC8l6MM6PefNgpSsRL4GrD22XITMjqDKFr +jqJTf0Fo1ZWm4v+Eds6s88rsLzEC+cKLRQ== +-----END EC PRIVATE KEY-----` +const pubKey = `-----BEGIN CERTIFICATE----- +MIIBeDCCAR+gAwIBAgIUUGE8w2S7XzpkVLbNq5QUxyVOwqEwCgYIKoZIzj0EAwIw +ETEPMA0GA1UEAwwGdW51c2VkMCAXDTE5MDcxNTIzNDQyOVoYDzQ3NTcwNjExMjM0 +NDI5WjARMQ8wDQYDVQQDDAZ1bnVzZWQwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC +AAQW6Z1KsR712c8RRTcu7ILyXowzo9582ClKxEvgasPbZchMyOoMoWuOolN/QWjV +labi/4R2zqzzyuwvMQL5wotFo1MwUTAdBgNVHQ4EFgQURYdcaniRqBHXeaM79LtV +pyJ4EwAwHwYDVR0jBBgwFoAURYdcaniRqBHXeaM79LtVpyJ4EwAwDwYDVR0TAQH/ +BAUwAwEB/zAKBggqhkjOPQQDAgNHADBEAiBHbhVnGbwXqaMZ1dB8eBAK56jyeWDZ +2PWXmFMTu7+RywIgaZ7UwVNB2k7KjEEBiLm0PIRcpJmczI2cP9+ZMIkPHHw= +-----END CERTIFICATE-----` + +func TestNewTLSServer(t *testing.T) { + t.Parallel() + tests := []struct { + name string + opt *ServerOptions + httpHandler http.Handler + grpcHandler http.Handler + // want *http.Server + wantErr bool + }{ + {"good basic http handler", + &ServerOptions{ + Addr: "127.0.0.1:0", + Cert: base64.StdEncoding.EncodeToString([]byte(pubKey)), + Key: base64.StdEncoding.EncodeToString([]byte(privKey)), + }, + http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, "Hello, http") + }), + nil, + false}, + {"good basic http and grpc handler", + &ServerOptions{ + Addr: "127.0.0.1:0", + Cert: base64.StdEncoding.EncodeToString([]byte(pubKey)), + Key: base64.StdEncoding.EncodeToString([]byte(privKey)), + }, + http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, "Hello, http") + }), + http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, "Hello, grpc") + }), + false}, + {"good with cert files", + &ServerOptions{ + Addr: "127.0.0.1:0", + CertFile: "test_data/cert.pem", + KeyFile: "test_data/privkey.pem", + }, + http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, "Hello, http") + }), + http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, "Hello, grpc") + }), + false}, + {"unreadable cert file", + &ServerOptions{ + Addr: "127.0.0.1:0", + CertFile: "test_data", + KeyFile: "test_data/privkey.pem", + }, + http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, "Hello, http") + }), + http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, "Hello, grpc") + }), + true}, + {"unreadable key file", + &ServerOptions{ + Addr: "127.0.0.1:0", + CertFile: "./test_data/cert.pem", + KeyFile: "./test_data", + }, + http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, "Hello, http") + }), + http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, "Hello, grpc") + }), + true}, + {"unreadable key file", + &ServerOptions{ + Addr: "127.0.0.1:0", + CertFile: "./test_data/cert.pem", + KeyFile: "./test_data/file-does-not-exist", + }, + http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, "Hello, http") + }), + http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, "Hello, grpc") + }), + true}, + {"bad private key base64", + &ServerOptions{ + Addr: "127.0.0.1:0", + Cert: base64.StdEncoding.EncodeToString([]byte(pubKey)), + Key: "bad guy", + }, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, "Hello, http") + }), + nil, + true}, + {"bad public key base64", + &ServerOptions{ + Addr: "127.0.0.1:9999", + Key: base64.StdEncoding.EncodeToString([]byte(pubKey)), + Cert: "bad guy", + }, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, "Hello, http") + }), + nil, + true}, + {"bad port - invalid port range ", + &ServerOptions{ + Addr: "127.0.0.1:65536", + Cert: base64.StdEncoding.EncodeToString([]byte(pubKey)), + Key: base64.StdEncoding.EncodeToString([]byte(privKey)), + }, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, "Hello, http") + }), + nil, + true}, + {"nil apply default but will fail", + nil, + http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, "Hello, http") + }), + nil, + true}, + {"empty, apply defaults to missing", + &ServerOptions{}, + http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, "Hello, http") + }), + nil, + true}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + srv, err := NewTLSServer(tt.opt, tt.httpHandler, tt.grpcHandler) + if (err != nil) != tt.wantErr { + t.Errorf("NewTLSServer() error = %v, wantErr %v", err, tt.wantErr) + return + } + if err == nil { + // we cheat a little bit here and use the httptest server to test the client + ts := httptest.NewTLSServer(srv.Handler) + defer ts.Close() + client := ts.Client() + res, err := client.Get(ts.URL) + if err != nil { + log.Fatal(err) + } + greeting, err := ioutil.ReadAll(res.Body) + res.Body.Close() + if err != nil { + log.Fatal(err) + } + fmt.Printf("%s", greeting) + } + if srv != nil { + // simulate a sigterm and cleanup the server + c := make(chan os.Signal, 1) + signal.Notify(c, syscall.SIGINT) + defer signal.Stop(c) + go Shutdown(srv) + syscall.Kill(syscall.Getpid(), syscall.SIGINT) + waitSig(t, c, syscall.SIGINT) + } + + }) + } +} +func waitSig(t *testing.T, c <-chan os.Signal, sig os.Signal) { + select { + case s := <-c: + if s != sig { + t.Fatalf("signal was %v, want %v", s, sig) + } + case <-time.After(1 * time.Second): + t.Fatalf("timeout waiting for %v", sig) + } +} diff --git a/internal/identity/providers.go b/internal/identity/providers.go index f2d21a00f..394e69566 100644 --- a/internal/identity/providers.go +++ b/internal/identity/providers.go @@ -9,11 +9,12 @@ import ( "net/url" "time" - oidc "github.com/pomerium/go-oidc" - "golang.org/x/oauth2" - "github.com/pomerium/pomerium/internal/log" "github.com/pomerium/pomerium/internal/sessions" + "github.com/pomerium/pomerium/internal/telemetry/trace" + + oidc "github.com/pomerium/go-oidc" + "golang.org/x/oauth2" ) const ( @@ -117,6 +118,8 @@ func (p *Provider) GetSignInURL(state string) string { // Validate does NOT check if revoked. // https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation func (p *Provider) Validate(ctx context.Context, idToken string) (bool, error) { + ctx, span := trace.StartSpan(ctx, "identity.provider.Validate") + defer span.End() _, err := p.verifier.Verify(ctx, idToken) if err != nil { log.Error().Err(err).Msg("identity: failed to verify session state") diff --git a/internal/metrics/exporter.go b/internal/metrics/exporter.go deleted file mode 100644 index 832cd8363..000000000 --- a/internal/metrics/exporter.go +++ /dev/null @@ -1,30 +0,0 @@ -package metrics // import "github.com/pomerium/pomerium/internal/metrics" - -import ( - "net/http" - - ocProm "contrib.go.opencensus.io/exporter/prometheus" - prom "github.com/prometheus/client_golang/prometheus" - "go.opencensus.io/stats/view" -) - -//NewPromHTTPListener creates a prometheus exporter on ListenAddr -func NewPromHTTPListener(addr string) error { - return http.ListenAndServe(addr, newPromHTTPHandler()) -} - -// newPromHTTPHandler creates a new prometheus exporter handler for /metrics -func newPromHTTPHandler() http.Handler { - // TODO this is a cheap way to get thorough go process - // stats. It will not work with additional exporters. - // It should turn into an FR to the OC framework - reg := prom.DefaultRegisterer.(*prom.Registry) - pe, _ := ocProm.NewExporter(ocProm.Options{ - Namespace: "pomerium", - Registry: reg, - }) - view.RegisterExporter(pe) - mux := http.NewServeMux() - mux.Handle("/metrics", pe) - return mux -} diff --git a/internal/metrics/middleware.go b/internal/metrics/middleware.go deleted file mode 100644 index 283cd25a6..000000000 --- a/internal/metrics/middleware.go +++ /dev/null @@ -1,151 +0,0 @@ -package metrics // import "github.com/pomerium/pomerium/internal/metrics" - -import ( - "net/http" - - "go.opencensus.io/plugin/ochttp" - - "github.com/pomerium/pomerium/internal/log" - - "github.com/pomerium/pomerium/internal/tripper" - "go.opencensus.io/stats/view" - "go.opencensus.io/tag" -) - -var ( - httpSizeDistribution = view.Distribution( - 1, 256, 512, 1024, 2048, 8192, 16384, 32768, 65536, 131072, 262144, 524288, - 1048576, 2097152, 4194304, 8388608, - ) - - httpLatencyDistrubtion = view.Distribution( - 1, 2, 5, 7, 10, 25, 500, 750, - 100, 250, 500, 750, - 1000, 2500, 5000, 7500, - 10000, 25000, 50000, 75000, - 100000, - ) - - // httpClientRequestCount = stats.Int64("http_client_requests_total", "Total HTTP Client Requests", "1") - // httpClientResponseSize = stats.Int64("http_client_response_size_bytes", "HTTP Client Response Size in bytes", "bytes") - // httpClientRequestDuration = stats.Int64("http_client_request_duration_ms", "HTTP Client Request duration in ms", "ms") - - // HTTPServerRequestCountView is an OpenCensus View that tracks HTTP server requests by pomerium service, host, method and status - HTTPServerRequestCountView = &view.View{ - Name: "http_server_requests_total", - Measure: ochttp.ServerLatency, - Description: "Total HTTP Requests", - TagKeys: []tag.Key{keyService, keyHost, keyHTTPMethod, ochttp.StatusCode}, - Aggregation: view.Count(), - } - - // HTTPServerRequestDurationView is an OpenCensus view that tracks HTTP server request duration by pomerium service, host, method and status - HTTPServerRequestDurationView = &view.View{ - Name: "http_server_request_duration_ms", - Measure: ochttp.ServerLatency, - Description: "HTTP Request duration in ms", - TagKeys: []tag.Key{keyService, keyHost, keyHTTPMethod, ochttp.StatusCode}, - Aggregation: httpLatencyDistrubtion, - } - - // HTTPServerRequestSizeView is an OpenCensus view that tracks HTTP server request size by pomerium service, host and method - HTTPServerRequestSizeView = &view.View{ - Name: "http_server_request_size_bytes", - Measure: ochttp.ServerRequestBytes, - Description: "HTTP Server Request Size in bytes", - TagKeys: []tag.Key{keyService, keyHost, keyHTTPMethod}, - Aggregation: httpSizeDistribution, - } - - // HTTPServerResponseSizeView is an OpenCensus view that tracks HTTP server response size by pomerium service, host, method and status - HTTPServerResponseSizeView = &view.View{ - Name: "http_server_response_size_bytes", - Measure: ochttp.ServerResponseBytes, - Description: "HTTP Server Response Size in bytes", - TagKeys: []tag.Key{keyService, keyHost, keyHTTPMethod, ochttp.StatusCode}, - Aggregation: httpSizeDistribution, - } - - // HTTPClientRequestCountView is an OpenCensus View that tracks HTTP client requests by pomerium service, destination, host, method and status - HTTPClientRequestCountView = &view.View{ - Name: "http_client_requests_total", - Measure: ochttp.ClientRoundtripLatency, - Description: "Total HTTP Client Requests", - TagKeys: []tag.Key{keyService, keyHost, keyHTTPMethod, ochttp.StatusCode, keyDestination}, - Aggregation: view.Count(), - } - - // HTTPClientRequestDurationView is an OpenCensus view that tracks HTTP client request duration by pomerium service, destination, host, method and status - HTTPClientRequestDurationView = &view.View{ - Name: "http_client_request_duration_ms", - Measure: ochttp.ClientRoundtripLatency, - Description: "HTTP Client Request duration in ms", - TagKeys: []tag.Key{keyService, keyHost, keyHTTPMethod, ochttp.StatusCode, keyDestination}, - Aggregation: httpLatencyDistrubtion, - } - - // HTTPClientResponseSizeView is an OpenCensus view that tracks HTTP client response size by pomerium service, destination, host, method and status - HTTPClientResponseSizeView = &view.View{ - Name: "http_client_response_size_bytes", - Measure: ochttp.ClientReceivedBytes, - Description: "HTTP Client Response Size in bytes", - TagKeys: []tag.Key{keyService, keyHost, keyHTTPMethod, ochttp.StatusCode, keyDestination}, - Aggregation: httpSizeDistribution, - } - - // HTTPClientRequestSizeView is an OpenCensus view that tracks HTTP client request size by pomerium service, destination, host and method - HTTPClientRequestSizeView = &view.View{ - Name: "http_client_response_size_bytes", - Measure: ochttp.ClientSentBytes, - Description: "HTTP Client Response Size in bytes", - TagKeys: []tag.Key{keyService, keyHost, keyHTTPMethod, keyDestination}, - Aggregation: httpSizeDistribution, - } -) - -// HTTPMetricsHandler creates a metrics middleware for incoming HTTP requests -func HTTPMetricsHandler(service string) func(next http.Handler) http.Handler { - - return func(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - ctx, tagErr := tag.New( - r.Context(), - tag.Insert(keyService, service), - tag.Insert(keyHost, r.Host), - tag.Insert(keyHTTPMethod, r.Method), - ) - if tagErr != nil { - log.Warn().Err(tagErr).Str("context", "HTTPMetricsHandler").Msg("internal/metrics: Failed to create metrics context tag") - next.ServeHTTP(w, r) - return - } - - ocHandler := ochttp.Handler{Handler: next} - ocHandler.ServeHTTP(w, r.WithContext(ctx)) - }) - } -} - -// HTTPMetricsRoundTripper creates a metrics tracking tripper for outbound HTTP Requests -func HTTPMetricsRoundTripper(service string, destination string) func(next http.RoundTripper) http.RoundTripper { - return func(next http.RoundTripper) http.RoundTripper { - return tripper.RoundTripperFunc(func(r *http.Request) (*http.Response, error) { - - ctx, tagErr := tag.New( - r.Context(), - tag.Insert(keyService, service), - tag.Insert(keyHost, r.Host), - tag.Insert(keyHTTPMethod, r.Method), - tag.Insert(keyDestination, destination), - ) - - if tagErr != nil { - log.Warn().Err(tagErr).Str("context", "HTTPMetricsRoundTripper").Msg("internal/metrics: Failed to create context tag") - return next.RoundTrip(r) - } - - ocTransport := ochttp.Transport{Base: next} - return ocTransport.RoundTrip(r.WithContext(ctx)) - }) - } -} diff --git a/internal/metrics/tags.go b/internal/metrics/tags.go deleted file mode 100644 index 4d712cc7e..000000000 --- a/internal/metrics/tags.go +++ /dev/null @@ -1,14 +0,0 @@ -package metrics - -import ( - "go.opencensus.io/tag" -) - -var ( - keyHTTPMethod tag.Key = tag.MustNewKey("http_method") - keyService tag.Key = tag.MustNewKey("service") - keyGRPCService tag.Key = tag.MustNewKey("grpc_service") - keyGRPCMethod tag.Key = tag.MustNewKey("grpc_method") - keyHost tag.Key = tag.MustNewKey("host") - keyDestination tag.Key = tag.MustNewKey("destination") -) diff --git a/internal/metrics/view.go b/internal/metrics/view.go deleted file mode 100644 index 5331d8620..000000000 --- a/internal/metrics/view.go +++ /dev/null @@ -1,32 +0,0 @@ -package metrics - -import ( - "github.com/pomerium/pomerium/internal/log" - "go.opencensus.io/stats/view" -) - -var ( - // HTTPClientViews contains opencensus views for HTTP Client metrics - HTTPClientViews = []*view.View{HTTPClientRequestCountView, HTTPClientRequestDurationView, HTTPClientResponseSizeView} - // HTTPServerViews contains opencensus views for HTTP Server metrics - HTTPServerViews = []*view.View{HTTPServerRequestCountView, HTTPServerRequestDurationView, HTTPServerRequestSizeView, HTTPServerResponseSizeView} - // GRPCClientViews contains opencensus views for GRPC Client metrics - GRPCClientViews = []*view.View{GRPCClientRequestCountView, GRPCClientRequestDurationView, GRPCClientResponseSizeView, GRPCClientRequestSizeView} - // GRPCServerViews contains opencensus views for GRPC Server metrics - GRPCServerViews = []*view.View{GRPCServerRequestCountView, GRPCServerRequestDurationView, GRPCServerResponseSizeView, GRPCServerRequestSizeView} - // InfoViews contains opencensus views for Info metrics - InfoViews = []*view.View{ConfigLastReloadView, ConfigLastReloadSuccessView} -) - -// RegisterView registers one of the defined metrics views. It must be called for metrics to see metrics -// in the configured exporters -func RegisterView(v []*view.View) { - if err := view.Register(v...); err != nil { - log.Warn().Str("context", "RegisterView").Err(err).Msg("internal/metrics: Could not register view") - } -} - -// UnRegisterView unregisters one of the defined metrics views. -func UnRegisterView(v []*view.View) { - view.Unregister(v...) -} diff --git a/internal/metrics/view_test.go b/internal/metrics/view_test.go deleted file mode 100644 index 3d34ea85d..000000000 --- a/internal/metrics/view_test.go +++ /dev/null @@ -1,25 +0,0 @@ -package metrics - -import ( - "testing" - - "go.opencensus.io/stats/view" -) - -func Test_RegisterView(t *testing.T) { - RegisterView(HTTPClientViews) - for _, v := range HTTPClientViews { - if view.Find(v.Name) != v { - t.Errorf("Failed to find registered view %s", v.Name) - } - } -} - -func Test_UnregisterView(t *testing.T) { - UnRegisterView(HTTPClientViews) - for _, v := range HTTPClientViews { - if view.Find(v.Name) == v { - t.Errorf("Found unregistered view %s", v.Name) - } - } -} diff --git a/internal/middleware/grpc.go b/internal/middleware/grpc.go index 60324f252..91adf8c5c 100644 --- a/internal/middleware/grpc.go +++ b/internal/middleware/grpc.go @@ -3,6 +3,8 @@ package middleware // import "github.com/pomerium/pomerium/internal/middleware" import ( "context" + "github.com/pomerium/pomerium/internal/telemetry/trace" + "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/metadata" @@ -30,6 +32,9 @@ func (s SharedSecretCred) RequireTransportSecurity() bool { return false } // handler and returns an error. Otherwise, the interceptor invokes the unary // handler. func (s SharedSecretCred) ValidateRequest(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { + ctx, span := trace.StartSpan(ctx, "middleware.grpc.ValidateRequest") + defer span.End() + md, ok := metadata.FromIncomingContext(ctx) if !ok { return nil, status.Errorf(codes.InvalidArgument, "missing metadata") diff --git a/internal/middleware/middleware.go b/internal/middleware/middleware.go index 18e65b1ab..72e4bf70c 100644 --- a/internal/middleware/middleware.go +++ b/internal/middleware/middleware.go @@ -12,6 +12,8 @@ import ( "github.com/pomerium/pomerium/internal/cryptutil" "github.com/pomerium/pomerium/internal/httputil" + "github.com/pomerium/pomerium/internal/telemetry/trace" + "golang.org/x/net/publicsuffix" ) @@ -19,10 +21,12 @@ import ( func SetHeaders(securityHeaders map[string]string) func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ctx, span := trace.StartSpan(r.Context(), "middleware.SetHeaders") + defer span.End() for key, val := range securityHeaders { w.Header().Set(key, val) } - next.ServeHTTP(w, r) + next.ServeHTTP(w, r.WithContext(ctx)) }) } } @@ -32,6 +36,9 @@ func SetHeaders(securityHeaders map[string]string) func(next http.Handler) http. func ValidateClientSecret(sharedSecret string) func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ctx, span := trace.StartSpan(r.Context(), "middleware.ValidateClientSecret") + defer span.End() + if err := r.ParseForm(); err != nil { httpErr := &httputil.Error{Message: err.Error(), Code: http.StatusBadRequest} httputil.ErrorResponse(w, r, httpErr) @@ -47,7 +54,7 @@ func ValidateClientSecret(sharedSecret string) func(next http.Handler) http.Hand httputil.ErrorResponse(w, r, &httputil.Error{Code: http.StatusInternalServerError}) return } - next.ServeHTTP(w, r) + next.ServeHTTP(w, r.WithContext(ctx)) }) } } @@ -57,6 +64,8 @@ func ValidateClientSecret(sharedSecret string) func(next http.Handler) http.Hand func ValidateRedirectURI(rootDomain *url.URL) func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ctx, span := trace.StartSpan(r.Context(), "middleware.ValidateRedirectURI") + defer span.End() err := r.ParseForm() if err != nil { httpErr := &httputil.Error{ @@ -80,7 +89,7 @@ func ValidateRedirectURI(rootDomain *url.URL) func(next http.Handler) http.Handl httputil.ErrorResponse(w, r, httpErr) return } - next.ServeHTTP(w, r) + next.ServeHTTP(w, r.WithContext(ctx)) }) } } @@ -103,6 +112,9 @@ func SameDomain(u, j *url.URL) bool { func ValidateSignature(sharedSecret string) func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ctx, span := trace.StartSpan(r.Context(), "middleware.ValidateSignature") + defer span.End() + err := r.ParseForm() if err != nil { httpErr := &httputil.Error{Message: err.Error(), Code: http.StatusBadRequest} @@ -120,7 +132,7 @@ func ValidateSignature(sharedSecret string) func(next http.Handler) http.Handler return } - next.ServeHTTP(w, r) + next.ServeHTTP(w, r.WithContext(ctx)) }) } } @@ -129,11 +141,14 @@ func ValidateSignature(sharedSecret string) func(next http.Handler) http.Handler func ValidateHost(validHost func(host string) bool) func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ctx, span := trace.StartSpan(r.Context(), "middleware.ValidateHost") + defer span.End() + if !validHost(r.Host) { httputil.ErrorResponse(w, r, &httputil.Error{Code: http.StatusNotFound}) return } - next.ServeHTTP(w, r) + next.ServeHTTP(w, r.WithContext(ctx)) }) } } @@ -145,13 +160,16 @@ func ValidateHost(validHost func(host string) bool) func(next http.Handler) http func Healthcheck(endpoint, msg string) func(http.Handler) http.Handler { f := func(next http.Handler) http.Handler { fn := func(w http.ResponseWriter, r *http.Request) { + ctx, span := trace.StartSpan(r.Context(), "middleware.Healthcheck") + defer span.End() + if r.Method == "GET" && strings.EqualFold(r.URL.Path, endpoint) { w.Header().Set("Content-Type", "text/plain") w.WriteHeader(http.StatusOK) w.Write([]byte(msg)) return } - next.ServeHTTP(w, r) + next.ServeHTTP(w, r.WithContext(ctx)) } return http.HandlerFunc(fn) } diff --git a/internal/middleware/reverse_proxy.go b/internal/middleware/reverse_proxy.go index bd4e6e780..280caf873 100644 --- a/internal/middleware/reverse_proxy.go +++ b/internal/middleware/reverse_proxy.go @@ -6,11 +6,14 @@ import ( "github.com/pomerium/pomerium/internal/cryptutil" "github.com/pomerium/pomerium/internal/log" + "github.com/pomerium/pomerium/internal/telemetry/trace" ) func SignRequest(signer cryptutil.JWTSigner, id, email, groups, header string) func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ctx, span := trace.StartSpan(r.Context(), "middleware.SignRequest") + defer span.End() jwt, err := signer.SignJWT( r.Header.Get(id), r.Header.Get(email), @@ -20,7 +23,7 @@ func SignRequest(signer cryptutil.JWTSigner, id, email, groups, header string) f } else { r.Header.Set(header, jwt) } - next.ServeHTTP(w, r) + next.ServeHTTP(w, r.WithContext(ctx)) }) } } @@ -29,6 +32,9 @@ func SignRequest(signer cryptutil.JWTSigner, id, email, groups, header string) f func StripPomeriumCookie(cookieName string) func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ctx, span := trace.StartSpan(r.Context(), "middleware.SignRequest") + defer span.End() + headers := make([]string, len(r.Cookies())) for _, cookie := range r.Cookies() { if cookie.Name != cookieName { @@ -36,7 +42,7 @@ func StripPomeriumCookie(cookieName string) func(next http.Handler) http.Handler } } r.Header.Set("Cookie", strings.Join(headers, ";")) - next.ServeHTTP(w, r) + next.ServeHTTP(w, r.WithContext(ctx)) }) } } diff --git a/internal/telemetry/metrics/const.go b/internal/telemetry/metrics/const.go new file mode 100644 index 000000000..7066ec3c0 --- /dev/null +++ b/internal/telemetry/metrics/const.go @@ -0,0 +1,41 @@ +package metrics // import "github.com/pomerium/pomerium/internal/telemetry/metrics" + +import ( + "go.opencensus.io/plugin/ocgrpc" + "go.opencensus.io/stats/view" + "go.opencensus.io/tag" +) + +// The following tags are applied to stats recorded by this package. +var ( + TagKeyHTTPMethod tag.Key = tag.MustNewKey("http_method") + TagKeyService tag.Key = tag.MustNewKey("service") + TagKeyGRPCService tag.Key = tag.MustNewKey("grpc_service") + TagKeyGRPCMethod tag.Key = tag.MustNewKey("grpc_method") + TagKeyHost tag.Key = tag.MustNewKey("host") + TagKeyDestination tag.Key = tag.MustNewKey("destination") +) + +// Default distributions used by views in this package. +var ( + DefaulHTTPSizeDistribution = view.Distribution( + 1, 256, 512, 1024, 2048, 8192, 16384, 32768, 65536, 131072, 262144, + 524288, 1048576, 2097152, 4194304, 8388608) + DefaultHTTPLatencyDistrubtion = view.Distribution( + 1, 2, 5, 7, 10, 25, 500, 750, 100, 250, 500, 750, 1000, 2500, 5000, + 7500, 10000, 25000, 50000, 75000, 100000) + grpcSizeDistribution = view.Distribution( + 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, + 2048, 4096, 8192, 16384, + ) + DefaultMillisecondsDistribution = ocgrpc.DefaultMillisecondsDistribution +) + +// DefaultViews are a set of default views to view HTTP and GRPC metrics. +var ( + DefaultViews = [][]*view.View{ + GRPCServerViews, + HTTPServerViews, + GRPCClientViews, + GRPCServerViews} +) diff --git a/internal/metrics/interceptors.go b/internal/telemetry/metrics/grpc.go similarity index 68% rename from internal/metrics/interceptors.go rename to internal/telemetry/metrics/grpc.go index b736a7559..04c2b3d66 100644 --- a/internal/metrics/interceptors.go +++ b/internal/telemetry/metrics/grpc.go @@ -1,4 +1,4 @@ -package metrics // import "github.com/pomerium/pomerium/internal/metrics" +package metrics // import "github.com/pomerium/pomerium/internal/telemetry/metrics" import ( "context" @@ -12,93 +12,98 @@ import ( grpcstats "google.golang.org/grpc/stats" ) +// GRPC Views var ( - grpcSizeDistribution = view.Distribution( - 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, - 2048, 4096, 8192, 16384, - ) - grcpLatencyDistribution = view.Distribution( - 1, 2, 5, 7, 10, 25, 50, 75, - 100, 250, 500, 750, 1000, - ) + // GRPCClientViews contains opencensus views for GRPC Client metrics. + GRPCClientViews = []*view.View{ + GRPCClientRequestCountView, + GRPCClientRequestDurationView, + GRPCClientResponseSizeView, + GRPCClientRequestSizeView} + // GRPCServerViews contains opencensus views for GRPC Server metrics. + GRPCServerViews = []*view.View{ + GRPCServerRequestCountView, + GRPCServerRequestDurationView, + GRPCServerResponseSizeView, + GRPCServerRequestSizeView} // GRPCServerRequestCountView is an OpenCensus view which counts GRPC Server // requests by pomerium service, grpc service, grpc method, and status GRPCServerRequestCountView = &view.View{ - Name: "grpc_server_requests_total", + Name: "grpc/server/requests_total", Measure: ocgrpc.ServerLatency, Description: "Total grpc Requests", - TagKeys: []tag.Key{keyService, keyGRPCMethod, ocgrpc.KeyServerStatus, keyGRPCService}, + TagKeys: []tag.Key{TagKeyService, TagKeyGRPCMethod, ocgrpc.KeyServerStatus, TagKeyGRPCService}, Aggregation: view.Count(), } // GRPCServerRequestDurationView is an OpenCensus view which tracks GRPC Server // request duration by pomerium service, grpc service, grpc method, and status GRPCServerRequestDurationView = &view.View{ - Name: "grpc_server_request_duration_ms", + Name: "grpc/server/request_duration_ms", Measure: ocgrpc.ServerLatency, Description: "grpc Request duration in ms", - TagKeys: []tag.Key{keyService, keyGRPCMethod, ocgrpc.KeyServerStatus, keyGRPCService}, - Aggregation: grcpLatencyDistribution, + TagKeys: []tag.Key{TagKeyService, TagKeyGRPCMethod, ocgrpc.KeyServerStatus, TagKeyGRPCService}, + Aggregation: DefaultMillisecondsDistribution, } // GRPCServerResponseSizeView is an OpenCensus view which tracks GRPC Server // response size by pomerium service, grpc service, grpc method, and status GRPCServerResponseSizeView = &view.View{ - Name: "grpc_server_response_size_bytes", + Name: "grpc/server/response_size_bytes", Measure: ocgrpc.ServerSentBytesPerRPC, Description: "grpc Server Response Size in bytes", - TagKeys: []tag.Key{keyService, keyGRPCMethod, ocgrpc.KeyServerStatus, keyGRPCService}, + TagKeys: []tag.Key{TagKeyService, TagKeyGRPCMethod, ocgrpc.KeyServerStatus, TagKeyGRPCService}, Aggregation: grpcSizeDistribution, } // GRPCServerRequestSizeView is an OpenCensus view which tracks GRPC Server // request size by pomerium service, grpc service, grpc method, and status GRPCServerRequestSizeView = &view.View{ - Name: "grpc_server_request_size_bytes", + Name: "grpc/server/request_size_bytes", Measure: ocgrpc.ServerReceivedBytesPerRPC, Description: "grpc Server Request Size in bytes", - TagKeys: []tag.Key{keyService, keyGRPCMethod, ocgrpc.KeyServerStatus, keyGRPCService}, + TagKeys: []tag.Key{TagKeyService, TagKeyGRPCMethod, ocgrpc.KeyServerStatus, TagKeyGRPCService}, Aggregation: grpcSizeDistribution, } // GRPCClientRequestCountView is an OpenCensus view which tracks GRPC Client // requests by pomerium service, target host, grpc service, grpc method, and status GRPCClientRequestCountView = &view.View{ - Name: "grpc_client_requests_total", + Name: "grpc/client/requests_total", Measure: ocgrpc.ClientRoundtripLatency, Description: "Total grpc Client Requests", - TagKeys: []tag.Key{keyService, keyHost, keyGRPCMethod, keyGRPCService, ocgrpc.KeyClientStatus}, + TagKeys: []tag.Key{TagKeyService, TagKeyHost, TagKeyGRPCMethod, TagKeyGRPCService, ocgrpc.KeyClientStatus}, Aggregation: view.Count(), } // GRPCClientRequestDurationView is an OpenCensus view which tracks GRPC Client // request duration by pomerium service, target host, grpc service, grpc method, and status GRPCClientRequestDurationView = &view.View{ - Name: "grpc_client_request_duration_ms", + Name: "grpc/client/request_duration_ms", Measure: ocgrpc.ClientRoundtripLatency, Description: "grpc Client Request duration in ms", - TagKeys: []tag.Key{keyService, keyHost, keyGRPCMethod, keyGRPCService, ocgrpc.KeyClientStatus}, - Aggregation: grcpLatencyDistribution, + TagKeys: []tag.Key{TagKeyService, TagKeyHost, TagKeyGRPCMethod, TagKeyGRPCService, ocgrpc.KeyClientStatus}, + Aggregation: DefaultMillisecondsDistribution, } // GRPCClientResponseSizeView is an OpenCensus view which tracks GRPC Client // response size by pomerium service, target host, grpc service, grpc method, and status GRPCClientResponseSizeView = &view.View{ - Name: "grpc_client_response_size_bytes", + Name: "grpc/client/response_size_bytes", Measure: ocgrpc.ClientReceivedBytesPerRPC, Description: "grpc Client Response Size in bytes", - TagKeys: []tag.Key{keyService, keyHost, keyGRPCMethod, keyGRPCService, ocgrpc.KeyClientStatus}, + TagKeys: []tag.Key{TagKeyService, TagKeyHost, TagKeyGRPCMethod, TagKeyGRPCService, ocgrpc.KeyClientStatus}, Aggregation: grpcSizeDistribution, } // GRPCClientRequestSizeView is an OpenCensus view which tracks GRPC Client // request size by pomerium service, target host, grpc service, grpc method, and status GRPCClientRequestSizeView = &view.View{ - Name: "grpc_client_request_size_bytes", + Name: "grpc/client/request_size_bytes", Measure: ocgrpc.ClientSentBytesPerRPC, Description: "grpc Client Request Size in bytes", - TagKeys: []tag.Key{keyService, keyHost, keyGRPCMethod, keyGRPCService, ocgrpc.KeyClientStatus}, + TagKeys: []tag.Key{TagKeyService, TagKeyHost, TagKeyGRPCMethod, TagKeyGRPCService, ocgrpc.KeyClientStatus}, Aggregation: grpcSizeDistribution, } ) @@ -126,13 +131,13 @@ func GRPCClientInterceptor(service string) grpc.UnaryClientInterceptor { taggedCtx, tagErr := tag.New( ctx, - tag.Insert(keyService, service), - tag.Insert(keyHost, cc.Target()), - tag.Insert(keyGRPCMethod, rpcMethod), - tag.Insert(keyGRPCService, rpcService), + tag.Insert(TagKeyService, service), + tag.Insert(TagKeyHost, cc.Target()), + tag.Insert(TagKeyGRPCMethod, rpcMethod), + tag.Insert(TagKeyGRPCService, rpcService), ) if tagErr != nil { - log.Warn().Err(tagErr).Str("context", "GRPCClientInterceptor").Msg("internal/metrics: Failed to create context") + log.Warn().Err(tagErr).Str("context", "GRPCClientInterceptor").Msg("internal/telemetry: Failed to create context") return invoker(ctx, method, req, reply, cc, opts...) } @@ -165,12 +170,12 @@ func (h *GRPCServerStatsHandler) TagRPC(ctx context.Context, tagInfo *grpcstats. taggedCtx, tagErr := tag.New( handledCtx, - tag.Insert(keyService, h.service), - tag.Insert(keyGRPCMethod, rpcMethod), - tag.Insert(keyGRPCService, rpcService), + tag.Insert(TagKeyService, h.service), + tag.Insert(TagKeyGRPCMethod, rpcMethod), + tag.Insert(TagKeyGRPCService, rpcService), ) if tagErr != nil { - log.Warn().Err(tagErr).Str("context", "GRPCServerStatsHandler").Msg("internal/metrics: Failed to create context") + log.Warn().Err(tagErr).Str("context", "GRPCServerStatsHandler").Msg("internal/telemetry: Failed to create context") return handledCtx } @@ -180,6 +185,5 @@ func (h *GRPCServerStatsHandler) TagRPC(ctx context.Context, tagInfo *grpcstats. // NewGRPCServerStatsHandler creates a new GRPCServerStatsHandler for a pomerium service func NewGRPCServerStatsHandler(service string) grpcstats.Handler { - return &GRPCServerStatsHandler{service: service, Handler: &ocgrpc.ServerHandler{}} } diff --git a/internal/metrics/interceptors_test.go b/internal/telemetry/metrics/grpc_test.go similarity index 97% rename from internal/metrics/interceptors_test.go rename to internal/telemetry/metrics/grpc_test.go index 7a3470cc1..9dd8aa666 100644 --- a/internal/metrics/interceptors_test.go +++ b/internal/telemetry/metrics/grpc_test.go @@ -1,10 +1,11 @@ -package metrics +package metrics // import "github.com/pomerium/pomerium/internal/telemetry/metrics" import ( "context" "testing" "go.opencensus.io/plugin/ocgrpc" + "go.opencensus.io/stats/view" "google.golang.org/grpc" "google.golang.org/grpc/stats" "google.golang.org/grpc/status" @@ -97,8 +98,8 @@ func Test_GRPCClientInterceptor(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - UnRegisterView(GRPCClientViews) - RegisterView(GRPCClientViews) + view.Unregister(GRPCClientViews...) + view.Register(GRPCClientViews...) invoker := testInvoker{ invokeResult: tt.errorCode, @@ -167,8 +168,8 @@ func Test_GRPCServerStatsHandler(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - UnRegisterView(GRPCServerViews) - RegisterView(GRPCServerViews) + view.Unregister(GRPCServerViews...) + view.Register(GRPCServerViews...) statsHandler := NewGRPCServerStatsHandler("test_service") mockServerRPCHandle(statsHandler, tt.method, tt.errorCode) diff --git a/internal/metrics/helpers_test.go b/internal/telemetry/metrics/helpers_test.go similarity index 53% rename from internal/metrics/helpers_test.go rename to internal/telemetry/metrics/helpers_test.go index b8f02a2e7..96a7a2b50 100644 --- a/internal/metrics/helpers_test.go +++ b/internal/telemetry/metrics/helpers_test.go @@ -1,41 +1,12 @@ -package metrics +package metrics // import "github.com/pomerium/pomerium/internal/telemetry/metrics" import ( - "strings" "testing" "github.com/google/go-cmp/cmp" "go.opencensus.io/metric/metricdata" - "go.opencensus.io/stats/view" ) -func testDataRetrieval(v *view.View, t *testing.T, want string) { - if v == nil { - t.Fatalf("%s: nil view passed", t.Name()) - } - name := v.Name - data, err := view.RetrieveData(name) - - if err != nil { - t.Fatalf("%s: failed to retrieve data line %s", name, err) - } - - if want != "" && len(data) != 1 { - t.Fatalf("%s: received incorrect number of data rows: %d", name, len(data)) - } - if want == "" && len(data) > 0 { - t.Fatalf("%s: received incorrect number of data rows: %d", name, len(data)) - } else if want == "" { - return - } - - dataString := data[0].String() - - if want != "" && !strings.HasPrefix(dataString, want) { - t.Errorf("%s: Found unexpected data row: \nwant: %s\ngot: %s\n", name, want, dataString) - } -} - func testMetricRetrieval(metrics []*metricdata.Metric, t *testing.T, labels []metricdata.LabelValue, value interface{}, name string) { switch value.(type) { case int64: diff --git a/internal/telemetry/metrics/http.go b/internal/telemetry/metrics/http.go new file mode 100644 index 000000000..9a2586208 --- /dev/null +++ b/internal/telemetry/metrics/http.go @@ -0,0 +1,157 @@ +package metrics // import "github.com/pomerium/pomerium/internal/telemetry/metrics" + +import ( + "fmt" + "net/http" + + "github.com/pomerium/pomerium/internal/log" + "github.com/pomerium/pomerium/internal/tripper" + "go.opencensus.io/plugin/ochttp" + "go.opencensus.io/stats/view" + "go.opencensus.io/tag" +) + +// HTTP Views +var ( + // HTTPClientViews contains opencensus views for HTTP Client metrics. + HTTPClientViews = []*view.View{ + HTTPClientRequestCountView, + HTTPClientRequestDurationView, + HTTPClientResponseSizeView} + // HTTPServerViews contains opencensus views for HTTP Server metrics. + HTTPServerViews = []*view.View{ + HTTPServerRequestCountView, + HTTPServerRequestDurationView, + HTTPServerRequestSizeView, + HTTPServerResponseSizeView} + + // HTTPServerRequestCountView is an OpenCensus View that tracks HTTP server + // requests by pomerium service, host, method and status + HTTPServerRequestCountView = &view.View{ + Name: "http/server/requests_total", + Measure: ochttp.ServerLatency, + Description: "Total HTTP Requests", + TagKeys: []tag.Key{TagKeyService, TagKeyHost, TagKeyHTTPMethod, ochttp.StatusCode}, + Aggregation: view.Count(), + } + + // HTTPServerRequestDurationView is an OpenCensus view that tracks HTTP + // server request duration by pomerium service, host, method and status + HTTPServerRequestDurationView = &view.View{ + Name: "http/server/request_duration_ms", + Measure: ochttp.ServerLatency, + Description: "HTTP Request duration in ms", + TagKeys: []tag.Key{TagKeyService, TagKeyHost, TagKeyHTTPMethod, ochttp.StatusCode}, + Aggregation: DefaultHTTPLatencyDistrubtion, + } + + // HTTPServerRequestSizeView is an OpenCensus view that tracks HTTP server + // request size by pomerium service, host and method + HTTPServerRequestSizeView = &view.View{ + Name: "http/server/request_size_bytes", + Measure: ochttp.ServerRequestBytes, + Description: "HTTP Server Request Size in bytes", + TagKeys: []tag.Key{TagKeyService, TagKeyHost, TagKeyHTTPMethod}, + Aggregation: DefaulHTTPSizeDistribution, + } + + // HTTPServerResponseSizeView is an OpenCensus view that tracks HTTP server + // response size by pomerium service, host, method and status + HTTPServerResponseSizeView = &view.View{ + Name: "http/server/response_size_bytes", + Measure: ochttp.ServerResponseBytes, + Description: "HTTP Server Response Size in bytes", + TagKeys: []tag.Key{TagKeyService, TagKeyHost, TagKeyHTTPMethod, ochttp.StatusCode}, + Aggregation: DefaulHTTPSizeDistribution, + } + + // HTTPClientRequestCountView is an OpenCensus View that tracks HTTP client + // requests by pomerium service, destination, host, method and status + HTTPClientRequestCountView = &view.View{ + Name: "http/client/requests_total", + Measure: ochttp.ClientRoundtripLatency, + Description: "Total HTTP Client Requests", + TagKeys: []tag.Key{TagKeyService, TagKeyHost, TagKeyHTTPMethod, ochttp.StatusCode, TagKeyDestination}, + Aggregation: view.Count(), + } + + // HTTPClientRequestDurationView is an OpenCensus view that tracks HTTP + // client request duration by pomerium service, destination, host, method and status + HTTPClientRequestDurationView = &view.View{ + Name: "http/client/request_duration_ms", + Measure: ochttp.ClientRoundtripLatency, + Description: "HTTP Client Request duration in ms", + TagKeys: []tag.Key{TagKeyService, TagKeyHost, TagKeyHTTPMethod, ochttp.StatusCode, TagKeyDestination}, + Aggregation: DefaultHTTPLatencyDistrubtion, + } + + // HTTPClientResponseSizeView is an OpenCensus view that tracks HTTP client + // esponse size by pomerium service, destination, host, method and status + HTTPClientResponseSizeView = &view.View{ + Name: "http/client/response_size_bytes", + Measure: ochttp.ClientReceivedBytes, + Description: "HTTP Client Response Size in bytes", + TagKeys: []tag.Key{TagKeyService, TagKeyHost, TagKeyHTTPMethod, ochttp.StatusCode, TagKeyDestination}, + Aggregation: DefaulHTTPSizeDistribution, + } + + // HTTPClientRequestSizeView is an OpenCensus view that tracks HTTP client + //request size by pomerium service, destination, host and method + HTTPClientRequestSizeView = &view.View{ + Name: "http/client/response_size_bytes", + Measure: ochttp.ClientSentBytes, + Description: "HTTP Client Response Size in bytes", + TagKeys: []tag.Key{TagKeyService, TagKeyHost, TagKeyHTTPMethod, TagKeyDestination}, + Aggregation: DefaulHTTPSizeDistribution, + } +) + +// HTTPMetricsHandler creates a metrics middleware for incoming HTTP requests +func HTTPMetricsHandler(service string) func(next http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ctx, tagErr := tag.New( + r.Context(), + tag.Insert(TagKeyService, service), + tag.Insert(TagKeyHost, r.Host), + tag.Insert(TagKeyHTTPMethod, r.Method), + ) + if tagErr != nil { + log.Warn().Err(tagErr).Str("context", "HTTPMetricsHandler"). + Msg("telemetry/metrics: failed to create metrics tag") + next.ServeHTTP(w, r) + return + } + + ocHandler := ochttp.Handler{ + Handler: next, + FormatSpanName: func(r *http.Request) string { + return fmt.Sprintf("%s%s", r.Host, r.URL.Path) + }, + } + ocHandler.ServeHTTP(w, r.WithContext(ctx)) + }) + } +} + +// HTTPMetricsRoundTripper creates a metrics tracking tripper for outbound HTTP Requests +func HTTPMetricsRoundTripper(service string, destination string) func(next http.RoundTripper) http.RoundTripper { + return func(next http.RoundTripper) http.RoundTripper { + return tripper.RoundTripperFunc(func(r *http.Request) (*http.Response, error) { + ctx, tagErr := tag.New( + r.Context(), + tag.Insert(TagKeyService, service), + tag.Insert(TagKeyHost, r.Host), + tag.Insert(TagKeyHTTPMethod, r.Method), + tag.Insert(TagKeyDestination, destination), + ) + if tagErr != nil { + log.Warn().Err(tagErr).Str("context", "HTTPMetricsRoundTripper").Msg("telemetry/metrics: failed to create metrics tag") + return next.RoundTrip(r) + } + + ocTransport := ochttp.Transport{Base: next} + return ocTransport.RoundTrip(r.WithContext(ctx)) + }) + } +} diff --git a/internal/metrics/middleware_test.go b/internal/telemetry/metrics/http_test.go similarity index 91% rename from internal/metrics/middleware_test.go rename to internal/telemetry/metrics/http_test.go index e13e9eca9..050b74517 100644 --- a/internal/metrics/middleware_test.go +++ b/internal/telemetry/metrics/http_test.go @@ -1,4 +1,4 @@ -package metrics // import "github.com/pomerium/pomerium/internal/metrics" +package metrics // import "github.com/pomerium/pomerium/internal/telemetry/metrics" import ( "bytes" @@ -7,13 +7,40 @@ import ( "io/ioutil" "net/http" "net/http/httptest" + "strings" "testing" - "github.com/pomerium/pomerium/internal/middleware" "github.com/pomerium/pomerium/internal/tripper" "go.opencensus.io/stats/view" ) +func testDataRetrieval(v *view.View, t *testing.T, want string) { + if v == nil { + t.Fatalf("%s: nil view passed", t.Name()) + } + name := v.Name + data, err := view.RetrieveData(name) + + if err != nil { + t.Fatalf("%s: failed to retrieve data line %s", name, err) + } + + if want != "" && len(data) != 1 { + t.Fatalf("%s: received incorrect number of data rows: %d", name, len(data)) + } + if want == "" && len(data) > 0 { + t.Fatalf("%s: received incorrect number of data rows: %d", name, len(data)) + } else if want == "" { + return + } + + dataString := data[0].String() + + if want != "" && !strings.HasPrefix(dataString, want) { + t.Errorf("%s: Found unexpected data row: \nwant: %s\ngot: %s\n", name, want, dataString) + } +} + func newTestMux() http.Handler { mux := http.NewServeMux() mux.HandleFunc("/good", func(w http.ResponseWriter, r *http.Request) { @@ -25,10 +52,6 @@ func newTestMux() http.Handler { func Test_HTTPMetricsHandler(t *testing.T) { - chain := middleware.NewChain() - chain = chain.Append(HTTPMetricsHandler("test_service")) - chainHandler := chain.Then(newTestMux()) - tests := []struct { name string url string @@ -73,7 +96,9 @@ func Test_HTTPMetricsHandler(t *testing.T) { req := httptest.NewRequest(tt.verb, tt.url, new(bytes.Buffer)) rec := httptest.NewRecorder() - chainHandler.ServeHTTP(rec, req) + + h := HTTPMetricsHandler("test_service")(newTestMux()) + h.ServeHTTP(rec, req) testDataRetrieval(HTTPServerRequestSizeView, t, tt.wanthttpServerRequestSize) testDataRetrieval(HTTPServerResponseSizeView, t, tt.wanthttpServerResponseSize) diff --git a/internal/metrics/info.go b/internal/telemetry/metrics/info.go similarity index 75% rename from internal/metrics/info.go rename to internal/telemetry/metrics/info.go index d822e7482..ec1f8f151 100644 --- a/internal/metrics/info.go +++ b/internal/telemetry/metrics/info.go @@ -1,4 +1,4 @@ -package metrics // import "github.com/pomerium/pomerium/internal/metrics" +package metrics // import "github.com/pomerium/pomerium/internal/telemetry/metrics" import ( "context" @@ -8,6 +8,7 @@ import ( "github.com/pomerium/pomerium/internal/log" "github.com/pomerium/pomerium/internal/version" + "go.opencensus.io/metric" "go.opencensus.io/metric/metricdata" "go.opencensus.io/metric/metricproducer" @@ -17,44 +18,53 @@ import ( ) var ( - //buildInfo = stats.Int64("build_info", "Build Metadata", "1") - configLastReload = stats.Int64("config_last_reload_success_timestamp", "Timestamp of last successful config reload", "seconds") - configLastReloadSuccess = stats.Int64("config_last_reload_success", "Returns 1 if last reload was successful", "1") - registry = newMetricRegistry() + // InfoViews contains opencensus views for informational metrics about + // pomerium itself. + InfoViews = []*view.View{ConfigLastReloadView, ConfigLastReloadSuccessView} + + configLastReload = stats.Int64( + "config_last_reload_success_timestamp", + "Timestamp of last successful config reload", + "seconds") + configLastReloadSuccess = stats.Int64( + "config_last_reload_success", + "Returns 1 if last reload was successful", + "1") + registry = newMetricRegistry() // ConfigLastReloadView contains the timestamp the configuration was last - // reloaded, labeled by service + // reloaded, labeled by service. ConfigLastReloadView = &view.View{ Name: configLastReload.Name(), Description: configLastReload.Description(), Measure: configLastReload, - TagKeys: []tag.Key{keyService}, + TagKeys: []tag.Key{TagKeyService}, Aggregation: view.LastValue(), } // ConfigLastReloadSuccessView contains the result of the last configuration - // reload, labeled by service + // reload, labeled by service. ConfigLastReloadSuccessView = &view.View{ Name: configLastReloadSuccess.Name(), Description: configLastReloadSuccess.Description(), Measure: configLastReloadSuccess, - TagKeys: []tag.Key{keyService}, + TagKeys: []tag.Key{TagKeyService}, Aggregation: view.LastValue(), } ) -// SetConfigInfo records the status, checksum and timestamp of a configuration reload. You must register InfoViews or the related -// config views before calling +// SetConfigInfo records the status, checksum and timestamp of a configuration +// reload. You must register InfoViews or the related config views before calling func SetConfigInfo(service string, success bool, checksum string) { if success { - serviceTag := tag.Insert(keyService, service) + serviceTag := tag.Insert(TagKeyService, service) if err := stats.RecordWithTags( context.Background(), []tag.Mutator{serviceTag}, configLastReload.M(time.Now().Unix()), ); err != nil { - log.Error().Err(err).Msg("internal/metrics: failed to record config checksum timestamp") + log.Error().Err(err).Msg("internal/telemetry: failed to record config checksum timestamp") } if err := stats.RecordWithTags( @@ -62,7 +72,7 @@ func SetConfigInfo(service string, success bool, checksum string) { []tag.Mutator{serviceTag}, configLastReloadSuccess.M(1), ); err != nil { - log.Error().Err(err).Msg("internal/metrics: failed to record config reload") + log.Error().Err(err).Msg("internal/telemetry: failed to record config reload") } } else { stats.Record(context.Background(), configLastReloadSuccess.M(0)) @@ -96,7 +106,7 @@ func (r *metricRegistry) init() { metric.WithLabelKeys("service", "version", "revision", "goversion"), ) if err != nil { - log.Error().Err(err).Msg("internal/metrics: failed to register build info metric") + log.Error().Err(err).Msg("internal/telemetry: failed to register build info metric") } r.configChecksum, err = r.registry.AddFloat64Gauge("config_checksum_decimal", @@ -104,7 +114,7 @@ func (r *metricRegistry) init() { metric.WithLabelKeys("service"), ) if err != nil { - log.Error().Err(err).Msg("internal/metrics: failed to register config checksum metric") + log.Error().Err(err).Msg("internal/telemetry: failed to register config checksum metric") } r.policyCount, err = r.registry.AddInt64DerivedGauge("policy_count_total", @@ -112,7 +122,7 @@ func (r *metricRegistry) init() { metric.WithLabelKeys("service"), ) if err != nil { - log.Error().Err(err).Msg("internal/metrics: failed to register policy count metric") + log.Error().Err(err).Msg("internal/telemetry: failed to register policy count metric") } }) } @@ -130,7 +140,7 @@ func (r *metricRegistry) setBuildInfo(service string) { metricdata.NewLabelValue((runtime.Version())), ) if err != nil { - log.Error().Err(err).Msg("internal/metrics: failed to get build info metric") + log.Error().Err(err).Msg("internal/telemetry: failed to get build info metric") } // This sets our build_info metric to a constant 1 per @@ -155,7 +165,7 @@ func (r *metricRegistry) setConfigChecksum(service string, checksum uint64) { } m, err := r.configChecksum.GetEntry(metricdata.NewLabelValue(service)) if err != nil { - log.Error().Err(err).Msg("internal/metrics: failed to get config checksum metric") + log.Error().Err(err).Msg("internal/telemetry: failed to get config checksum metric") } m.Set(float64(checksum)) } @@ -172,7 +182,7 @@ func (r *metricRegistry) addPolicyCountCallback(service string, f func() int64) } err := r.policyCount.UpsertEntry(f, metricdata.NewLabelValue(service)) if err != nil { - log.Error().Err(err).Msg("internal/metrics: failed to get policy count metric") + log.Error().Err(err).Msg("internal/telemetry: failed to get policy count metric") } } diff --git a/internal/metrics/info_test.go b/internal/telemetry/metrics/info_test.go similarity index 95% rename from internal/metrics/info_test.go rename to internal/telemetry/metrics/info_test.go index c29285a49..efce2d0a4 100644 --- a/internal/metrics/info_test.go +++ b/internal/telemetry/metrics/info_test.go @@ -1,4 +1,4 @@ -package metrics // import "github.com/pomerium/pomerium/internal/metrics" +package metrics // import "github.com/pomerium/pomerium/internal/telemetry/metrics" import ( "runtime" @@ -8,6 +8,7 @@ import ( "go.opencensus.io/metric/metricdata" "go.opencensus.io/metric/metricproducer" + "go.opencensus.io/stats/view" ) func Test_SetConfigInfo(t *testing.T) { @@ -24,9 +25,8 @@ func Test_SetConfigInfo(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - UnRegisterView(InfoViews) - RegisterView(InfoViews) - + view.Unregister(InfoViews...) + view.Register(InfoViews...) SetConfigInfo("test_service", tt.success, tt.checksum) testDataRetrieval(ConfigLastReloadView, t, tt.wantLastReload) diff --git a/internal/telemetry/metrics/providers.go b/internal/telemetry/metrics/providers.go new file mode 100644 index 000000000..c5ed505df --- /dev/null +++ b/internal/telemetry/metrics/providers.go @@ -0,0 +1,39 @@ +package metrics // import "github.com/pomerium/pomerium/internal/telemetry/metrics" + +import ( + "fmt" + "net/http" + + ocprom "contrib.go.opencensus.io/exporter/prometheus" + prom "github.com/prometheus/client_golang/prometheus" + "go.opencensus.io/stats/view" +) + +// PrometheusHandler creates an exporter that exports stats to Prometheus +// and returns a handler suitable for exporting metrics. +func PrometheusHandler() (http.Handler, error) { + if err := registerDefaultViews(); err != nil { + return nil, fmt.Errorf("internal/telemetry: failed registering views") + } + reg := prom.DefaultRegisterer.(*prom.Registry) + exporter, err := ocprom.NewExporter( + ocprom.Options{ + Namespace: "pomerium", + Registry: reg, + }) + if err != nil { + return nil, fmt.Errorf("internal/telemetry: prometheus exporter: %v", err) + } + view.RegisterExporter(exporter) + mux := http.NewServeMux() + mux.Handle("/metrics", exporter) + return mux, nil +} + +func registerDefaultViews() error { + var views []*view.View + for _, v := range DefaultViews { + views = append(views, v...) + } + return view.Register(views...) +} diff --git a/internal/metrics/exporter_test.go b/internal/telemetry/metrics/providers_test.go similarity index 81% rename from internal/metrics/exporter_test.go rename to internal/telemetry/metrics/providers_test.go index 2d9b1566f..6c5e6ea1c 100644 --- a/internal/metrics/exporter_test.go +++ b/internal/telemetry/metrics/providers_test.go @@ -1,4 +1,4 @@ -package metrics // import "github.com/pomerium/pomerium/internal/metrics" +package metrics // import "github.com/pomerium/pomerium/internal/telemetry/metrics" import ( "bytes" @@ -8,9 +8,11 @@ import ( "testing" ) -func Test_newPromHTTPHandler(t *testing.T) { - h := newPromHTTPHandler() - +func Test_PrometheusHandler(t *testing.T) { + h, err := PrometheusHandler() + if err != nil { + t.Fatal(err) + } req := httptest.NewRequest("GET", "http://test.local/metrics", new(bytes.Buffer)) rec := httptest.NewRecorder() h.ServeHTTP(rec, req) diff --git a/internal/telemetry/trace/trace.go b/internal/telemetry/trace/trace.go new file mode 100644 index 000000000..d52a5fa79 --- /dev/null +++ b/internal/telemetry/trace/trace.go @@ -0,0 +1,74 @@ +package trace // import "github.com/pomerium/pomerium/internal/telemetry/trace" + +import ( + "context" + "fmt" + + "github.com/pomerium/pomerium/internal/log" + + "contrib.go.opencensus.io/exporter/jaeger" + "go.opencensus.io/trace" +) + +const ( + JaegerTracingProviderName = "jaeger" +) + +// TracingOptions contains the configurations settings for a http server. +type TracingOptions struct { + // Shared + Provider string + Service string + Debug bool + + // Jaeger + + // CollectorEndpoint is the full url to the Jaeger HTTP Thrift collector. + // For example, http://localhost:14268/api/traces + JaegerCollectorEndpoint string `mapstructure:"tracing_jaeger_collector_endpoint"` + // AgentEndpoint instructs exporter to send spans to jaeger-agent at this address. + // For example, localhost:6831. + JaegerAgentEndpoint string `mapstructure:"tracing_jaeger_agent_endpoint"` +} + +func RegisterTracing(opts *TracingOptions) error { + var err error + switch opts.Provider { + case JaegerTracingProviderName: + err = registerJaeger(opts) + default: + return fmt.Errorf("telemetry/trace: provider %s unknown", opts.Provider) + } + if err != nil { + return err + } + if opts.Debug { + log.Debug().Msg("telemetry/trace: debug on, sample everything") + trace.ApplyConfig(trace.Config{DefaultSampler: trace.AlwaysSample()}) + } + log.Debug().Interface("Opts", opts).Msg("telemetry/trace: exporter created") + return nil +} + +func registerJaeger(opts *TracingOptions) error { + jex, err := jaeger.NewExporter( + jaeger.Options{ + AgentEndpoint: opts.JaegerAgentEndpoint, + CollectorEndpoint: opts.JaegerCollectorEndpoint, + ServiceName: opts.Service, + }) + if err != nil { + return err + } + trace.RegisterExporter(jex) + return nil +} + +// StartSpan starts a new child span of the current span in the context. If +// there is no span in the context, creates a new trace and span. +// +// Returned context contains the newly created span. You can use it to +// propagate the returned span in process. +func StartSpan(ctx context.Context, name string, o ...trace.StartOption) (context.Context, *trace.Span) { + return trace.StartSpan(ctx, name, o...) +} diff --git a/internal/telemetry/trace/trace_test.go b/internal/telemetry/trace/trace_test.go new file mode 100644 index 000000000..745060104 --- /dev/null +++ b/internal/telemetry/trace/trace_test.go @@ -0,0 +1,23 @@ +package trace // import "github.com/pomerium/pomerium/internal/telemetry/trace" + +import "testing" + +func TestRegisterTracing(t *testing.T) { + tests := []struct { + name string + opts *TracingOptions + wantErr bool + }{ + {"jaeger", &TracingOptions{JaegerAgentEndpoint: "localhost:6831", Service: "all", Provider: "jaeger"}, false}, + {"jaeger with debug", &TracingOptions{JaegerAgentEndpoint: "localhost:6831", Service: "all", Provider: "jaeger", Debug: true}, false}, + {"jaeger no endpoint", &TracingOptions{JaegerAgentEndpoint: "", Service: "all", Provider: "jaeger"}, true}, + {"unknown provider", &TracingOptions{JaegerAgentEndpoint: "localhost:0", Service: "all", Provider: "Lucius Cornelius Sulla"}, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := RegisterTracing(tt.opts); (err != nil) != tt.wantErr { + t.Errorf("RegisterTracing() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/proxy/clients/authenticate_client.go b/proxy/clients/authenticate_client.go index a807b792a..572828526 100644 --- a/proxy/clients/authenticate_client.go +++ b/proxy/clients/authenticate_client.go @@ -3,12 +3,12 @@ package clients // import "github.com/pomerium/pomerium/proxy/clients" import ( "context" "errors" - "time" - - "google.golang.org/grpc" "github.com/pomerium/pomerium/internal/sessions" + "github.com/pomerium/pomerium/internal/telemetry/trace" pb "github.com/pomerium/pomerium/proto/authenticate" + + "google.golang.org/grpc" ) // Authenticator provides the authenticate service interface @@ -48,11 +48,12 @@ type AuthenticateGRPC struct { // Redeem makes an RPC call to the authenticate service to creates a session state // from an encrypted code provided as a result of an oauth2 callback process. func (a *AuthenticateGRPC) Redeem(ctx context.Context, code string) (*sessions.SessionState, error) { + ctx, span := trace.StartSpan(ctx, "proxy.client.grpc.Redeem") + defer span.End() + if code == "" { return nil, errors.New("missing code") } - ctx, cancel := context.WithTimeout(ctx, 5*time.Second) - defer cancel() protoSession, err := a.client.Authenticate(ctx, &pb.AuthenticateRequest{Code: code}) if err != nil { return nil, err @@ -68,6 +69,9 @@ func (a *AuthenticateGRPC) Redeem(ctx context.Context, code string) (*sessions.S // user's session. Requires a valid refresh token. Will return an error if the identity provider // has revoked the session or if the refresh token is no longer valid in this context. func (a *AuthenticateGRPC) Refresh(ctx context.Context, s *sessions.SessionState) (*sessions.SessionState, error) { + ctx, span := trace.StartSpan(ctx, "proxy.client.grpc.Refresh") + defer span.End() + if s.RefreshToken == "" { return nil, errors.New("missing refresh token") } @@ -75,14 +79,7 @@ func (a *AuthenticateGRPC) Refresh(ctx context.Context, s *sessions.SessionState if err != nil { return nil, err } - // todo(bdd): handle request id in grpc receiver and add to ctx logger - // reqID, ok := middleware.IDFromCtx(ctx) - // if ok { - // md := metadata.Pairs("req_id", reqID) - // ctx = metadata.NewOutgoingContext(ctx, md) - // } - ctx, cancel := context.WithTimeout(ctx, 5*time.Second) - defer cancel() + // todo(bdd): add grpc specific timeouts to main options // todo(bdd): handle request id (metadata!?) in grpc receiver and add to ctx logger reply, err := a.client.Refresh(ctx, req) @@ -100,18 +97,13 @@ func (a *AuthenticateGRPC) Refresh(ctx context.Context, s *sessions.SessionState // does NOT do nonce or revokation validation. // https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation func (a *AuthenticateGRPC) Validate(ctx context.Context, idToken string) (bool, error) { + ctx, span := trace.StartSpan(ctx, "proxy.client.grpc.Validate") + defer span.End() + if idToken == "" { return false, errors.New("missing id token") } - // todo(bdd): add grpc specific timeouts to main options - // todo(bdd): handle request id in grpc receiver and add to ctx logger - // reqID, ok := middleware.IDFromCtx(ctx) - // if ok { - // md := metadata.Pairs("req_id", reqID) - // ctx = metadata.NewOutgoingContext(ctx, md) - // } - ctx, cancel := context.WithTimeout(ctx, 5*time.Second) - defer cancel() + r, err := a.client.Validate(ctx, &pb.ValidateRequest{IdToken: idToken}) if err != nil { return false, err diff --git a/proxy/clients/authorize_client.go b/proxy/clients/authorize_client.go index e444f739f..229fa87b5 100644 --- a/proxy/clients/authorize_client.go +++ b/proxy/clients/authorize_client.go @@ -3,12 +3,12 @@ package clients // import "github.com/pomerium/pomerium/proxy/clients" import ( "context" "errors" - "time" - - "google.golang.org/grpc" "github.com/pomerium/pomerium/internal/sessions" + "github.com/pomerium/pomerium/internal/telemetry/trace" pb "github.com/pomerium/pomerium/proto/authorize" + + "google.golang.org/grpc" ) // Authorizer provides the authorize service interface @@ -47,11 +47,12 @@ type AuthorizeGRPC struct { // Authorize takes a route and user session and returns whether the // request is valid per access policy func (a *AuthorizeGRPC) Authorize(ctx context.Context, route string, s *sessions.SessionState) (bool, error) { + ctx, span := trace.StartSpan(ctx, "proxy.client.grpc.Authorize") + defer span.End() + if s == nil { return false, errors.New("session cannot be nil") } - ctx, cancel := context.WithTimeout(ctx, 5*time.Second) - defer cancel() response, err := a.client.Authorize(ctx, &pb.Identity{ Route: route, User: s.User, @@ -65,11 +66,12 @@ func (a *AuthorizeGRPC) Authorize(ctx context.Context, route string, s *sessions // IsAdmin takes a session and returns whether the user is an administrator func (a *AuthorizeGRPC) IsAdmin(ctx context.Context, s *sessions.SessionState) (bool, error) { + ctx, span := trace.StartSpan(ctx, "proxy.client.grpc.IsAdmin") + defer span.End() + if s == nil { return false, errors.New("session cannot be nil") } - ctx, cancel := context.WithTimeout(ctx, 5*time.Second) - defer cancel() response, err := a.client.IsAdmin(ctx, &pb.Identity{Email: s.Email, Groups: s.Groups}) return response.GetIsAdmin(), err } diff --git a/proxy/clients/clients.go b/proxy/clients/clients.go index 8f6d234fa..794e4bd08 100644 --- a/proxy/clients/clients.go +++ b/proxy/clients/clients.go @@ -11,8 +11,9 @@ import ( "strings" "github.com/pomerium/pomerium/internal/log" - "github.com/pomerium/pomerium/internal/metrics" "github.com/pomerium/pomerium/internal/middleware" + "github.com/pomerium/pomerium/internal/telemetry/metrics" + "go.opencensus.io/plugin/ocgrpc" "google.golang.org/grpc" "google.golang.org/grpc/credentials" diff --git a/proxy/proxy.go b/proxy/proxy.go index a4690c9b1..a7ba6c80f 100755 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -8,6 +8,7 @@ import ( "fmt" "html/template" stdlog "log" + "net" "net/http" "net/http/httputil" "net/url" @@ -16,9 +17,10 @@ import ( "github.com/pomerium/pomerium/internal/config" "github.com/pomerium/pomerium/internal/cryptutil" "github.com/pomerium/pomerium/internal/log" - "github.com/pomerium/pomerium/internal/metrics" "github.com/pomerium/pomerium/internal/middleware" "github.com/pomerium/pomerium/internal/sessions" + "github.com/pomerium/pomerium/internal/telemetry/metrics" + "github.com/pomerium/pomerium/internal/telemetry/trace" "github.com/pomerium/pomerium/internal/templates" "github.com/pomerium/pomerium/internal/tripper" "github.com/pomerium/pomerium/proxy/clients" @@ -196,11 +198,19 @@ func (p *Proxy) UpdatePolicies(opts *config.Options) error { } proxy := NewReverseProxy(policy.Destination) // build http transport (roundtripper) middleware chain - // todo(bdd): this will make vet complain, it is safe - // and can be replaced with transport.Clone() in go 1.13 - // https://go-review.googlesource.com/c/go/+/174597/ - // https://github.com/golang/go/issues/26013#issuecomment-399481302 - transport := *(http.DefaultTransport.(*http.Transport)) + // todo(bdd): replace with transport.Clone() in go 1.13 + transport := http.Transport{ + Proxy: http.ProxyFromEnvironment, + DialContext: (&net.Dialer{ + Timeout: 30 * time.Second, + KeepAlive: 30 * time.Second, + DualStack: true, + }).DialContext, + MaxIdleConns: 100, + IdleConnTimeout: 90 * time.Second, + TLSHandshakeTimeout: 10 * time.Second, + ExpectContinueTimeout: 1 * time.Second, + } c := tripper.NewChain() c = c.Append(metrics.HTTPMetricsRoundTripper("proxy", policy.Destination.Host)) if policy.TLSSkipVerify { @@ -236,7 +246,9 @@ type UpstreamProxy struct { // ServeHTTP handles the second (reverse-proxying) leg of pomerium's request flow func (u *UpstreamProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) { - u.handler.ServeHTTP(w, r) + ctx, span := trace.StartSpan(r.Context(), fmt.Sprintf("%s%s", r.Host, r.URL.Path)) + defer span.End() + u.handler.ServeHTTP(w, r.WithContext(ctx)) } // NewReverseProxy returns a new ReverseProxy that routes URLs to the scheme, host, and