Skip to content

Aggregadantur is a lib to handle aggregation and security over oauth2/baic auth (basic auth is recommended only for testing) on multiple endpoints

License

Notifications You must be signed in to change notification settings

orange-cloudfoundry/aggregadantur

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Aggregadantur

Aggregadantur is a lib to handle aggregation and security over oauth2/basic auth (basic auth is recommended only for testing) on multiple endpoints.

It has an embedded small server to be used as reverse proxy in front of a website.

Install

As a lib

The easiest way is to include in your configuration file a *models.AggregateRoute ( see below for configuration in your file) and add your handler afterward and finally create the router associate to this route, example:

package main

import (
	"github.com/gorilla/sessions"
	"github.com/orange-cloudfoundry/aggregadantur"
	"github.com/orange-cloudfoundry/aggregadantur/models"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"net/http"
)

type MyConfig struct {
	//...
	SessionKey     string                 `json:"session_key"`
	AggregateRoute *models.AggregateRoute `json:"aggregate_route"`
}

func main() {
	// load config file
	b, err := ioutil.ReadFile("./config.yml")
	if err != nil {
		panic(err)
	}

	var myConfig MyConfig
	// can be also json.Unmarshal or gautocloud.Inject
	err = yaml.Unmarshal(b, &myConfig)
	if err != nil {
		panic(err)
	}
	aggrRoute := myConfig.AggregateRoute

	// adding my own handler as upstream, aggregadantur will act as a middleware
	aggrRoute.Upstream = models.NewUpstreamFromHandler(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		w.Write([]byte("simple text\nmultiline"))
	}))

	// create a router, and add it a session store, here it is a cookie store, it is for saving session with token.
	sess := sessions.NewCookieStore([]byte(myConfig.SessionKey))
	rtr := aggregadantur.NewRouter(sess)
	// add you aggregate route
	err = rtr.AddMuxRoute(aggrRoute)
	if err != nil {
		panic(err)
	}
	// listen on it directly
	panic(http.ListenAndServe("127.0.0.1:8089", rtr))
}

With small server

Create a config file named config.yml with this content ( see below for configuration routes in your file):

server:
  # Where the server should listen
  # this can be empty if env var PORT set, it will be set as 0.0.0.0:${PORT}
  listen: 127.0.0.1:8000
  # Set a strong session_key, this will be used to encrypt cookie which contains oauth2 token
  session_key: "super-secure-session"
  # Set to true to enable ssl, you will need to fill tls_pem
  enable_ssl: false
  # Set pem files if you have enabled ssl
  tls_pem:
    cert_chain: ---pem format of cert chain
    private_key: --pem format of private key
logging:
  # set level of log (debug, info, warning, error, panic)
  level: info
  # Set to true to not have color
  no_color: false
  # Set to true to see logs in json
  in_json: true
routes: # routes see below

Configuration Aggregation Route

# Name of your aggregate route
name: my-route
# An identity to identify after in endpoints to aggregate
identifier: an-identity
# Includes path for making aggregation
# You can use globs:
#   - appending /* will only make requests available in first level to aggregate
#   - appending /** will mark everything to aggregate
# e.g.: /app/**
includes: [ "/**" ]
# The same as Includes except that's path matching this will be excluded of endpoint to be aggregated
excludes: [ "/metrics" ]
# Set to false to not let pass method Options on auth
options_passthrough: true
# Upstream URL where all request will be redirected
# Query parameters can be passed, e.g.: http:#localhost?param=1
# User and password are given as basic auth too (this is not recommended to use it), e.g.: http://user:password@localhost
# This can be empty if you will set an handler as upstream when using lib
upstream: http://localhost:8080
# By default response from upstream are buffered, it can be issue when sending big files
# Set to true to use stream response. retry will not be performed in this case
# Buffer is only use when upstream url exists
no_buffer: false
# Set to true to not check ssl certificates from upstream or endpoints (not really recommended)
insecure_skip_verify: false
# Must match host(s)
# You can set a wildcard on host like *.my-domain.com to only trigger reverse proxy on this domain
hosts: [ "*" ]
# Must match this beginning of path for let it pass in reverse proxy
# Note that this path will be remove before send path to upstream
# e.g. with prefix path `/foo`: `/foo/index` will give as `/index` to upstream
path: "/"
# Endpoints to aggregate in aggregate mode
# You MUST set always the current route as one of the endpoint to get value of the current one
# All requests to endpoints will be made in parallel before get aggregated
aggregate_endpoints:
  # declare itself
  - url: http://localhost:8000
    identifier: an-identity
  # declare a second endpoint
  - url: http://other-domain:8000
    identifier: an-identity2

# Set an auth which is independent of aggregation, you can set an auth on all path you want even if no aggregation exists
auth:
  # Includes path for triggering authentication
  # You can use globs:
  #   - appending /* will only make requests available in first level to aggregate
  #   - appending /** will mark everything to aggregate
  # e.g.: /app/**
  includes: [ "/**" ]
  # The same as Includes except that's path matching this will be excluded of authentication
  excludes: [ "/metrics" ]
  # Set oauth2 server, this server MUST implements grant type password (as uaa do)
  oauth2:
    # Token url to retrieve the token
    token_url: https://localhost:8080/oauth/token
    # Client id for making token request with grant type password
    client_id: "a client id"
    # Client secret for making token request with grant type password
    client_secret: "a client secret"
    # By default token retrieval is made in post form, you can either set to send params in json format
    params_as_json: false
    # Token format to retrieve when getting token (defaulting to `jwt`)
    token_format: "jwt"
    # Scopes that are allowed to enter
    scopes: [ "admin scope", "openid" ]
  # You can set basic auth but only for testing, this is not recommended
  basic_auth:
    - username: "admin"
      password: "admin"
      # set scopes to your user in addition of login information
      scopes: [ "admin scope", "openid" ]
  # You should be able to verify all jwt for each endpoints
  # this let you set all jwt checks you want
  jwt_checks:
    # You must set alg of the token
    # we do not allow to retrieve freely from token itself, there was too much security issue in libs for that
    - alg: RS256
      # Secret public key to verify signature
      secret: "secret public key"
      # Set the issuer which create the token (normally the same url as auth.oauth2.token_url
      issuer: https://localhost:8080/oauth/token
      # Set to true to not verify token expiration from current time
      # timezone can be an issue for verifying expire time, this is anyway checked after that
      not_verify_expire: false
    - alg: RS256
      secret: "secret public key"
      issuer: https://other-domain:8080/oauth/token
      not_verify_expire: false
  # Set another login page template
  # you must use two %s, there is not golang templating just string format
  # First %s is route name and second %s is where to do post login
  # e.g.: `Login %s, %s` become `Login my-route, /`
  login_page_template: ""
  # If you prefer use a file instead of plain text
  login_page_template_path: "./my-login-page.html"

How I do aggregation ?

For triggering aggregation you must pass either:

  • The HTTP header X-Aggregator-Mode: aggregate
  • The GET parameter on URL aggregator_mode=aggregate: http://localhost?aggregator_mode=aggregate

When aggregating you will get JSON in this format:

{
  "an-identity": "json content or raw content",
  "an-identity2": "json content or raw content"
}

You can set exactly which endpoint to target with either:

  • The header X-Aggregator-Targets: an-identity,an-identity2,..., it must contain the list of identifier
  • The get parameter on url aggregator_targets=an-identity,an-identity2,...

What I've received when on upstream

When using handler

You can get username, scopes and tokens if auth has been triggered on this request:

package main

import (
	"github.com/gorilla/sessions"
	"github.com/orange-cloudfoundry/aggregadantur"
	"github.com/orange-cloudfoundry/aggregadantur/contexes"
	"github.com/orange-cloudfoundry/aggregadantur/models"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"net/http"
)

func main() {
	// ...

	// adding my own handler as upstream, aggregadantur will act as a middleware
	aggrRoute.Upstream = models.NewUpstreamFromHandler(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		username := contexes.Username(req)
		scopes := contexes.Scopes(req)
		claim := contexes.JwtClaim(req)
		w.Write([]byte("simple text\nmultiline"))
	}))

	// ...
}

When upstream

You receive headers:

  • Authorization with JWT token
  • X-Aggregator-Username with username when auth
  • X-Aggregator-Scopes with scopes from user

About

Aggregadantur is a lib to handle aggregation and security over oauth2/baic auth (basic auth is recommended only for testing) on multiple endpoints

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages