CVE-2022-24797 (Go, published 2022-03-31T23:15Z)
Description: value: Pomerium is an identity-aware access proxy. In distributed service mode, Pomerium's Authenticate service exposes pprof debug and prometheus metrics handlers to untrusted traffic. This can leak potentially sensitive environmental information or lead to limited denial of service conditions. This issue is patched in version v0.17.1 Workarounds: Block access to `/debug` and `/metrics` paths on the authenticate service. This can be done with any L7 proxy, including Pomerium's own proxy service.
Severity: MEDIUM (CVSS v3 base score: 9.1)
Weakness: NVD-CWE-Other \u2014 Other
NVD is only using a subset of CWE for mapping instead of the entire CWE, and the weakness type is not covered by that subset.
Repository: https://github.com/pomerium/pomerium
Files changed:
config/config.go
config/config_source.go
config/envoyconfig/bootstrap_test.go
config/envoyconfig/builder.go
config/envoyconfig/clusters.go
config/envoyconfig/clusters_test.go
config/envoyconfig/listeners.go
config/envoyconfig/listeners_test.go
config/envoyconfig/routes.go
config/envoyconfig/routes_test.go
config/metrics.go
config/options.go
internal/cmd/pomerium/pomerium.go
internal/controlplane/http.go
internal/controlplane/server.go
internal/registry/reporter.go
Commit message: authenticate: fix debug and metrics endpoints (#3212)



Vulnerable code:
	ports, err := netutil.AllocatePorts(3)
	b := New("local-grpc", "local-http", filemgr.NewManager(), nil)
	b := New("localhost:1111", "localhost:2222", filemgr.NewManager(), nil)
		b := New("localhost:1111", "localhost:2222", filemgr.NewManager(), nil)
		b := New("xyz:zyx", "localhost:2222", filemgr.NewManager(), nil)
	b := New("local-grpc", "local-http", filemgr.NewManager(), nil)
	localGRPCAddress string
	localHTTPAddress string
	filemgr          *filemgr.Manager
	reproxy          *reproxy.Handler
		localGRPCAddress: localGRPCAddress,
		localHTTPAddress: localHTTPAddress,
		filemgr:          fileManager,
		reproxy:          reproxyHandler,
	b := New("local-grpc", "local-http", filemgr.NewManager(), nil)
	b := New("local-grpc", "local-http", filemgr.NewManager(), nil)
	b := New("local-grpc", "local-http", filemgr.NewManager(), nil)
						Cluster: "pomerium-control-plane-http",
	b := New("local-grpc", "local-http", filemgr.NewManager(), nil)
								"cluster": "pomerium-control-plane-http"
	b := New("local-grpc", "local-http", nil, nil)
	b := New("local-grpc", "local-http", filemgr.NewManager(), nil)
	b := New("local-grpc", "local-http", nil, nil)
	b := New("local-grpc", "local-http", nil, nil)
		// enable ext_authz
		r, err := b.buildControlPlanePathRoute("/.pomerium/jwt", true)
		if err != nil {
			return nil, err
		}
		routes = append(routes, r)

		// disable ext_authz and passthrough to proxy handlers
		r, err = b.buildControlPlanePathRoute("/ping", false)
		if err != nil {
			return nil, err
		}
		routes = append(routes, r)
		r, err = b.buildControlPlanePathRoute("/healthz", false)
		if err != nil {
			return nil, err
		}
		routes = append(routes, r)
		r, err = b.buildControlPlanePathRoute("/.pomerium", false)
		if err != nil {
			return nil, err
		}
		routes = append(routes, r)
		r, err = b.buildControlPlanePrefixRoute("/.pomerium/", false)
		if err != nil {
			return nil, err
		}
		routes = append(routes, r)
		r, err = b.buildControlPlanePathRoute("/.well-known/pomerium", false)
		if err != nil {
			return nil, err
		}
		routes = append(routes, r)
		r, err = b.buildControlPlanePrefixRoute("/.well-known/pomerium/", false)
		if err != nil {
			return nil, err
		}
		routes = append(routes, r)
			r, err := b.buildControlPlanePathRoute("/robots.txt", false)
			if err != nil {
				return nil, err
			}
			routes = append(routes, r)
		r, err := b.buildControlPlanePrefixRoute("/", false)
		if err != nil {
			return nil, err
		}
		routes = append(routes, r)
func (b *Builder) buildControlPlanePathRoute(path string, protected bool) (*envoy_config_route_v3.Route, error) {
	return r, nil
func (b *Builder) buildControlPlanePrefixRoute(prefix string, protected bool) (*envoy_config_route_v3.Route, error) {
	return r, nil
			`+routeString("prefix", "/", false)+`
	route, err := b.buildControlPlanePathRoute("/hello/world", false)
	require.NoError(t, err)
	route, err := b.buildControlPlanePrefixRoute("/hello/world/", false)
	require.NoError(t, err)
	mu             sync.RWMutex
	installationID string
	serviceName    string
	addr           string
	basicAuth      string
	handler        http.Handler
	mgr.updateInfo(cfg)
	mgr.updateServer(cfg)
func (mgr *MetricsManager) updateInfo(cfg *Config) {
		log.Error(context.TODO()).Err(err).Msg("telemetry/metrics: failed to get OS hostname")
func (mgr *MetricsManager) updateServer(cfg *Config) {
		cfg.Options.InstallationID == mgr.installationID {
		log.Info(context.TODO()).Msg("metrics: http server disabled")
	handler, err := metrics.PrometheusHandler(EnvoyAdminURL, mgr.installationID)
		log.Error(context.TODO()).Err(err).Msg("metrics: failed to create prometheus handler")
// EnvoyAdminURL indicates where the envoy control plane is listening
var EnvoyAdminURL = &url.URL{Host: "127.0.0.1:9901", Scheme: "http"}

	root.Path("/debug/pprof/cmdline").HandlerFunc(pprof.Cmdline)
	root.Path("/debug/pprof/profile").HandlerFunc(pprof.Profile)
	root.Path("/debug/pprof/symbol").HandlerFunc(pprof.Symbol)
	root.Path("/debug/pprof/trace").HandlerFunc(pprof.Trace)
	root.PathPrefix("/debug/pprof/").HandlerFunc(pprof.Index)
	root.Handle("/metrics", srv.metricsMgr)
	GRPCListener net.Listener
	GRPCServer   *grpc.Server
	HTTPListener net.Listener
	HTTPRouter   *mux.Router
	Builder      *envoyconfig.Builder
	hsrv := (&http.Server{
		BaseContext: func(li net.Listener) context.Context {
			return ctx
		},
		Handler: srv.HTTPRouter,
	})
	// start the HTTP server
	eg.Go(func() error {
		log.Info(ctx).Str("addr", srv.HTTPListener.Addr().String()).Msg("starting control-plane HTTP server")
		return hsrv.Serve(srv.HTTPListener)
	})
	// gracefully stop the HTTP server on context cancellation
	eg.Go(func() error {
		<-ctx.Done()
		ctx, cleanup := context.WithTimeout(ctx, time.Second*5)
		defer cleanup()
		return hsrv.Shutdown(ctx)
	})

Fixed code:
	// MetricsPort is the port the metrics listener is running on.
	MetricsPort string
	// DebugPort is the port the debug listener is running on.
	DebugPort string
		MetricsPort:  cfg.MetricsPort,
		DebugPort:    cfg.DebugPort,
	ports, err := netutil.AllocatePorts(5)
	metricsPort := ports[3]
	debugPort := ports[4]
		MetricsPort:  metricsPort,
		DebugPort:    debugPort,
	b := New("local-grpc", "local-http", "local-metrics", filemgr.NewManager(), nil)
	b := New("localhost:1111", "localhost:2222", "localhost:3333", filemgr.NewManager(), nil)
		b := New("localhost:1111", "localhost:2222", "localhost:3333", filemgr.NewManager(), nil)
		b := New("xyz:zyx", "localhost:2222", "localhost:3333", filemgr.NewManager(), nil)
	b := New("local-grpc", "local-http", "local-metrics", filemgr.NewManager(), nil)
	localGRPCAddress    string
	localHTTPAddress    string
	localMetricsAddress string
	filemgr             *filemgr.Manager
	reproxy             *reproxy.Handler
	localMetricsAddress string,
		localGRPCAddress:    localGRPCAddress,
		localHTTPAddress:    localHTTPAddress,
		localMetricsAddress: localMetricsAddress,
		filemgr:             fileManager,
		reproxy:             reproxyHandler,
	metricsURL := &url.URL{
		Scheme: "http",
		Host:   b.localMetricsAddress,
	}
	controlMetrics, err := b.buildInternalCluster(ctx, cfg.Options, "pomerium-control-plane-metrics", []*url.URL{metricsURL}, upstreamProtocolAuto)
	if err != nil {
		return nil, err
	}

		controlMetrics,
	b := New("local-grpc", "local-http", "local-metrics", filemgr.NewManager(), nil)
	b := New("local-grpc", "local-http", "local-metrics", filemgr.NewManager(), nil)
	b := New("local-grpc", "local-http", "local-metrics", filemgr.NewManager(), nil)
						Cluster: "pomerium-control-plane-metrics",
	b := New("local-grpc", "local-http", "local-metrics", filemgr.NewManager(), nil)
								"cluster": "pomerium-control-plane-metrics"
	b := New("local-grpc", "local-http", "local-metrics", nil, nil)
	b := New("local-grpc", "local-http", "local-metrics", filemgr.NewManager(), nil)
	b := New("local-grpc", "local-http", "local-metrics", nil, nil)
	b := New("local-grpc", "local-http", "local-metrics", nil, nil)
		routes = append(routes,
			// enable ext_authz
			b.buildControlPlanePathRoute("/.pomerium/jwt", true),
			// disable ext_authz and passthrough to proxy handlers
			b.buildControlPlanePathRoute("/ping", false),
			b.buildControlPlanePathRoute("/healthz", false),
			b.buildControlPlanePathRoute("/.pomerium", false),
			b.buildControlPlanePrefixRoute("/.pomerium/", false),
			b.buildControlPlanePathRoute("/.well-known/pomerium", false),
			b.buildControlPlanePrefixRoute("/.well-known/pomerium/", false),
		)
			routes = append(routes, b.buildControlPlanePathRoute("/robots.txt", false))
		routes = append(routes,
			b.buildControlPlanePathRoute(options.AuthenticateCallbackPath, false),
			b.buildControlPlanePathRoute("/", false),
		)
func (b *Builder) buildControlPlanePathRoute(path string, protected bool) *envoy_config_route_v3.Route {
	return r
func (b *Builder) buildControlPlanePrefixRoute(prefix string, protected bool) *envoy_config_route_v3.Route {
	return r
			`+routeString("path", "/oauth2/callback", false)+`,
			`+routeString("path", "/", false)+`
	route := b.buildControlPlanePathRoute("/hello/world", false)
	route := b.buildControlPlanePrefixRoute("/hello/world/", false)
	"net/url"
	mu                sync.RWMutex
	installationID    string
	serviceName       string
	addr              string
	basicAuth         string
	envoyAdminAddress string
	handler           http.Handler
	mgr.updateInfo(ctx, cfg)
	mgr.updateServer(ctx, cfg)
func (mgr *MetricsManager) updateInfo(ctx context.Context, cfg *Config) {
		log.Error(ctx).Err(err).Msg("telemetry/metrics: failed to get OS hostname")
func (mgr *MetricsManager) updateServer(ctx context.Context, cfg *Config) {
		cfg.Options.InstallationID == mgr.installationID &&
		cfg.Options.EnvoyAdminAddress == mgr.envoyAdminAddress {
	mgr.envoyAdminAddress = cfg.Options.EnvoyAdminAddress
		log.Info(ctx).Msg("metrics: http server disabled")
	envoyURL, err := url.Parse("http://" + cfg.Options.EnvoyAdminAddress)
		log.Error(ctx).Err(err).Msg("metrics: invalid envoy admin address, disabling")
		return
	}

	handler, err := metrics.PrometheusHandler(envoyURL, mgr.installationID)
	if err != nil {
		log.Error(ctx).Err(err).Msg("metrics: failed to create prometheus handler")
		Str("metrics-port", src.GetConfig().MetricsPort).
		Str("debug-port", src.GetConfig().DebugPort).
	srv.DebugRouter.Path("/debug/pprof/cmdline").HandlerFunc(pprof.Cmdline)
	srv.DebugRouter.Path("/debug/pprof/profile").HandlerFunc(pprof.Profile)
	srv.DebugRouter.Path("/debug/pprof/symbol").HandlerFunc(pprof.Symbol)
	srv.DebugRouter.Path("/debug/pprof/trace").HandlerFunc(pprof.Trace)
	srv.DebugRouter.PathPrefix("/debug/pprof/").HandlerFunc(pprof.Index)
	srv.MetricsRouter.Handle("/metrics", srv.metricsMgr)
	GRPCListener    net.Listener
	GRPCServer      *grpc.Server
	HTTPListener    net.Listener
	HTTPRouter      *mux.Router
	MetricsListener net.Listener
	MetricsRouter   *mux.Router
	DebugListener   net.Listener
	DebugRouter     *mux.Router
	Builder         *envoyconfig.Builder

	srv.MetricsListener, err = net.Listen("tcp4", net.JoinHostPort("127.0.0.1", cfg.MetricsPort))
	if err != nil {
		_ = srv.GRPCListener.Close()
		_ = srv.HTTPListener.Close()
		return nil, err
	}

	srv.DebugListener, err = net.Listen("tcp4", net.JoinHostPort("127.0.0.1", cfg.DebugPort))
	if err != nil {
		_ = srv.GRPCListener.Close()
		_ = srv.HTTPListener.Close()
		_ = srv.DebugListener.Close()
		return nil, err
	}

	srv.DebugRouter = mux.NewRouter()
	srv.MetricsRouter = mux.NewRouter()
		srv.MetricsListener.Addr().String(),
	for _, entry := range []struct {
		Name     string
		Listener net.Listener
		Handler  *mux.Router
	}{
		{"http", srv.HTTPListener, srv.HTTPRouter},
		{"debug", srv.DebugListener, srv.DebugRouter},
		{"metrics", srv.MetricsListener, srv.MetricsRouter},
	} {
		entry := entry
		hsrv := (&http.Server{
			BaseContext: func(li net.Listener) context.Context {
				return ctx
			},
			Handler: entry.Handler,
		})
		// start the HTTP server
		eg.Go(func() error {
			log.Info(ctx).
				Str("addr", entry.Listener.Addr().String()).
				Msgf("starting control-plane %s server", entry.Name)
			return hsrv.Serve(entry.Listener)
		})
		// gracefully stop the HTTP server on context cancellation
		eg.Go(func() error {
			<-ctx.Done()
			ctx, cleanup := context.WithTimeout(ctx, time.Second*5)
			defer cleanup()
			return hsrv.Shutdown(ctx)
		})
	}
	if cfg.Options.MetricsAddr == "" {
		return nil, nil
	}