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 <bobbydesimone@gmail.com>
This commit is contained in:
Bobby DeSimone 2019-07-24 09:20:16 -07:00
parent 6b61a48fce
commit 5edfa7b03f
No known key found for this signature in database
GPG key ID: AEE4CF12FE86D07E
49 changed files with 1524 additions and 758 deletions

View file

@ -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

View file

@ -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
}

View file

@ -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"

View file

@ -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