Skip to content

Commit

Permalink
Enforce deny-by-default approach on the admin listener by matching on…
Browse files Browse the repository at this point in the history
… exact paths and on GET requests

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.

Some manual verifications:

```
curl http://localhost:9001/ready
LIVE
```

```
curl -vv --request POST http://localhost:9001/runtime
*   Trying [::1]:9001...
* Connected to localhost (::1) port 9001
> POST /runtime HTTP/1.1
> Host: localhost:9001
> User-Agent: curl/8.4.0
> Accept: */*
>
< HTTP/1.1 404 Not Found
< date: Wed, 15 May 2024 22:15:56 GMT
< server: envoy
< content-length: 0
<
* Connection #0 to host localhost left intact
```

```
                {
                 "match": {
                  "path": "/stats/prometheus",
                  "headers": [
                   {
                    "name": ":method",
                    "string_match": {
                     "exact": "GET",
                     "ignore_case": true
                    }
                   }
                  ]
                 },
```

and then:
```
curl -vv  'http://localhost:9001/stats?usedonly'
*   Trying [::1]:9001...
* Connected to localhost (::1) port 9001
> GET /stats?usedonly HTTP/1.1
> Host: localhost:9001
> User-Agent: curl/8.4.0
> Accept: */*
>
< HTTP/1.1 200 OK
```

Signed-off-by: Sotiris Nanopoulos <[email protected]>
  • Loading branch information
davinci26 committed May 17, 2024
1 parent 7530c06 commit 0817f87
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 9 deletions.
1 change: 1 addition & 0 deletions changelogs/unreleased/6447-davinci26-small.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Enforce `deny-by-default` approach on the `admin` listener by matching on exact paths and on `GET` requests
24 changes: 19 additions & 5 deletions internal/envoy/v3/stats.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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{{
Expand All @@ -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{
Expand Down
35 changes: 31 additions & 4 deletions internal/envoy/v3/stats_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand All @@ -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{
Expand All @@ -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{
Expand Down

0 comments on commit 0817f87

Please sign in to comment.