Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enforce deny-by-default approach on the admin listener by matching on exact paths and on GET requests #6447

Merged
merged 1 commit into from
May 17, 2024

Conversation

davinci26
Copy link
Contributor

@davinci26 davinci26 commented May 15, 2024

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

@davinci26 davinci26 requested a review from a team as a code owner May 15, 2024 23:28
@davinci26 davinci26 requested review from tsaarni, skriss, sunjayBhatia and clayton-gonsalves and removed request for a team, tsaarni and skriss May 15, 2024 23:28
@sunjayBhatia sunjayBhatia requested review from a team and izturn and removed request for a team May 15, 2024 23:28
@davinci26 davinci26 added the release-note/small A small change that needs one line of explanation in the release notes. label May 15, 2024
@davinci26
Copy link
Contributor Author

(will check CI a bit later)

Copy link

codecov bot commented May 15, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 81.61%. Comparing base (7530c06) to head (0817f87).

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #6447      +/-   ##
==========================================
+ Coverage   81.60%   81.61%   +0.01%     
==========================================
  Files         133      133              
  Lines       15842    15854      +12     
==========================================
+ Hits        12928    12940      +12     
  Misses       2620     2620              
  Partials      294      294              
Files Coverage Δ
internal/envoy/v3/stats.go 100.00% <100.00%> (ø)

Copy link
Member

@tsaarni tsaarni left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me, but a question: is there any reason not to change to path (exact) match instead of prefix?

@sunjayBhatia
Copy link
Member

Looks good to me, but a question: is there any reason not to change to path (exact) match instead of prefix?

yeah thats a good shout, esp. since we explicitly list out all the /stats/* paths we want to expose, an exact match should also work and lock things down even more:

"/stats",
"/stats/prometheus",
"/stats/recentlookups",

to be pedantic might just need to check that query params work fine with an exact match but I would expect that does just fine

@tsaarni
Copy link
Member

tsaarni commented May 16, 2024

to be pedantic might just need to check that query params work fine with an exact match but I would expect that does just fine

Yes, it seems it should since doc says

If specified, the route is an exact path rule meaning that the path must exactly match the :path header once the query string is removed

but good to double check by testing it as well 😅

@davinci26
Copy link
Contributor Author

davinci26 commented May 16, 2024

I thought it would be more future proof that way, in case there is an endpoint that supports both GET/POST so next time someone adds one we dont have to double check if it supports both GET and POST

@davinci26 davinci26 requested a review from tsaarni May 16, 2024 15:31
@davinci26
Copy link
Contributor Author

@sunjayBhatia @tsaarni I gave my reasoning but I will leave the final call to you. Just let me know what you prefer

@tsaarni
Copy link
Member

tsaarni commented May 16, 2024

I thought it would be more future proof that way, in case there is an endpoint that supports both GET/POST so next time someone adds one we dont have to double check if it supports both GET and POST

I'm not sure if I understood, but just to make sure there is no confusion: I also think that permitting only GET is good improvement. But it could be even safer to also change path matching from prefix to exact match to make sure we follow "deny-by-default" approach.

@davinci26
Copy link
Contributor Author

But it could be even safer to also change path matching from prefix to exact match to make sure we follow "deny-by-default" approach.

Now I got you :D I thought it was mutually exclusive, will update

@davinci26
Copy link
Contributor Author

🫡 updated

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

@davinci26 davinci26 changed the title Only allow GET requests to the admin listener Enforce deny-by-default approach on the admin listener by matching on exact paths and on GET requests May 17, 2024
Copy link
Member

@tsaarni tsaarni left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Thank you!

Copy link
Member

@skriss skriss left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One small comment, otherwise LGTM as well, thanks @davinci26!

internal/envoy/v3/stats.go Outdated Show resolved Hide resolved
… 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 <sotiris.nanopoulos@reddit.com>
Copy link
Member

@sunjayBhatia sunjayBhatia left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM also tested it out manually and looks good

@sunjayBhatia sunjayBhatia enabled auto-merge (squash) May 17, 2024 20:20
@sunjayBhatia sunjayBhatia merged commit 4d4f5db into projectcontour:main May 17, 2024
26 checks passed
SamMHD pushed a commit to SamMHD/contour that referenced this pull request Sep 8, 2024
… exact paths and on GET requests (projectcontour#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 <sotiris.nanopoulos@reddit.com>
Signed-off-by: Saman Mahdanian <saman@mahdanian.xyz>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
release-note/small A small change that needs one line of explanation in the release notes.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants