From 4d4f5db1c644f011b62f96aad6961eec1dae2f49 Mon Sep 17 00:00:00 2001 From: Sotiris Nanopoulos Date: Fri, 17 May 2024 16:32:40 -0400 Subject: [PATCH] Enforce deny-by-default approach on the admin listener by matching on exact paths and on GET requests (#6447) We want to block all requests to the `admin` listener endpoint that are not `GET`. In particular someone know can do `/runtime_modify?key1=value1&key2=value2&keyN=valueN` to change runtime variables such as the max regexp program size This is mostly for security reasons to prevent any potential attacks that could happen by an attacker modifying the `runtime` configuration of Envoy (or any other configuration). Note that since `shutdownmanager.go` uses the admin socket `/admin/admin.sock` to send a `POST` request it should be unaffected by this change. Signed-off-by: Sotiris Nanopoulos --- changelogs/unreleased/6447-davinci26-small.md | 1 + internal/envoy/v3/stats.go | 24 ++++++++++--- internal/envoy/v3/stats_test.go | 35 ++++++++++++++++--- 3 files changed, 51 insertions(+), 9 deletions(-) create mode 100644 changelogs/unreleased/6447-davinci26-small.md diff --git a/changelogs/unreleased/6447-davinci26-small.md b/changelogs/unreleased/6447-davinci26-small.md new file mode 100644 index 00000000000..168d0474ad7 --- /dev/null +++ b/changelogs/unreleased/6447-davinci26-small.md @@ -0,0 +1 @@ +Enforce `deny-by-default` approach on the `admin` listener by matching on exact paths and on `GET` requests diff --git a/internal/envoy/v3/stats.go b/internal/envoy/v3/stats.go index 21cc7b57c1f..76aca3e2dfb 100644 --- a/internal/envoy/v3/stats.go +++ b/internal/envoy/v3/stats.go @@ -20,6 +20,7 @@ import ( envoy_filter_http_router_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/router/v3" envoy_filter_network_http_connection_manager_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" envoy_transport_socket_tls_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" + envoy_matcher_v3 "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" "github.com/envoyproxy/go-control-plane/pkg/wellknown" "google.golang.org/protobuf/types/known/wrapperspb" @@ -133,8 +134,8 @@ func filterChain(statsPrefix string, transportSocket *envoy_config_core_v3.Trans }} } -// routeForAdminInterface creates static RouteConfig that forwards requested prefixes to Envoy admin interface. -func routeForAdminInterface(prefixes ...string) *envoy_filter_network_http_connection_manager_v3.HttpConnectionManager_RouteConfig { +// routeForAdminInterface creates static RouteConfig that forwards requested paths to Envoy admin interface. +func routeForAdminInterface(paths ...string) *envoy_filter_network_http_connection_manager_v3.HttpConnectionManager_RouteConfig { config := &envoy_filter_network_http_connection_manager_v3.HttpConnectionManager_RouteConfig{ RouteConfig: &envoy_config_route_v3.RouteConfiguration{ VirtualHosts: []*envoy_config_route_v3.VirtualHost{{ @@ -144,12 +145,25 @@ func routeForAdminInterface(prefixes ...string) *envoy_filter_network_http_conne }, } - for _, prefix := range prefixes { + for _, p := range paths { config.RouteConfig.VirtualHosts[0].Routes = append(config.RouteConfig.VirtualHosts[0].Routes, &envoy_config_route_v3.Route{ Match: &envoy_config_route_v3.RouteMatch{ - PathSpecifier: &envoy_config_route_v3.RouteMatch_Prefix{ - Prefix: prefix, + PathSpecifier: &envoy_config_route_v3.RouteMatch_Path{ + Path: p, + }, + Headers: []*envoy_config_route_v3.HeaderMatcher{ + { + Name: ":method", + HeaderMatchSpecifier: &envoy_config_route_v3.HeaderMatcher_StringMatch{ + StringMatch: &envoy_matcher_v3.StringMatcher{ + IgnoreCase: true, + MatchPattern: &envoy_matcher_v3.StringMatcher_Exact{ + Exact: "GET", + }, + }, + }, + }, }, }, Action: &envoy_config_route_v3.Route_Route{ diff --git a/internal/envoy/v3/stats_test.go b/internal/envoy/v3/stats_test.go index dc10c34808f..00a86086ae4 100644 --- a/internal/envoy/v3/stats_test.go +++ b/internal/envoy/v3/stats_test.go @@ -22,6 +22,7 @@ import ( envoy_filter_http_router_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/router/v3" envoy_filter_network_http_connection_manager_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" envoy_transport_socket_tls_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" + envoy_matcher_v3 "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" "github.com/envoyproxy/go-control-plane/pkg/wellknown" "google.golang.org/protobuf/types/known/wrapperspb" @@ -32,8 +33,21 @@ import ( func TestStatsListeners(t *testing.T) { readyRoute := &envoy_config_route_v3.Route{ Match: &envoy_config_route_v3.RouteMatch{ - PathSpecifier: &envoy_config_route_v3.RouteMatch_Prefix{ - Prefix: "/ready", + PathSpecifier: &envoy_config_route_v3.RouteMatch_Path{ + Path: "/ready", + }, + Headers: []*envoy_config_route_v3.HeaderMatcher{ + { + Name: ":method", + HeaderMatchSpecifier: &envoy_config_route_v3.HeaderMatcher_StringMatch{ + StringMatch: &envoy_matcher_v3.StringMatcher{ + IgnoreCase: true, + MatchPattern: &envoy_matcher_v3.StringMatcher_Exact{ + Exact: "GET", + }, + }, + }, + }, }, }, Action: &envoy_config_route_v3.Route_Route{ @@ -47,8 +61,21 @@ func TestStatsListeners(t *testing.T) { statsRoute := &envoy_config_route_v3.Route{ Match: &envoy_config_route_v3.RouteMatch{ - PathSpecifier: &envoy_config_route_v3.RouteMatch_Prefix{ - Prefix: "/stats", + PathSpecifier: &envoy_config_route_v3.RouteMatch_Path{ + Path: "/stats", + }, + Headers: []*envoy_config_route_v3.HeaderMatcher{ + { + Name: ":method", + HeaderMatchSpecifier: &envoy_config_route_v3.HeaderMatcher_StringMatch{ + StringMatch: &envoy_matcher_v3.StringMatcher{ + IgnoreCase: true, + MatchPattern: &envoy_matcher_v3.StringMatcher_Exact{ + Exact: "GET", + }, + }, + }, + }, }, }, Action: &envoy_config_route_v3.Route_Route{