Skip to content
This repository has been archived by the owner on Jul 30, 2024. It is now read-only.

Latest commit

 

History

History
245 lines (185 loc) · 8.03 KB

README.md

File metadata and controls

245 lines (185 loc) · 8.03 KB

Build Status Go Report Card Docker Repository on Quay

Service gateway for microservice architectures

Author and license

Martin Helmich
Mittwald CM Service GmbH & Co. KG

This code is GPL-licensed.

Synopsis

This repository contains a service gateway that can be used both as a single-signon gateway or API gateway for a microservice architecture. It implements features like load balancing, rate limiting and (rudimentary) HTTP caching. It uses Consul for service discovery and configuration management.

Compilation and installation

For building, you will need a halfway current Go SDK (tested with 1.12). Then simply go install:

> go install github.com/mittwald/servicegateway

This will produce a (more or less) static binary that you can deploy without any dependencies.

Alternatively, use the Makefile that is shipped within this repository to build the binary and/or a Docker image containing this application:

> make
> make docker

Configuration

Configuration sources

Basic configuration file

The basic configuration is read from a configuration file that by default is expected to be located in /etc/servicegateway.json. However, you can override that location using the -config command line parameter.

Upon startup the config is parsed as a gotemplate. It's therefore possible to inject sensitive information via env: {{ .Env.SENSITIVE_PASSWORD }}

Check the example-configs directory for example configurations.

Configuration with Consul

Most of the configuration options (that are not required for the actual program startup) can also be provided by Consul, an open-source service discovery engine. Configuration can be stored in Consul's key-value store.

Consul uses a hierarchical key-value store. All configuration items for the service gateway must be stored under a common key prefix that is supplied via the -consul-base command-line parameter. This affects the following configuration items:

  1. Rate-limiting configuration (key <base-prefix>/ratelimiting)
  2. Caching configuration (key <base-prefix>/caching)
  3. Upstream application (keys <base-prefix>/applications/<app-identifier>)

Each upstream application is its own key/value pair with the value being a JSON document describing the application.

You can configure the key prefix in which the service gateway should look for configured applications using the -consul-base parameter:

./servicegateway -consul-base gateway/applications

You can create a new application like follows (substitute <name> with an arbitrary identifier for your application):

> curl -X PUT -d @app.json http://localhost:8500/v1/kv/gateway/applications/<name>

This PUTs the contents of the following file app.json into the Consul key/value store:

{
  "backend": {
    "url": "http://httpbin.org"
  },
  "caching": {
    "auto_flush": true,
    "enabled": true,
    "ttl": 3600
  },
  "routing": {
    "type": "path",
    "path": "/bin"
  },
  "rate_limiting": true
}

Important: Configuration changes made in Consul will become effective immediately, without needing to restart the service gateway.

Configuration reference

See the documentation reference.

Core concepts

Routing and dispatching

For this service gateway, multiple upstream applications can be configured. Each application is a key/value entry in Consul's key/value store.

The servicegateway employs a compex logic to determine which HTTP request to route to which upstream application. Currently, there are three different strategies supported that can be used alongside each other:

  • Path based routing: The target upstream application is determined by a HTTP path prefix. For example, all requests having a path starting with /one may be routed to one upstream application and all requests starting with /twoto another upstream application. This may cause issues when the response from the upstream applications contain absolute links to other documents (like in-document links, Link headers or Location headers); the service gateway tries to rewrite these links to use the path prefix configured for the upstream application.

    Example:

    {
      "type": "path",
      "path": "/one"
    }
  • Host based routing: The target upstream application is determined by the HTTP host header.

    Example:

    {
      "type": "host",
      "host": "name.servcices.acme.corp"
    }
  • Pattern based routing: This is the most complex routing strategy. For each application, you can configure a set of path patterns that are mapped to path patterns of the upstream application.

    Example:

    {
      "type": "pattern",
      "patterns": {
        "/products": "app.php?controller=products&action=list",
        "/products/:id": "app.php?controller=products&action=show&product_id=:id"
      }
    }

Applications can be configured by adding new key/value entries into Consul's key/value store under the configured prefix. This can be done at runtime; changes become effective immediately without restarting the servicegateway.

Authentication forwarding

The Servicegateway also features a (very opinionated) authentication handling. Currently, upstream services are expected to authenticate users using JSON Web Tokens. However, the service gateway will not expose these JWTs to the end user. Instead, it is built to map JWTs to random and non-informative API tokens; when receiving a request with an API token, the gateway will lookup the respective JWT from a Redis storage and attach it to the request to the upstream service.

Basic configuration

In order to make authentication work, you'll need the following:

  1. Configure the key that the gateway can use to verify tokens presented by users. For this, you can either specify the key directly, or specify a URL from which the key can be loaded.

    {
      "authentication": {
        "verification_key": "...",
        "verification_key_url": "..."
      }
    }
  2. Configure how to add mapped JWTs to the upstream service requests. Currently, the token can be included in a custom header or an Authorization header.

    {
      "authentication": {
        "mode": "rest",
        "writer": {
          "mode": "header",
          "name": "X-Jwt"
        }
      }
    }

Adding new tokens

The service gateway implementes an administration API that listens on a different port than the actual gateway (caution: the administration port does not provide authentication, do not expose it to the public!); you can use this to pre-configure access tokens for users.

> curl -X POST -H 'Content-Type: application/jwt' -d 'JWT contents...' http://localhost:8081/tokens
{"token":"DLOD5FCRO6PVSLVWD7QPPGIIBXK7XXFACV7LMKEUZOP6DCADXTSQ===="}

Using the same admin API, you can also update existing tokens (for example, if they contained an expirable JWT):

> curl -X PUT -H 'Content-Type: application/jwt' -d 'JWT contents...' http://localhost:81/tokens/DLOD5FCRO6PVSLVWD7QPPGIIBXK7XXFACV7LMKEUZOP6DCADXTSQ%3D%3D%3D%3D
{"token":"DLOD5FCRO6PVSLVWD7QPPGIIBXK7XXFACV7LMKEUZOP6DCADXTSQ===="}