Skip to content
forked from SAP-archive/aker

Reverse proxy for elasticsearch and Kibana providing authentication, authorization and more.

License

Notifications You must be signed in to change notification settings

georgi-lozev/aker

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

88 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Aker

CLA assistant GoDoc

Aker is an HTTP reverse proxy server that is designed to handle incoming requests by forwarding them to a number of plugin handlers.

Plugins can be configured and attached to specific HTTP paths. By default, an HTTP Not Found status is returned for paths that don't have plugins configured.

License

This project is licensed under the Apache Software License, v. 2 except as noted otherwise in the LICENSE file.

Getting Started

Before setting up Aker, one should get familiar with the internal workings of the software.

Aker acts as an HTTP reverse proxy that routes incoming requests to sequences of plugins, depending on the request path. All of the plugin chains (sequences) and their path binding are modeled via the Aker configuration.

Overview

Once a request is received by Aker, it checks the path of the request and determines the plugin chain that should handle it. Afterwards, it forwards the request to the first plugin in that chain. That plugin, external to the Aker process, has the chance to process and modify the incoming request. It can then write output to the response, at which point the processing stops and the request will not go further, or it can leave the request to be processed by subsequent plugins in the chain.

For example, there could be a plugin which does basic authentication. It checks the Authorization header in the request and if the user and password match some desired value, then control is passed to the next plugin, otherwise a 401 Unauthorized code can be returned and processing flow stopped.

In the example above, we have two plugin chains. The first one processes requests to the /hello.png endpoint by passing the request to Plugin A and potentially to Plugin B if the former allows it. The second plugin chain, consisting of a single plugin, processes requests to the /auth path by passing control to Plugin C.

An endpoint can handle a path tree as well. If in the diagram above the second plugin had the /auth/ path configured instead, the handler would handle /auth/something/else as well as the root /auth path. If two endpoints have overlapping paths, then the plugin chain with a path expression that matches the incoming request better will be the one to handle it. This is aligned with how the Go language processes requests.

A common use case for Aker is to have a / endpoint that has a plugin chain ending with a reverse-proxy plugin. That way, anything that does not get processed by other more specific chains gets reverse proxied.

Plugins can use HTTP headers to forward information to subsequent plugins in the chain.

User Guide

The application is written in Go so you will need to set that up. Once you have Go, you can use the following command to download the source code and build it.

go get github.com/SAP/aker

To verify that Aker has been properly installed, use the following command.

which aker

You can run Aker as follows.

aker -config <path_to_config_file>

ℹ️ If you don't specify the -config flag, then Aker will look for a configuration file in ./config.yml.

Let's have a look at a minimal configuration.

---
server:
  host: 0.0.0.0
  port: 8080
  read_timeout: 5
  write_timeout: 10

endpoints: []

This configuration will start Aker and have it listen on the 8080 port for HTTP requests and will have the corresponding read and write timeouts for tcp connections. It will not handle any of the requests, however, since we haven't configured any behavior.

ℹ️ If you want Aker to listen only for local requests, you can change host from 0.0.0.0 to 127.0.0.1.

Here is an extension to the above configuration that adds some meaningful behavior.

---
server:
  host: 0.0.0.0
  port: 8080
  read_timeout: 5
  write_timeout: 10

endpoints:
  - path: "/"
    audit: true
    plugins:
      - name: aker-proxy-plugin
        configuration:
          url: http://location.com

The above configuration specifies that all incoming requests on path / should be forwarded to the aker-proxy-plugin, which in turn will proxy calls to http://example.org.

ℹ️ One needs to make sure that the aker-proxy-plugin plugin is available on the PATH, or one could configure the plugin name to point to the plugin executable.

The audit option can be used to configure detailed logging of incoming requests.

Developer Guide

You will need to download the following tools.

  • Ginkgo - Used for running the tests.
  • Godep - Used for dependency management.
  • Counterfeiter - Used for interface mocking.

You can run the tests with the following command.

ginkgo -r

You can regenerate all the interface mocks with the following command.

go generate ./...

You can update the stored Go dependencies with the following command.

rm -rf Godeps/ vendor/
godep save ./...

Writing a Plugin

Package plugin of Aker provides means to create an Aker plugin. It exposes the ListenAndServeHTTP function, which is the main entry point for a plugin.

Following is an example plugin implementation that returns a response body and status code, both specified via the Aker configuration.

package main

import (
  "net/http"

  "github.com/SAP/aker/plugin"
)

type MessageHandler struct {
  Message []byte
  Code    int
}

func (h *MessageHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
  w.WriteHeader(h.Code)
  w.Write(h.Message)
}

func main() {
  plugin.ListenAndServeHTTP(func(data []byte) (http.Handler, error) {
    var handler = &MessageHandler{}
    if err := plugin.UnmarshalConfig(data, &handler); err != nil {
      return nil, err
    }
    return handler, nil
  })
}

Creating a plugin requires implementing a HandlerFactory object, which is basically just a function that accepts a byte array as input parameter and returns a http.Handler and an error. The byte array will contain the plugin configuration as specified in the Aker yaml configuration.

The factory should use UnmarshalConfig to get a Go struct representation of the data. The underlying notation is YAML, thus if you want to make use of Go's struct field tags, you should use yaml:<opts> for configuring how the unmarshaller should handle the Go struct fields.

func myFactory(data []byte) (http.Handler, error) {
  var myConfig struct{
    a int
    b string
  } cfg

  if err := plugin.UnmarshalConfig(data, &cfg); err != nil {
    return nil, err
  }
  return newMyHandler(cfg), nil
}

The communication between Aker and each plugin, and between each pair of plugins, happens via HTTP, which is transported over unix domain sockets. The ListenAndServeHTTP function takes care of cleaning up the socket file, once the plugin receives a signal to exit. Because of that, it is undesirable to call os.Exit from within a plugin, as this will leave the allocated socket file on the file system.

Plugin's stdout and stderr are captured by Aker, so writing to them is the way to send log messages to the central Aker log. They'll get decorated by having the plugin name appended in front of each log line.

[plugin-name]: 2016/07/19 14:28:59 [INFO] Starting...

It is advisable to use the logger provided by the logging package.

Request tracking mechanism is provided by Aker. Each incoming HTTP request is decorated with the X-Aker-Request-Id header, as well as each response. If a plugin encounters problem with some request, it is advisable to dump the value of the X-Aker-Request-Id header for debugging purposes. The X-Aker-Request-Id header is also propagated to the end user, so should the user face a problem, they can provide the header value for tracing.

If a plugin is part of a plugin chain, which means that each request gets processed by multiple plugins before it is returned to Aker and thus to the user, then the way of telling the requests not to continue further the plugin chain is to write something to the response by calling Write or WriteHeader of the http.ResponseWriter. This will stop the request from going through subsequent plugins and will return the response to the end user.

Tests

aker project contains unit tests, in order to execute them run the following command in project root directory.

ginkgo -r

About

Reverse proxy for elasticsearch and Kibana providing authentication, authorization and more.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Go 99.6%
  • Shell 0.4%