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

Overview

Couper

Go Go Report Docker

Couper

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

Getting started

Features

Couper …

  • is a proxy component connecting clients with (micro) services
  • adds access control and observability to the project
  • needs no special development skills
  • is easy to configure & integrate
  • runs on Linux, Mac OS X, Windows, Docker and Kubernetes.

Key features are:

  • Easy Configuration & Deployment
  • HTTP Request Routing / Forwarding
  • Custom Requests and Responses
  • Request / Response Manipulation
  • Centralized Access-Control Layer:
    • Basic-Auth
    • JWT Validation & Signing
    • Single Sign On with SAML2
    • OAuth2 Client Credentials
  • Configurable Service Connectivity
  • Upstream Validation & CORS
  • SPA & Web Serving
  • Error Handling
  • Observability

The full list of features of Couper 1.x is here or at couper.io.

Developers

Developers requiring Go to start with make build. Couper requires a configuration file. You can start with a simple one and use:

./couper run -f public/couper.hcl

Contributing

Thanks for your interest in contributing.

If you have any questions or feedback you are welcome to start a discussion.

If you have an issue please open an issue.

Comments
  • check and log ulimits on startup

    check and log ulimits on startup

    We would like to check the current ulimit for our Couper process. Especially in docker environments this information could be very helpful.

    Check and log the current and max system settings. If the number is below a constant threshold, lets start with 4096, log as warn level.

    Implementation could take place within the run command.

    hacktoberfest 
    opened by malud 9
  • OpenAPI upstream request/response validation

    OpenAPI upstream request/response validation

    We want to prevent invalid upstream request and responses which does not match the requirements from a given openAPI yaml file.

    • [x] implementation
    • [x] documentation
    enhancement spec theme/config 
    opened by johakoch 8
  • Implement SAML2 assertion access control

    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"
        #  ]
        #}
      }
    
    opened by johakoch 7
  • Upgrade to Go 1.17

    Upgrade to Go 1.17

    Upgrade to go 1.17

    • update go.mod
    • update Dockerfile
    • update GitHub actions
    • fix new go vet errors

    resolves #330


    Reviewer checklist
    • Read PR description: a summary about the changes is required
    • Changelog updated
    • Documentation: docs/{Reference, Cli, ...}, Docker and cli help/usage
    • Pulled branch, manually tested
    • Verified requirements are met
    • Reviewed the code
    • Reviewed the related tests
    opened by develerik 4
  • Explicitly pass non-json upstream response body `backend_responses.<label>.body`

    Explicitly pass non-json upstream response body `backend_responses.

    When I define an upstream request with a label (other than default), I have to define a response{}, too. Because otherwise Couper wouldn't know what to pass downstream.

    It is straight-forward to define response status and headers. But currently I cannot simply copy the response body:

    server "pass" {
      endpoint "/teapot" {
        request "st" {
          url = "https://httpbin.org/status/418"
        }
        response {
          status = backend_responses.st.status
          headers = backend_responses.st.headers
          body = backend_responses.st.body
        }
      }
    }
    

    … because backend_responses.<label>.body doesn't exist :)

    We should provide the de-chunked, de-compressed bytes of the response body in that property. E.g. to allow for useless string expressions like

     "Body was: ${backend_responses.st.body}"
    

    The string must also be binary-safe to pass arbitrary content, e.g. pass a PDF generated by an upstream service. (And a binary-string containing a null byte shouldn't truncate the value :))

    At first, it's not about optimizing this kind of operation. We can buffer the download, pull the bytes it through the expression evaluation and then compress them on the way to the client.

    enhancement 
    opened by filex 4
  • Log parent-field should wrap all logs

    Log parent-field should wrap all logs

    Couper offers two settings to wrap JSON log lines into a parent field. This is useful to avoid field clashes in the log db (e.g. ElasticSearch / ELK). I recently had that situation in a project running in Kubernetes where logs were supposed to ship via Logstash to a ES.

    I have used these env vars:

    COUPER_ACCESS_LOG_PARENT_FIELD=couper
    COUPER_BACKEND_LOG_PARENT_FIELD=couper
    

    As one would expect, this works for Couper's access log and backend log.

    However, there are more log "types" that Couper uses. For example those with type":"couper_daemon. Those lines are used for startup messages and some generic errors that don't belong elsewhere.

    I propose that we introduce a single setting that configures a wrapper object for all couper logs:

    COUPER_LOG_PARENT_FIELD=couper
    

    This way I

    • don't loose any less obvious loglines
    • have to write less env var settings
    bug 
    opened by filex 3
  • config: add coalesce() function

    config: add coalesce() function

    Currently, hcl does not support a null coalesce operator for default values. Until then, we can use the coalesce() function from cty's stdlib.

    This would allow us to easily define default values:

    user = coalesce(env.USER, "default")
    
    opened by filex 3
  • Support for nested struct environment mapping

    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.

    enhancement good first issue theme/config 
    opened by malud 3
  • Inject server data to a SPA

    Inject server data to a SPA

    There are some concepts how to bring e.g. environment related configurations to a Single Page Application. One of them is to replace a __SERVER_DATA__ placeholder at the apps index.html while serving it to the client.

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <script>
          window.SERVER_DATA = __SERVER_DATA__;
        </script>
      </head>
    </html>
    

    Which could be configured like this:

    spa {
        bootstrap_file = "/htdocs/index.html"
        paths = ["/**"]
        # replaces __SERVER_DATA__ in bootstrap_file (marshalled+cached on startup)
        server_data = {
            "apiUrl": env.API_URL
            "idp": {
              "clientId": env.IDP_CLIENT_ID,
              "idpUrl": env.IDP_URL
          }
        }
      }
    

    To prevent a XSS Vulnerability we have to html-escape the content. (https://pkg.go.dev/encoding/json#HTMLEscape) Related article: https://medium.com/node-security/the-most-common-xss-vulnerability-in-react-js-applications-2bdffbcc1fa0

    enhancement spec proposal 
    opened by malud 2
  • Multiple error handler labels

    Multiple error handler labels

    config.ErrorHandler with only one Kind; configload.kindContent with only one kind; avoid double iteration


    Reviewer checklist
    • Read PR description: a summary about the changes is required
    • Changelog updated
    • Documentation: docs/{Reference, Cli, ...}, Docker and cli help/usage
    • Pulled branch, manually tested
    • Verified requirements are met
    • Reviewed the code
    • Reviewed the related tests
    opened by johakoch 2
  • OAuth2

    OAuth2 "password flow"

    OAuth2 password flow as a hidden feature

            backend {
              oauth2 {
                grant_type = "password"
                token_endpoint = "http://localhost:8081/token"
                client_id = env.OAUTH_CLIENT_ID
                client_secret = env.OAUTH_CLIENT_SECRET
                username = env.OAUTH_USERNAME
                password = env.OAUTH_PASSWORD
              }
            }
    

    WARNING: This is no proper implementation of the OAuth2 password flow, as the received access token is stored for the client (like with the client credentials flow) and not per user. However, this is a solution for the very limited use case where the client requests a token on behalf of exactly one (system) user without any user interaction.


    Reviewer checklist
    • Read PR description: a summary about the changes is required
    • Changelog updated
    • Documentation: docs/{Reference, Cli, ...}, Docker and cli help/usage
    • Pulled branch, manually tested
    • Verified requirements are met
    • Reviewed the code
    • Reviewed the related tests
    opened by johakoch 2
  • Endpoint wildcard proxy wildcard

    Endpoint wildcard proxy wildcard

    Fixed endpoint wildcard/proxy wildcard combination where wildcard matches "":

      endpoint "/**" {
        proxy {
          backend {
            origin = "http://localhost:8081"
            path = "/**"
          }
        }
      }
    
      endpoint "/a/**" {
        proxy {
          backend {
            origin = "http://localhost:8081"
            path = "/b/**"
          }
        }
      }
    

    before:

    • request GET / -> backend request GET /**
    • request GET /a -> backend request GET /b/**

    now:

    • request GET / -> backend request GET /
    • request GET /a -> backend request GET /b

    BTW, without

            path = "/**"
    

    in the first endpoint, the backend request path was ok.

    Non-empty-matches were ok, too:

    • request GET /c -> backend request GET /c
    • request GET /a/c -> backend request GET /b/c

    Reviewer checklist
    • Read PR description: a summary about the changes is required
    • Changelog updated
    • Documentation: docs/{Reference, Cli, ...}, Docker and cli help/usage
    • Pulled branch, manually tested
    • Verified requirements are met
    • Reviewed the code
    • Reviewed the related tests
    opened by johakoch 0
  • cases where client_secret is not required

    cases where client_secret is not required

    Docu: added case where client_secret is not required

    see https://github.com/avenga/couper/blob/master/oauth2/client.go#L77


    Reviewer checklist
    • Read PR description: a summary about the changes is required
    • Changelog updated
    • Documentation: docs/{Reference, Cli, ...}, Docker and cli help/usage
    • Pulled branch, manually tested
    • Verified requirements are met
    • Reviewed the code
    • Reviewed the related tests
    opened by johakoch 0
  • Provide and extend `Server-Timing` header

    Provide and extend `Server-Timing` header

    It would be a great addition if Couper provides the backend timings additionally to the existing log timings as Server-Timing header. Extending an existing Server-Timing header while chaining multiple gateways is also a valid use-case.

    enhancement 
    opened by malud 1
  • Missing referenced blocks

    Missing referenced blocks

    Harmonized handling of references to undefined blocks


    Reviewer checklist
    • Read PR description: a summary about the changes is required
    • Changelog updated
    • Documentation: docs/{Reference, Cli, ...}, Docker and cli help/usage
    • Pulled branch, manually tested
    • Verified requirements are met
    • Reviewed the code
    • Reviewed the related tests
    opened by johakoch 0
  • token introspection

    token introspection

    JWT: call token introspection endpoint


    Reviewer checklist
    • Read PR description: a summary about the changes is required
    • Changelog updated
    • Documentation: docs/{Reference, Cli, ...}, Docker and cli help/usage
    • Pulled branch, manually tested
    • Verified requirements are met
    • Reviewed the code
    • Reviewed the related tests
    opened by johakoch 1
  • Use `backend_request` in request header modifier

    Use `backend_request` in request header modifier

    OAuth2 DPoP currently (draft 11) requires an OAuth2-DPoP client, when approaching an authorization or resource server, to sign a JWT with, among others, an htu claim containing the request URL ("without query and fragment parts") and send this JWT in a DPoP request header field.

    A first try to configure this in couper HCL:

      backend "as" {
        origin = "https://authorization.server"
        add_request_headers = {
          DPoP = jwt_sign(
            "dpop",
            {
              htu = backend_request.url
            }
          )
        }
      }
    

    However, backend_request is not available in the request phase, as it is set together with backend_response after the backend response is received.

    For this use case to work, we could

    • at least, set backend_request earlier, i.e. prior to applying the request header modifiers,
    • possibly, change the order in which request header, query and form param modifiers are applied (e.g. 1) query, 2) request headers, 3) form params) with documenting the order,
    • additionally, set backend_request prior to, in between and after the three modification steps.
    proposal 
    opened by johakoch 0
Releases(v1.11.1)
lightweight, idiomatic and composable router for building Go HTTP services

chi is a lightweight, idiomatic and composable router for building Go HTTP services. It's especially good at helping you write large REST API services

go-chi 13k Jan 6, 2023
⚡ Rux is an simple and fast web framework. support middleware, compatible http.Handler interface. 简单且快速的 Go web 框架,支持中间件,兼容 http.Handler 接口

Rux Simple and fast web framework for build golang HTTP applications. NOTICE: v1.3.x is not fully compatible with v1.2.x version Fast route match, sup

Gookit 84 Dec 8, 2022
Golanger Web Framework is a lightweight framework for writing web applications in Go.

/* Copyright 2013 Golanger.com. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except

golanger 298 Nov 14, 2022
A Go framework for building JSON web services inspired by Dropwizard

Tiger Tonic A Go framework for building JSON web services inspired by Dropwizard. If HTML is your game, this will hurt a little. Like the Go language

Richard Crowley 999 Dec 9, 2022
package for building REST-style Web Services using Go

go-restful package for building REST-style Web Services using Google Go Code examples using v3 REST asks developers to use HTTP methods explicitly and

Ernest Micklei 4.7k Jan 1, 2023
Simple and lightweight Go web framework inspired by koa

VOX A golang web framework for humans, inspired by Koa heavily. Getting started Installation Using the go get power: $ go get -u github.com/aisk/vox B

An Long 79 Dec 14, 2022
Dragon 🐲 🐲 🐲 is a lightweight high performance web framework with Go for the feature and comfortable develop.

Dragon project new link start dragon ab performance Dragon ?? ?? ?? is a lightweight high performance web framework with Go for the feature and comfor

azerothyang 0 Sep 6, 2022
skr: The lightweight and powerful web framework using the new way for Go.Another go the way.

skr Overview Introduction Documents Features Install Quickstart Releases Todo Pull Request Issues Thanks Introduction The lightweight and powerful web

go-the-way 1 Jan 11, 2022
hiboot is a high performance web and cli application framework with dependency injection support

Hiboot - web/cli application framework About Hiboot is a cloud native web and cli application framework written in Go. Hiboot is not trying to reinven

hidevops.io 179 Nov 20, 2022
A minimal framework to build web apps; with handler chaining, middleware support; and most of all standard library compliant HTTP handlers(i.e. http.HandlerFunc).

WebGo v4.1.3 WebGo is a minimalistic framework for Go to build web applications (server side) with zero 3rd party dependencies. Unlike full-fledged fr

Kamaleshwar 266 Jan 1, 2023
Building basic API with go, gin and gorm

Project Description Terima kasih sudah berkunjung ke halaman repositori ini, repositori ini berisi basic RESTFUL API dengan menggunakan teknologi seba

dev-beard 1 Nov 20, 2021
Bramble is a production-ready GraphQL federation gateway.

Bramble is a production-ready GraphQL federation gateway. It is built to be a simple, reliable and scalable way to aggregate GraphQL services together.

Movio 452 Jan 3, 2023
Lightweight web framework based on net/http.

Goweb Light weight web framework based on net/http. Includes routing middleware logging easy CORS (experimental) Goweb aims to rely only on the standa

Travis Harmon 34 Dec 21, 2022
Eudore is the core of a golang lightweight web framework.

Eudore eudore是一个golang轻量级web框架核心,可以轻松扩展成一个技术栈专用框架,具有完整框架设计体系。 反馈和交流请加群组:QQ群373278915。 Features 易扩展:主要设计目标、核心全部解耦,接口即为逻辑。 简单:对象语义明确,框架代码量少复杂度低,无依赖库。 易用

null 73 Nov 7, 2022
A lightweight RESTful web framework for Go

Goweb A lightweight RESTful web framework for Go. For examples and usage, please read the Goweb API Documentation Read our Articles Who uses Goweb? "U

Stretchr, Inc. 629 Jan 5, 2023
Best simple, lightweight, powerful and really fast Api with Golang (Fiber, REL, Dbmate) PostgreSqL Database and Clean Architecture

GOLANG FIBER API (CLEAN ARCHITECTURE) Best simple, lightweight, powerful and really fast Api with Golang (Fiber, REL, Dbmate) PostgreSqLDatabase using

Elias Champi 3 Sep 2, 2022
Roche is a Code Generator and Web Framework, makes web development super concise with Go, CleanArch

It is still under development, so please do not use it. We plan to release v.1.0.0 in the summer. roche is a web framework optimized for microservice

Riita 14 Sep 19, 2022