Uploaded image for project: 'Red Hat 3scale API Management'
  1. Red Hat 3scale API Management
  2. THREESCALE-3489

Fix policies injected for backend APIs to proxy without that routing path

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Done
    • Icon: Blocker Blocker
    • None
    • None
    • System
    • None
    • 3
    • Not Started
    • Not Started
    • Not Started
    • Not Started
    • Not Started
    • Not Started
    • 3scale 2019-09-09

      The current routing of Backend APIs in a product is carrying out the path of the backend API config while proxying the request to the backend URL.

      For example, consider a product using 2 backend APIs (path -> private URL):

      / -> http://host1/aaa
      /other-api -> http://host2/bbb
      

      A request to <public-url>/x will be correctly proxied to http://host1/aaa/x, while a request to <public-url>/other-api/x will be incorrectly proxied to http://host2/bbb/other-api/x.

      This would obligate the developer of the second backend API (whose for this product is /other-api) to implement other-api/ at the backend API level, which violates the purposes of each layer product and API.

      The so called violation becomes even clearer if we consider a second product that happens to use the same backend API with a different routing path. The developer now needs to implement two base paths that nothing have to do with the API itself, but only to product decisions.

      In order to solve this problem, we need to make sure the routing paths are used for the match in the gateway but stripped out before proxying the request.

      Dev notes

      Solution 1 (it only involves system)

      1. Append to the tail of the policy chain one URL rewriting policy that strips out the routing path of backend APIs. This policy needs to be injected after the apicast policy, otherwise mapping rules defined at the level of backend APIs will not match.
        {
          "name": "url_rewriting",
          "version": "builtin",
          "configuration": {
            "commands": [
              { "op": "sub", "regex": "<todo: regex to remove only the routing path>", "replace": "/" }
            ]
          }
        }
        
      2. Include a liquid_value property to each routing policy injected for the backend APIs to make sure the proxy match continues to be based on the original URI and not on the modified URL after rewriting.
        "liquid_value": "{{ original_request.uri }}",
        

      Example
      A policy chain that currently looks like the following for a product containing 2 backend APIs

      "policy_chain": [
        {
          "name": "routing",
          "version": "builtin",
          "enabled": true,
          "configuration": {
            "rules": [
              {
                "url": "http://svatky.adresa.info:80/json",
                "condition": {
                  "operations": [
                    {
                      "match": "path",
                      "op": "matches",
                      "value": "/secret/.*|/secret/?"
                    }
                  ]
                }
              },
              {
                "url": "https://echo-api.3scale.net:443",
                "condition": {
                  "operations": [
                    {
                      "match": "path",
                      "op": "matches",
                      "value": "/.*"
                    }
                  ]
                }
              }
            ]
          }
        },
        {
          "name": "apicast",
          "version": "builtin",
          "configuration": {}
        }
      ]
      

      would change to

      "policy_chain": [
        {
          "name": "routing",
          "version": "builtin",
          "enabled": true,
          "configuration": {
            "rules": [
              {
                "url": "http://svatky.adresa.info:80/json",
                "condition": {
                  "operations": [
                    {
                      "match": "path",
                      "liquid_value": "{{ original_request.uri }}",
                      "op": "matches",
                      "value": "/secret/.*|/secret/?"
                    }
                  ]
                }
              },
              {
                "url": "https://echo-api.3scale.net:443",
                "condition": {
                  "operations": [
                    {
                      "match": "path",
                      "liquid_value": "{{ original_request.uri }}",
                      "op": "matches",
                      "value": "/.*"
                    }
                  ]
                }
              }
            ]
          }
        },
        {
          "name": "apicast",
          "version": "builtin",
          "configuration": {}
        },
        {
          "name": "url_rewriting",
          "version": "builtin",
          "configuration": {
            "commands": [
              {
                "op": "sub",
                "regex": "<regex>",
                "replace": "/"
              }
            ]
          }
        }
      ]
      

      Solution 2 (requires changes in apicast, but cleaner)

      1. Include a replace_path property to each routing policy injected for the backend APIs that contains a path, holding the instructions to how apicast should strip the path part from the URL.
        "replace_path": "{{ original_url.scheme }}://{{ original_url.host }}{{original_url.path | replace: \"/the-path\", \"/\"}}",
        

      Example
      The same policy chain of the previous example would in this case change to

      "policy_chain": [
        {
          "name": "routing",
          "version": "builtin",
          "enabled": true,
          "configuration": {
            "rules": [
              {
                "url": "http://svatky.adresa.info:80/json",
                "condition": {
                  "operations": [
                    {
                      "match": "path",
                      "op": "matches",
                      "value": "/secret/.*|/secret/?"
                    }
                  ]
                },
                "replace_path": "{{ original_url.scheme }}://{{ original_url.host }}{{original_url.path | replace: \"/secret\", \"/json\"}}"
              },
              {
                "url": "https://echo-api.3scale.net:443",
                "condition": {
                  "operations": [
                    {
                      "match": "path",
                      "liquid_value": "{{ original_request.uri }}",
                      "op": "matches",
                      "value": "/.*"
                    }
                  ]
                }
              }
            ]
          }
        },
        {
          "name": "apicast",
          "version": "builtin",
          "configuration": {}
        }
      ]
      

            Unassigned Unassigned
            mcassola Guilherme Cassolato
            Guilherme Cassolato Guilherme Cassolato
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

              Created:
              Updated:
              Resolved: