Giter Site home page Giter Site logo

coupergateway / couper Goto Github PK

View Code? Open in Web Editor NEW
83.0 10.0 14.0 20.5 MB

Couper is a lightweight API gateway designed to support developers in building and operating API-driven Web projects

Home Page: https://couper.io

License: MIT License

Dockerfile 0.06% Makefile 0.07% Go 95.39% HTML 4.22% HCL 0.03% JavaScript 0.25%
gateway api spa http http-server server proxy go couper access-control

couper's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

couper's Issues

access_control 404 behaviour

If this block is protected and a sub-match for api base_path/... have no matching endpoint Couper returns with a 404 Not Found. This should be changed to access denied to prevent exposing available endpoints.

`req.json_body` result is an empty object for specific types

Describe the bug
The variable req.json_body does not return the expected outcome.

To Reproduce
Steps to reproduce the behavior:

  1. Which Couper version? Run couper version or docker run --entrypoint=/couper avenga/couper version
    master 2021-03-29 acfce46
  2. Provide your configuration file *.hcl. Remove sensitive data.
server "api" {
  api {
    base_path = "/api"

    endpoint "/req" {
      response {
#        body = json_encode(req.json_body)
        body = json_encode([1,2,3])
      }
    }
  }
}
  1. Provide a curl call for reproduction
    curl -s -H "Content-Type: application/json" -d "[1,2,3]" localhost:8080/api/req|jq

Expected behavior

  • json_encode([1,2,3]): got expected
 [
  1,
  2,
  3
]
  • json_encode(req.json_body) got empty object, expected array with 1, 2, 3
 {}

Additional context
This happens with boolean, number, string or array values.

Send WWW-Authenticate response header and appropriate status code for unauthorized bearer requests

The OAuth 2.0 Authorization Framework: Bearer Token Usage

(This is the RFC defining Bearer Authorization.)

Section "3. The WWW-Authenticate Response Header Field":

If the protected resource request does not include authentication credentials or does not contain an access token that enables access to the protected resource, the resource server MUST include the HTTP "WWW-Authenticate" response header field; it MAY include it in response to other conditions as well.

If the protected resource request included an access token and failed authentication, the resource server SHOULD include the "error" attribute to provide the client with the reason why the access request was declined. The parameter value is described in Section 3.1. In addition, the resource server MAY include the "error_description" attribute to provide developers a human-readable explanation that is not meant to be displayed to end-users. It also MAY include the "error_uri" attribute with an absolute URI identifying a human-readable web page explaining the error. The "error", "error_description", and "error_uri" attributes MUST NOT appear more than once.

Example:

     HTTP/1.1 401 Unauthorized
     WWW-Authenticate: Bearer realm="example",
                       error="invalid_token",
                       error_description="The access token expired"

Section "3.1. Error Codes":

invalid_token
The access token provided is expired, revoked, malformed, or invalid for other reasons. The resource SHOULD respond with the HTTP 401 (Unauthorized) status code. The client MAY request a new access token and retry the protected resource request.

Currently, the status code for a rejected request (lacking authrorization or with an invalid token) is 403 (Forbidden), which SHOULD be used for insufficient privileges:

insufficient_scope
The request requires higher privileges than provided by the access token. The resource server SHOULD respond with the HTTP 403 (Forbidden) status code and MAY include the "scope" attribute with the scope necessary to access the protected resource.

Add a http health check

A basic requirement is to provide a configurable health check status for our own server.

We have defined some health specific requirements:

  • has a configurable route which defaults to /healthz
  • check must work with http 1.0 and without host or xfh
  • path match only which allows queries too
  • mark health check within access log (searchable field)
  • send Cache-Control with no-store
  • handle shutdown signal and serve status 500 while the server is closing

introduce cli commands starting with "run"

currently, the couper binary is just the server. starting the binary ./couper starts the server.

in the future, we will most likely need more commands to be incorporated into the binary, e.g. couper validate.

for now, we should start the server with the sub command run:

$ couper run -f couper.hcl

Add query param to backend requests

My upstream systems needs an authorization token to be passed as a query parameter. Let's call it token=….

I configure an /upstream/** endpoint and route all requests to a backend configuration. As of now, all client requests hitting /usptream will be passed unchanged to the backend (except for the path being changed by /**).

The token is an authorization mechanism that I want to implement in couper. Maybe I will extract it from a cookie, an arbitrary header or a JWT token. Now I am looking for a way to add it as a query parameter on every upstream request.

Documentation: json-doc(path) must be relative to calling flow

json-doc() expects a path to a json file. If we use json-doc() in an xpath inside a file which has been called from another file, the json-doc path must not be relative to the current file but to the original caller, i.e.

conf/flow.xml:

<xslt in="$preResult" src="./transform/transform.xsl"/>

conf/transform/fields.json contains data we want to parse.

conf/transform/transform.xsl:

<xsl:variable name="fieldList" select="json-doc('./conf/transform/fields.json')"/>

As this behaviour might be misleading, it would be good to add an example or a hint in the documentation.

basic auth

We would like to support this kind of access control too.

A simple hcl block described in definitions and can be referenced via label.

basic_auth "my-ba" {
    # simple inline definition for demonstration
    user = "john.do"
    password = env.BASIC_AUTH_PASS

    # production setup: multiple users with hashed passwords:
    htpasswd_file = "/etc/htpasswd"
}
  • user and password are allowed to exist next to a htpasswd file reference
    • user & password configuration is optional and gets evaluated before htpasswd file.
  • user could be empty :password
  • crypto/subtle.ConstantTimeCompare() should be used for the equality check
  • send the WWW-Authenticate-Header alongside with a 401 status
  • htpasswd_file should be parsed by our own via scan per line and split(':')
  • assume that the htpasswd file was created with https://httpd.apache.org/docs/2.4/programs/htpasswd.html
    • which leads to support the following algorithms by today's security standards:
      • bcrypt
      • md5 by apache (APR1)
      • htpasswd files may contain a mixture of different encoding types of passwords

Tag docker image with release tag, latest points to latest release tag

Currently every merge to master releases this state to dockerhub latest. We will change this behaviour that the latest tag points to the latest release tag.

The github actions needs some configuration to tag and release as github release (tag).

We could provide an edge tag which points to the latest master revision.

access_control - error_handler

We want to introduce a new option for all kinds of access control to react to all or specific context errors. A common use-case for example is to redirect to a login route if a jwt token is expired. Currently Couper just throws this error and some frontend application must handle the redirect.

Specified as follows:

definitions {
  jwt "myJ" {
    error_handler "jwt_token_expired" "jwt_another_specific_one" {
      # redirect { ... } or
      response {
        status = 403
      }
    }
    # or a more generic fallback for all non listed jwt related cases
    error_handler {
      redirect {
        location = "/login"
      }
    }
  }

  #saml "sso" { ... }
}

Implementation details:

Since the access_control validates before the client-request will hit the final endpoint handler we must move the response object creation from there to the server level to intercept writing any client bytes before entering the error handling.
The error handling must be slightly refactored to fulfill this requirements in combination with the current "classic" error-file handling.

Specific error types could be a composition for grouping several cases and the access_control context.

OpenAPI: resolve servers: relative url with backend origin

The OpenAPI file may have defined servers with urls which could be absolute or relative. This leads to invalid requests for relative ones. We must add the current backend origin host to the relative OpenAPI server url.

Implementation: Since the backend origin gets evaluated during runtime the openAPI configuration must move to a runtime factory.

Add access to json body content via `req` and `beresp`

Accessing a bodies json payload is crucial for at least header enrichment in both directions. This will be a useful option for some new features later on.

The request (req) and response (beresp) evaluation needs to be extended with json_body.
Since we have to buffer and may rewind the body content for further processing a limit is a requirement:

request/response_body_limit or body_limit for both directions.

Buffering those bodies should be known in general on startup/config load. Detecting the json_body usage within a backend block. This enables some kind of lazy loading for json_body if used in config and with the http method matches POST PUT or PATCH and content-type application/json.

Support for an UUID option as request-id

Couper generates a unique request id with the help of https://github.com/rs/xid .
Some projects have a requirement of a real uuid which should be sent along with the request.
To provide a good log correlation, couper should be able to generate those via configuration.

The correct place for this would be the settings block within our couper configuration file.

settings {
  request_id_format = "uuid4"
}

Default should be common which would be the current behaviour with the xid package.
If the user configure the value uuid4 another generation method should be mapped to the uidFn Field of the HTTPServer struct.

Since all settings are mapped to config/runtime/HTTPConfig this one must be configurable via COUPER_REQUEST_ID_FORMAT and command flag -request-id-format.

Depends on PR #29 due to the settings block.

Pick a stable uuid package.

Misleading documentation

https://github.com/avenga/couper/tree/master/docs#the-api-block-

You can add more than one api block to a server block

This is wrong.

The label for server is documented as optional, but is required.

time="2020-10-08T07:05:18Z" level=fatal msg="Failed to load configuration bytes: loadBytes.hcl:1,8-9: Missing name for server; All server blocks must have 1 labels (name)." type=couper_daemon

The label for api, files and spa is documented as optional, but if it is set there is an error. This may be the case for other blocks too. I only tested these three.

time="2020-10-08T07:00:27Z" level=fatal msg="Failed to load configuration bytes: loadBytes.hcl:6,7-15: Extraneous label for spa; No labels are expected for spa blocks." type=couper_daemon

update overview.png

access control is currently displayed in front of endpoints, that's not correct as it can be set at various locations in the config. not sure if it's possible to update the image in order to be 100% correct. It might be ok to stick with the simplified version. TBD

Add support for more than one api block per server block

As in #46 mentioned the documentation differs from our implementation. Currently we support just one api block but there is no reason why we should not have multiple of them and is was planned anyways.

Add support for more than one api block within a server block. Configure the muxer accordingly and watch for clashes / conflicts and handle them.

Empty response with proxy gzip response

If the upstream backend answers with a gzip body the reverseproxy component throws a panic panic: net/http: abort Handler which is related to an error during body copy (client went away or writer closed).

  1. Empty response because the server crashed.
  2. Seems to be a bug in combination with our response writer wrapper und a gzip writer (close call?)

Couper Version: 0.6

Configuration:

server "zipzip" {
  endpoint "/**" {
    proxy {
      backend {
        path = "/en/"
        set_request_headers = {
          Accept-Encoding: "gzip"
        }
        origin = "https://wao.io/"
      }
    }
  }
}

Empty json array encodes to `null`.

Describe the bug
The array may evaluate to null and therefore encodes respectively to null.

To Reproduce
Steps to reproduce the behavior:

  1. Which Couper version? Run couper version or docker run --entrypoint=/couper avenga/couper version
    master 2021-03-29 acfce46
  2. Provide your configuration file *.hcl. Remove sensitive data.
server "custom-response" {
  endpoint "/**" {
    request {
      url = "https://httpbin.org/anything?a=${req.id}"
      body = json_encode({
        foo = []
        bar = ["bar"]
      })
    }
    response {
      body = json_encode(beresp.json_body.json)
    }
  }
}
  1. Provide a curl call for reproduction

curl -v http://localhost:8080/

Result:

$ http localhost
HTTP/1.1 200 OK
Connection: close
Content-Encoding: gzip
Content-Length: 47
Date: Thu, 18 Mar 2021 18:02:26 GMT
Server: couper.io
Vary: Accept-Encoding
{"bar": ["bar"], "foo": null }

Expected behavior

{"bar": ["bar"], "foo": []}

Path parameter

Supporting user defined path parameter in couper is crucial. An example would look like this:

endpoint "/my/{category}/{sub}/list" {
    backend {
        path = "/cms/${req.path_param.category}/order/${req.path_param.sub}/list"
        # or
        request_headers {
            x-cat: req.path_param.category
            x-sub-cat: req.path_param.sub
        }
    }
}

Since some other packages has already solved some related details we would like to use this pathpattern module. This could be handy in combination with our openapi validation poc #21 later on.

So the main task is to replace our current http multiplexer and routing which additionally will simplify some implementation details.

block `label` differs in implementation and documentation

In the documentation the label is optional for the server, api, spa and files block.

But the implementation differs. The label is required for server, and not allowed for api, spa and files. Other blocks may be also differ between implementation and documentation.

The implementation should make the label optional for all blocks, as it is in the documentation.

Provide a simple default configuration

Couper should serve a simple htdocs directory with a default index.html (welcome page) if the user provides no other configuration.

This enables at least some simple static serving with our docker image:

docker run -p 80:8080 -v `pwd`/public:/htdocs avenga/couper

For this task we should use and cleanup /public in our repository. /couper.hcl should be discarded.

Create a couper welcome page and basic couper hcl configuration and add /public to our Dockerfile.

Implement JWT signing profile and function

At least, JWT signing needs

  • a payload (a set of claims)
  • a signing algorithm
  • a signing key (secret or private key)

optionally

  • an expiry date, which ends up in the claims (exp) and may either be part of the provided claims or a separate parameter (e.g. as time-to-live ttl)
  • a key id, which ends up in the header section (kid) of the JWT (necessary for JWK); this may not be useful, if Couper is both JWT signer and signature validator
  • a media type (typ) header (e.g. at+jwt, see https://tools.ietf.org/html/draft-ietf-oauth-access-token-jwt-11#section-2.1)

The JWT signing function expects a reference to a JWT signing profile (with invariable parameters; see jwt_signing_profile "MyToken" {} below) and (optionally?) non-default claim:

jwt_sign(jwt_signing_profile_label, claims)

The first parameter jwt_signing_profile_label references a jwt_signing_profile {} block by its label (e.g. "MyToken").
The second parameter claims is an object with (non-default) claims (e.g. { sub: "12345" }).

A JWT signing profile specifies several parameters for JWT signing:

  jwt_signing_profile "MyToken" {
    # the algorithm to use for signing, REQUIRED
    signature_algorithm = "RS256"

    # the private key or secret to use for signing, REQUIRED; one of
    key_file = "/keys/user/priv.pem"
    # or
    key = "$3cRe4"

    # the time-to-live; translated into exp claim, REQUIRED
    ttl = "1d" # 0 meaning no expiration

    # the "default" claims to include in the JWT, optional
    # they are merged with claims provided as function parameter, which take precedence
    # they are evaluated at function call time
    #claims = req.ctx.get_from_somewhere.claims
    claims = {
      iss = "the_issuer"
      aud = req.query.youchoose
      iat = unixtime()
    }

    # later:
    # key id: id of the corresponding public key published in JWK, optional
    # kid = "foo12345bar" # or key_id, ...
    # ...
  }

This may have a corresponding JWT access control block (already implemented), e.g.:

  jwt "MyToken" {
    signature_algorithm = "RS256"
    key_file = "/keys/user/pub.pem"
    cookie = "UserToken"
    claims = {
      aud = "user"
    }
  }

Allow single string values for list attributes with only one element

In order to make the configuration more user friendly we should allow single string values for list attributes with only one element.

example (all three configurations should be valid):

hosts = "example.com"
hosts = ["example.com"]
hosts = ["example.com", "foo.bar"]

This should be implemented for:

  • hosts
  • access_control
  • paths
  • allowed_origins
  • and all other lists(!)
  • make sure implementation won't collide with configuration concepts
  • implementation
  • documentation

Free Endpoints!

endpoints are a means to configure a proxy connection to "mount" upstream services ("backend") to a local path. Currently, an endpoint can only be configured in the api section.

The idea of the api is to group endpoints or (client) requests that are meant to be consumed by applications (and probably follow strict schemas). Whereas the rest of the server (files…) is consumed by "browsers" - that is navigational requests and usual Web semantics. Put simply, it's general purpose Web traffic vs API calls.

There are use cases for general purpose traffic proxy configurations apart from api. For example, we could deploy a Docker container with the swagger UI to render our API docs. These endpoint should not be part of the api. Here, a "free endpoint" would make sense:

server "foo" {
  endpoint "/doc/**" {
    access_control = [ "JWTToken" ]
    backend {
       origin = "http://swaggerui:8080"
       path = "/**"
    }
  }
  //
}

Spec:

  • Free endpoints are muxed & matched after api routes, but before files and spa paths.
  • differences from API:
    • HTML errors (like for files) instead of JSON error documents (TODO: should we lift files.error_document to server?)
  • logging access and upstream requests as in api.endpoint (but handler:endpoint instead of handler:api)
  • backend should work as in api: anonymous "inline" backend with no label or refine a named backend or fallback to fallback backend that is defined in server
  • The last point adds a new requirement: one backend can be defined in server as the fallback backend. This applies to all free endpoints, and to api.endpoint (if no fallback backend is defined in api). The server.backend can be named (labelled) to allow refining in endpoints.

Example with fallback backend:

server "foo" {

  // fallback backend for free endpoints and API (may be overwritten by api.backend)
  // label is optional
  backend  {
     //
  }
  
  // uses default backend
  endpoint "/foo/**" {
  }
}

disable_certificate_validation does not work in definitions

My couper.hcl:

server "my-api" {
  api {
    endpoint "/**" {
      backend  = "foo"
    }
  }
}
definitions {
  backend "foo" {
    origin = "https://self-signed.badssl.com"
    disable_certificate_validation = true
  }
}

Although disable_certificate_validation is active, I get a request error:

$ http :8080/
…
{
    "error": {
        "code": 4002,
        "id": "c02mu9h8d3b4hja4foi0",
        "message": "API upstream connection error",
        "path": "/",
        "status": 502
    }
}

Couper's backend log says:

x509: certificate signed by unknown authority

However, the settings works, when defined in the endpoint:

server "my-api" {
  api {
    endpoint "/**" {
      backend {
        origin = "https://self-signed.badssl.com"
        disable_certificate_validation = true
      }
    }
  }
}

This config yields a 200, as expected.

access controls: provide more specific reason for request rejection

Currently there are only two visible reasons for a request rejected by JWT access control:

  • "Authorization required" with status 401
  • "Authorization failed" with status 403

Internally, there are several reasons for the rejection of a request, leading to "Authorization failed" with status 403:

  • an Authorization header is included, but its value lacks "bearer" (case-insensitively) (accesscontrol.ErrorBearerRequired)
  • the JWT is malformed (jwt.MalformedTokenError), e.g.
    • it does not consist of three parts separated by '.'
    • the JWT header is malformed
    • the JWT body is malformed
  • the token signature is invalid (jwt.InvalidSignatureError)
  • the token issuer (iss claim) has an unexpected value (jwt.InvalidIssuerError)
  • the expected token audience is not found in the aud claim (if present) (jwt.InvalidAudienceError)
  • the token lacks a required claim (jwt.InvalidClaimsError)
  • the token lacks a required claim, for which a specific value is expected
  • the token has a required claim, but its value is unexpected

It would be handy to have the more specific reason in the error response.

Implement SAML2 assertion access control

An access control to be used in a SAML2 assertion consumer service endpoint.

https://cheatsheetseries.owasp.org/cheatsheets/SAML_Security_Cheat_Sheet.html mentions some security issues and how to address them.

https://tools.ietf.org/html/rfc7522#section-3 (which describes a similar case) has some requirements for assertion format and processing requirements.

The following is a mix of requirements:

  • check RelayState param (= RelayState from AuthN request query param)
  • always perform schema validation on the XML document prior to using it for any security-­related purposes.
  • validate SAML2 response:
    • valid signature (from IdP metadata file (and samlp:Response/ds:Signature))
    • samlp:Response/@Destination (= ACS endpoint URL of SP)
    • samlp:Response/@InResponseTo (= samlp:AuthnRequest/@ID of the AuthN request)
    • samlp:Response/saml:Issuer (= entity_id of IdP, from IdP metadata file)
    • check samlp:Response/samlp:Status/samlp:StatusCode/@Value
  • validate incoming SAML2 assertion:
    • valid signature (from IdP metadata file (and saml:Assertion/ds:Signature))
    • saml:Assertion/saml:Issuer (= entity_id of IdP, from IdP metadata file)
    • saml:Assertion/saml:Conditions/saml:AudienceRestriction/saml:Audience (= entity_id of SP, from Couper config)
    • has saml:Assertion/saml:Subject/saml:Subject
    • saml:Assertion/saml:Conditions/@NotOnOrAfter or saml:Assertion/saml:Subject/saml:SubjectConfirmation/saml:SubjectConfirmationData/@NotOnOrAfter
      • saml:Assertion/saml:Conditions/@NotOnOrAfter: if time passed, reject Assertion
      • saml:Assertion/saml:Subject/saml:SubjectConfirmation/saml:SubjectConfirmationData/@NotOnOrAfter: if time passed, reject SubjectConfirmation
    • saml:Assertion/saml:Subject/saml:SubjectConfirmation/@Method (= 'urn:oasis:names:tc:SAML:2.0:cm:bearer')
    • saml:Assertion/saml:Subject/saml:SubjectConfirmation/saml:SubjectConfirmationData/@Recipient (= ACS endpoint URL of SP)
    • saml:Assertion/saml:Subject/saml:SubjectConfirmation/saml:SubjectConfirmationData/@InResponseTo (= samlp:AuthnRequest/@ID of the AuthN request)
    • check saml:Assertion/saml:AuthnStatement/saml:AuthnContext/saml:AuthnContextClassRef
    • if invalid: create HTML error response, because the request is a front-channel request
  • transform attributes about the authenticated user into claims, which may be used elsewhere in JWT creation
  saml2 "SAML_AC" {
    # the XML metadata about the identity provider
    idp_metadata_file = "idp-metadata.xml"
    # the entity id of the service provider (couper)
    entity_id = "my-couper-api"

    # single sign on service (idp)
    sso_binding = "HTTP-Redirect" # irrelevant for access control?
    # assertion consumer service (couper)
    acs_binding = "HTTP-Post"
    # alternative: binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"

    # whether to generate JSON claims
    generate_json_claims = true
    # which `saml:Attribute` elements are expected to possibly have multiple `saml:AttributeValue` element children
    array_attributes = ["memberOf"]
    
    # generates:
    #claims = {
    #  iss = "https://the-id-provider.com…" # saml:Assertion/saml:Issuer
    #  sub = "username" # saml:Assertion/saml:Subject/saml:NameID
    #  exp = 1612371650 # saml:Assertion/saml:Conditions/@NotOnOrAfter -> unixtime
    #  nbf = 1612300000 # saml:Assertion/saml:Conditions/@NotBefore -> unixtime
    #  iat = 1612300010 # saml:Assertion/@IssueInstant -> unixtime
    #  aud = "my-couper-api" # saml:Assertion/saml:Conditions/saml:AudienceRestriction/saml:Audience
    #  # loop saml:Assertion/saml:AttributeStatement/saml:Attribute -> @Name = ./saml:AttributeValue
    #  displayName = "Doe, John (External)"
    #  objectSid = "S-1-5-21-123456789-0123456789-123456789-000001"
    #  mail = "[email protected]"
    #  # Attribute with multiple AttributeValue's become arrays?
    #  memberOf = [
    #    "admin",
    #    "users",
    #    "CN=Special AD Group,OU=foo,DC=example,DC=com"
    #  ]
    #}
  }

BasicAuth should send www-authenticate header /w missing creds

Resources which have an access_control type basic_auth are not reachable via browser login-form triggered via WWW-Authenticate header.

The current result:

curl -i http://localhost:8080
HTTP/1.1 403 Forbidden
Content-Type: text/html
Couper-Error: 5001 - "Authorization failed"
Server: couper.io

expected:

curl -i http://localhost:8080
HTTP/1.1 401 Unauthorized
Content-Type: text/html
Couper-Error: 5002 - "Unauthorized"
Server: couper.io
Www-Authenticate: Basic

auto reload config when config file changes

When used in kubernetes with a config file mounted from a config map,
it would be best to have cooper autodetect changes in a/the config file and auto reload it.

Also: Make a boot param to enable/disable this feature.

OAuth 2.0 retry on 401 resource request

We would like to enhance our flow and retry the resource request if the response status is 401 with a new token request and replay the resource request.

Log enrichment to mark this as retry.

Ensure Content-Length header instead of TE: chunked

Since we know the length of the given body bytes for each direction we should ensure sending the Content-Length header instead of sending those bytes with Transfer-Encoding: chunked.

Depends on the io.Reader. StringReader for example triggers an implicit set...

Security check for Upstream Request Validation

If the OpenAPI3 description of an upstream API mentions security requirements (security) for routes, Couper should perform security checks when validating requests to this upstream API (according to the referenced security scheme objects in components.securitySchemes):

  • type: http
    • scheme: basic: header "Authorization: Basic ..." set?
    • scheme: bearer: header "Authorization: Bearer ..." set?
  • type: apiKey
    • in: query; name: <query_param>: query parameter <query_param> set?
    • in: header; name: <header_name>: header <header_name> set?
    • in: cookie; name: <cookie_name>: cookie <cookie_name> set?
  • type: oauth2/openIdConnect: header "Authorization: Bearer ..." set?

If security references several security scheme objects, the requirements must be ANDed or ORed accordingly.

This additional check should be configurable (default is ontrue; performed only if upstream request validation is on).

server and api labels

We want to change some label requirements for a cleaner configuration and useful log enrichment.

As follows:
The server label is optional. If set: must be unique across the configuration file.
The api block could have an optional label. If set: must be unique per server.

Configured labels must be logged.

Missing params to null

Currently, if I define my origin like origin = "http://${req.path_param.origin}" and the {origin} is not defined in the path of my endpoint, couper panics.

We should convert missing params to null. The same goes for JsonBody, Query, Post, etc.

Support for nested struct environment mapping

Currently we are able to map environment variables to configuration structs via tag.

An example:

type Config struct {
  DefaultPort int `env:"default_port"`
}

This gets internally prefixed with COUPER_ and uppercased. The value of COUPER_DEFAULT_PORT will be assigned to Config.DefaultPort.

The following example is not possible at this moment but should:

type Config struct {
  DefaultPort int `env:"default_port"`
  Timings Timings
}

Type Timings struct {
  Timeout time.Duration `env:"timeout"`
}

Env COUPER_TIMEOUT must be assigned to Config.Timings.Timeout.

Related files are config/env/env.go and runtime/http.go makes use of it.

Websocket support is broken

Due to some features we have wrapped the http.ResponseWriter which is missing a http.Hijacker interface implementation now. This interface must be implemented and requires a test to verify the UpgradeResponse (Status 101) is working again.

Configuration variables

We have to document our already implemented and registered variables for our request/response evaluation.

req is the client request.
bereq is the modified backend request.
beresp is the original backend response.
resp would be the modified client response. Not implemented.

Most fields are self explanatory:

  • req:
    • id - unique request id
    • method http method
    • path url path
    • endpoint matched endpoint pattern
    • headers.<name> http header value for requested key
    • cookies.<name> http cookie value for requested key (last wins)
    • query.<name> query parameter values (last wins)
    • post.<name> params - not implemented
  • bereq:
    • almost the same as req except endpoint does not exist
    • url backend origin url
  • beresp:
    • status http status code
    • headers|cookies<name> value from set-cookie-header (last wins)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.