Page MenuHomePhabricator

Test routing PCS through API Gateway
Closed, ResolvedPublic

Description

Background Information

In order to be able to test PCS responses without restbase, we need a sandbox with routing through API gateway to get an idea on how close we are to a response from restbase.

What

Split this work in two phases

  1. Internal Connections: add a routing URL that doesn't go through RESTBase
  2. Public Connection: final test with api gateway, e. g. https://api.wikimedia.org/api/test/[...]

Acceptance Criteria

  • Be able to request a PCS page in an internal request without passing through RESTBase - API Gateway as routing
  • Be able to request a PCS page in an public request in a sandbox environment without passing through RESTBase - API Gateway as routing

Related Objects

StatusSubtypeAssignedTask
Resolvedhnowlan
Resolvedhnowlan

Event Timeline

MSantos triaged this task as High priority.Feb 7 2023, 4:34 PM

Could you give me the desired query URL please for testing? I'm assuming this is being routed to mobileapps.discovery.wmnet, correct?

I think a good starting point to test PCS related endpoints is to setup the following routes for mobileapps:

  • <domain>/v1/page/mobile-html/<title>
  • <domain>/v1/page/mobile-html/<title>/<revision>
  • <domain>/v1/page/summary/<title>
  • <domain>/v1/page/summary/<title>/<revision>

The backend is mobileapps.discovery.wmnet.

The URL mapping that we use in RESTBase is the following:

  • For mobile-html
    • without revision specified
      • restbase:port/<domain>/v1/page/mobile-html/<title>
      • mobileapps:port/<domain>/v1/page/mobile-html/<title>
    • with revision specified
      • restbase:port/<domain>/v1/page/mobile-html/<title>
      • mobileapps:port/<domain>/v1/page/mobile-html/<title>
  • For summary
    • without revision specified
      • restbase:port/<domain>/v1/page/summary/<title>
      • mobileapps:port/<domain>/v1/page/summary/<title>
    • with revision specified
      • restbase:port/<domain>/v1/page/summary/<title>/<revision>
        • note: this is something not exposed currently in restbase but is a known feature that we never implemented because of restbase maintenance status
      • mobileapps:port/<domain>/v1/page/summary/<title>/<revision>

Change 895327 had a related patch set uploaded (by Hnowlan; author: Hnowlan):

[operations/deployment-charts@master] rest-gateway: add helmfile, enable mobileapps

https://gerrit.wikimedia.org/r/895327

Be able to request a PCS page in an internal request without passing through RESTBase - API Gateway as routing

In my mind, internal requests should go through the service mesh, not through the gateway. Did I get that wrong?

Change 904493 had a related patch set uploaded (by Hnowlan; author: Hnowlan):

[operations/dns@master] Add service records for rest-gateway

https://gerrit.wikimedia.org/r/904493

Change 904493 merged by Hnowlan:

[operations/dns@master] Add service records for rest-gateway

https://gerrit.wikimedia.org/r/904493

Change 895327 merged by jenkins-bot:

[operations/deployment-charts@master] rest-gateway: add helmfile, enable mobileapps

https://gerrit.wikimedia.org/r/895327

There is now an instance of the rest gateway routing mobileapps running in staging for testing. Mobileapps routes can be tested via something like curl https://staging.svc.eqiad.wmnet:4113/en.wikipedia.org/v1/page/mobile-html/Dublin.

@hnowlan is the API gateway overriding the response headers? or it's not forwarding headers to the services? I'm saying that because there's a difference in the headers for the requests, see a comparison with current production through restbase:

"中華民國國軍": {
       "added": {
           "x-envoy-upstream-service-time": "836"
       },
       "deleted": {},
       "updated": {
           "access-control-allow-origin": "*,*",
           "access-control-allow-headers": "accept, content-type, content-length, cache-control, accept-language, api-user-agent, if-match, if-modified-since, if-none-match, dnt, accept-encoding,accept, content-type, content-length, cache-control, accept-language, api-user-agent, if-match, if-modified-since, if-none-match, dnt, accept-encoding",
           "access-control-expose-headers": "etag,etag",
           "x-xss-protection": "1; mode=block, 1; mode=block",
           "x-content-type-options": "nosniff, nosniff",
           "x-frame-options": "SAMEORIGIN, SAMEORIGIN",
           "access-control-allow-methods": "GET,HEAD,GET,HEAD",
           "referrer-policy": "origin-when-cross-origin, origin-when-cross-origin"
       }
   }

This differences could be either API Gateway not forwarding the header 'x-restbase-compat': 'true' to PCS or overriding the headers. The difference doesn't happen requesting PCS directly.

@hnowlan is the API gateway overriding the response headers? or it's not forwarding headers to the services? I'm saying that because there's a difference in the headers for the requests, see a comparison with current production through restbase:

"中華民國國軍": {
       "added": {
           "x-envoy-upstream-service-time": "836"
       },
       "deleted": {},
       "updated": {
           "access-control-allow-origin": "*,*",
           "access-control-allow-headers": "accept, content-type, content-length, cache-control, accept-language, api-user-agent, if-match, if-modified-since, if-none-match, dnt, accept-encoding,accept, content-type, content-length, cache-control, accept-language, api-user-agent, if-match, if-modified-since, if-none-match, dnt, accept-encoding",
           "access-control-expose-headers": "etag,etag",
           "x-xss-protection": "1; mode=block, 1; mode=block",
           "x-content-type-options": "nosniff, nosniff",
           "x-frame-options": "SAMEORIGIN, SAMEORIGIN",
           "access-control-allow-methods": "GET,HEAD,GET,HEAD",
           "referrer-policy": "origin-when-cross-origin, origin-when-cross-origin"
       }
   }

This differences could be either API Gateway not forwarding the header 'x-restbase-compat': 'true' to PCS or overriding the headers. The difference doesn't happen requesting PCS directly.

Interesting - it appears the gateway is appending headers rather than replacing. Not ideal as it will require us to add this logic into our Lua layer, but this isn't a critical issue.

However, the gateway *shouldn't* be removing upstream headers. I see content-type being set correctly, and things like x-webkit-csp that we explicitly don't touch in the gateway.

So, how can I debug what is the incoming header in PCS when routing through the API Gateway? More specifically, does the API Gateway send anything in the payload that can make it possible to identify which requests came through API Gateway and the headers requested?

So, how can I debug what is the incoming header in PCS when routing through the API Gateway? More specifically, does the API Gateway send anything in the payload that can make it possible to identify which requests came through API Gateway and the headers requested?

Envoy will set x-envoy-internal when a request is being sent to a service. Here are the full headers sent/received via a simple curl:

2023-05-08 09:44:33.906][85][debug][http] [source/common/http/conn_manager_impl.cc:882] [C43097][S11737610104135090600] request headers complete (end_stream=true):
':authority', 'staging.svc.eqiad.wmnet:4113'
':path', '/en.wikipedia.org/v1/page/mobile-html/Duck'
':method', 'GET'
'user-agent', 'curl/7.64.0'
'accept', '*/*'

[2023-05-08 09:44:33.906][85][debug][http] [source/common/http/filter_manager.cc:779] [C43097][S11737610104135090600] request end stream
[2023-05-08 09:44:33.906][85][debug][router] [source/common/router/router.cc:445] [C43097][S11737610104135090600] cluster 'mobileapps_cluster' match for URL '/en.wikipedia.org/v1/page/mobile-html/Duck'

[2023-05-08 09:44:33.906][85][debug][router] [source/common/router/router.cc:631] [C43097][S11737610104135090600] router decoding headers:
':authority', 'mobileapps_cluster'
':path', '/en.wikipedia.org/v1/page/mobile-html/Duck'
':method', 'GET'
':scheme', 'https'
'user-agent', 'curl/7.64.0'
'accept', '*/*'
'x-forwarded-for', '10.2.2.69'
'x-forwarded-proto', 'https'
'x-envoy-internal', 'true'
'x-request-id', '2b9c3069-0c24-4153-a9bc-c70a0d02a1f3'
'x-envoy-expected-rq-timeout-ms', '15000'
'x-envoy-original-path', '/en.wikipedia.org/v1/page/mobile-html/Duck'


[2023-05-08 09:44:34.152][85][debug][http] [source/common/http/conn_manager_impl.cc:1469] [C43097][S11737610104135090600] encoding headers via codec (end_stream=false):
':status', '200'
'access-control-allow-origin', '*,*'
'access-control-allow-headers', 'accept, x-requested-with, content-type,accept, content-type, content-length, cache-control, accept-language, api-user-agent, if-match, if-modified-since, if-none-match, dnt, accept-encoding'
'access-control-expose-headers', 'etag,etag'
'x-xss-protection', '1; mode=block'
'x-content-type-options', 'nosniff'
'x-frame-options', 'SAMEORIGIN'
'x-content-security-policy', 'default-src 'none'; connect-src app://*.wikipedia.org https://*.wikipedia.org; media-src app://upload.wikimedia.org https://upload.wikimedia.org 'self'; img-src app://*.wikimedia.org https://*.wikimedia.org app://wikimedia.org https://wikimedia.org 'self' data:; object-src 'none'; script-src app://meta.wikimedia.org https://meta.wikimedia.org 'unsafe-inline'; style-src app://meta.wikimedia.org https://meta.wikimedia.org app://*.wikipedia.org https://*.wikipedia.org 'self' 'unsafe-inline'; frame-ancestors 'self'' 'x-webkit-csp', 'default-src 'none'; connect-src app://*.wikipedia.org https://*.wikipedia.org; media-src app://upload.wikimedia.org https://upload.wikimedia.org 'self'; img-src app://*.wikimedia.org https://*.wikimedia.org app://wikimedia.org https://wikimedia.org 'self' data:; object-src 'none'; script-src app://meta.wikimedia.org https://meta.wikimedia.org 'unsafe-inline'; style-src app://meta.wikimedia.org https://meta.wikimedia.org app://*.wikipedia.org https://*.wikipedia.org 'self' 'unsafe-inline'; frame-ancestors 'self''
'content-type', 'text/html; charset=utf-8; profile="https://www.mediawiki.org/wiki/Specs/Mobile-HTML/1.2.2"'
'etag', '"1136340390/f04d7ef0-ed84-11ed-9432-4d2f262a8f39"'
'content-language', 'en'
'content-length', '151307'
'vary', 'Accept-Encoding'
'date', 'Mon, 08 May 2023 09:44:34 GMT'
'x-envoy-upstream-service-time', '244'
'server', 'envoy'
'access-control-allow-methods', 'GET,HEAD'
'x-content-type-options', 'nosniff'
'x-frame-options', 'SAMEORIGIN'
'referrer-policy', 'origin-when-cross-origin'
'x-xss-protection', '1; mode=block'
'content-security-policy', 'default-src 'none'; connect-src app://*.wikipedia.org https://*.wikipedia.org; media-src app://upload.wikimedia.org https://upload.wikimedia.org 'self'; img-src app://*.wikimedia.org https://*.wikimedia.org app://wikimedia.org https://wikimedia.org 'self' data:; object-src 'none'; script-src app://meta.wikimedia.org https://meta.wikimedia.org 'unsafe-inline'; style-src app://meta.wikimedia.org https://meta.wikimedia.org app://*.wikipedia.org https://*.wikipedia.org 'self' 'unsafe-inline'; frame-ancestors 'self''
MSantos added a subscriber: VirginiaPoundstone.

Interesting - it appears the gateway is appending headers rather than replacing. Not ideal as it will require us to add this logic into our Lua layer, but this isn't a critical issue.

Thanks for helping me debug this. I can now confirm that everything works as expected and in fact, my diff tests were false positives because of the gateway appending the headers. Should we create a task for that and prioritize when necessary? cc/ @VirginiaPoundstone it's not a blocker but we should look into it in the future.

Saying that I will close the task, thanks @hnowlan!

Interesting - it appears the gateway is appending headers rather than replacing. Not ideal as it will require us to add this logic into our Lua layer, but this isn't a critical issue.

Thanks for helping me debug this. I can now confirm that everything works as expected and in fact, my diff tests were false positives because of the gateway appending the headers. Should we create a task for that and prioritize when necessary? cc/ @VirginiaPoundstone it's not a blocker but we should look into it in the future.

Saying that I will close the task, thanks @hnowlan!

There are two routes towards fixing this - ideally we can get Envoy to not append (which I am testing atm). If this is not available in the current version then we can accomplish it by expanding our CSP lua to iterate over a list of headers and defaults to set if they are absent.

Change 917340 had a related patch set uploaded (by Hnowlan; author: Hnowlan):

[operations/deployment-charts@master] rest-gateway: don't append when setting headers

https://gerrit.wikimedia.org/r/917340

Change 917340 merged by jenkins-bot:

[operations/deployment-charts@master] rest-gateway: don't append when setting headers

https://gerrit.wikimedia.org/r/917340

Header duplication issue is resolved.

However, I have noted that some CSP headers are different (and the deprecated headers can probably be removed anyway):

< x-content-security-policy: default-src 'none'; connect-src app://*.wikipedia.org https://*.wikipedia.org; media-src app://upload.wikimedia.org https://upload.wikimedia.org 'self'; img-src app://*.wikimedia.org https://*.wikimedia.org app://wikimedia.org https://wikimedia.org 'self' data:; object-src 'none'; script-src app://meta.wikimedia.org https://meta.wikimedia.org 'unsafe-inline'; style-src app://meta.wikimedia.org https://meta.wikimedia.org app://*.wikipedia.org https://*.wikipedia.org 'self' 'unsafe-inline'; frame-ancestors 'self'
< x-webkit-csp: default-src 'none'; connect-src app://*.wikipedia.org https://*.wikipedia.org; media-src app://upload.wikimedia.org https://upload.wikimedia.org 'self'; img-src app://*.wikimedia.org https://*.wikimedia.org app://wikimedia.org https://wikimedia.org 'self' data:; object-src 'none'; script-src app://meta.wikimedia.org https://meta.wikimedia.org 'unsafe-inline'; style-src app://meta.wikimedia.org https://meta.wikimedia.org app://*.wikipedia.org https://*.wikipedia.org 'self' 'unsafe-inline'; frame-ancestors 'self'
< content-security-policy: default-src 'none'; connect-src app://*.wikipedia.org https://*.wikipedia.org; media-src app://upload.wikimedia.org https://upload.wikimedia.org 'self'; img-src app://*.wikimedia.org https://*.wikimedia.org app://wikimedia.org https://wikimedia.org 'self' data:; object-src 'none'; script-src app://meta.wikimedia.org https://meta.wikimedia.org 'unsafe-inline'; style-src app://meta.wikimedia.org https://meta.wikimedia.org app://*.wikipedia.org https://*.wikipedia.org 'self' 'unsafe-inline'; frame-ancestors 'self'