Decision Service Quickstart

This document describes a very simple use case in which you’ll see heimdall’s Decision service in action.

Prerequisites

Configure

heimdall can be configured via environment variables, as well as using a configuration file. For simplicity reasons, we’ll use a configuration file here. So create a config file named config.yaml with the following content:

log:
  level: info  (1)

serve:
  decision:
    trusted_proxies:  (2)
      - 0.0.0.0/0

rules:
  mechanisms:
    authenticators:  (3)
      - id: do_nothing
        type: noop
      - id: anon
        type: anonymous
      - id: reject_requests
        type: unauthorized
    unifiers:  (4)
      - id: do_nothing
        type: noop
      - id: create_jwt
        type: jwt

  default:  (5)
    methods:
      - GET
      - POST
    execute:
      - authenticator: reject_requests
      - unifier: create_jwt

  providers:
    file_system:  (6)
      src: /heimdall/conf/rules.yaml
      watch: true
1Here we are setting the log level to info to be able to see any log output. By default, heimdall logs on error log level.
2Configures heimdall’s decision service to trust requests from all sources. This allows usage of X-Forwarded-* headers. This configuration is only required if you want to try out the docker compose example from below.
3Configures a couple of authenticators - the noop authenticator, which does nothing, the anonymous authenticator, which treats every request as anonymous (creates a subject with its id set to anonymous) and the unauthorized authenticator, which rejects every request with 401 Unauthorized HTTP code.
4Configured a couple of unifiers - the noop unifier, which does nothing and the jwt` unifier, which transforms the information about the subject into a JWT.
5Configures the default rule, which will obviously reject every request.
6Configures a file_system provider, which allows heimdall loading rules from the local filesystem; here from the /heimdall/conf/rule.yaml file.

With that configuration and without any additional rules, heimdall would reject all requests with 401 Unauthorized. But it has a file_system provider configured, which makes heimdall expect the referenced rules file already on startup. To make it more usable, create a rules.yaml file with the following content:

version: "1alpha2"
rules:
- id: rule1  (1)
  match:
    url: http://<**>/public
  execute:
    - authenticator: do_nothing
    - unifier: do_nothing

- id: rule2  (2)
  match:
    url: http://<**>/anonymous
  execute:
    - authenticator: anon

This file contains two rules:

1This rule should match the http://<**>/public url and accept every request without performing any authentication and unification.
2This rule should match the http://<**>/anonymous url. It uses the anon authenticator from our mechanisms definitions and since it does not specify a unifier, the unifier from the default rule create_jwt is used.

Run Standalone

Run heimdall specifying the configuration file from above

If you’re using a binary, put the rules.yaml file into the /heimdall/conf directory (as heimdall will look the rules.yaml file in it according to our configuration from above) just execute

$ ./heimdall serve decision -c config.yaml

The above command will start heimdall in a decision operation mode. By default, the service will be served on port 4456.

Otherwise, if you’ve built a Docker image, run heimdall in the decision operation mode via

$ docker run -t -v $PWD:/heimdall/conf -p 4456:4456 \
  dadrus/heimdall:latest serve decision -c /heimdall/conf/config.yaml

In both cases, you’ll see similar output to

2022-08-04T07:40:12+02:00 INF No opentracing provider configured. Tracing will be disabled.
2022-08-04T07:40:12+02:00 INF Instantiating in memory cache
2022-08-04T07:40:12+02:00 INF Loading pipeline definitions
2022-08-04T07:40:12+02:00 WRN No rule provider configured. Only defaults will be used.
2022-08-04T07:40:12+02:00 WRN Key store is not configured. NEVER DO IT IN PRODUCTION!!!! Generating an
                          RSA key pair.
2022-08-04T07:40:12+02:00 WRN No key id for signer configured. Taking first entry from the key store
2022-08-04T07:40:12+02:00 INF Starting cache evictor
2022-08-04T07:40:12+02:00 INF Starting rule definition loader
2022-08-04T07:40:12+02:00 INF Management service starts listening on: :4457
2022-08-04T07:40:12+02:00 INF Metrics service starts listening on: :9000
2022-08-04T07:40:12+02:00 INF Decision service starts listening on: :4456

Ignore the warnings. They are expected as we’ve neither configured a rule provider, nor have we configured a key store for JWT signing purposes. Nevertheless, the default rule can be used.

Run Integrated

Alternatively, if you would like to have an environment close to a real scenario, you could make use of docker compose. Create the following docker-compose.yaml file for this purpose:

version: "3"

services:
  proxy:
    image: traefik:2.9.1
    ports:
      - 9090:9090
    command: >
      --providers.docker=true
      --providers.docker.exposedbydefault=false
      --entryPoints.http.address=":9090"
      --accesslog --api=true --api.insecure=true
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
    labels:
      - traefik.enable=true
      - traefik.http.routers.traefik_http.service=api@internal
      - traefik.http.routers.traefik_http.entrypoints=http
      - traefik.http.middlewares.heimdall.forwardauth.address=http://heimdall:4456  (1)
      - traefik.http.middlewares.heimdall.forwardauth.authResponseHeaders=Authorization

  heimdall:  (2)
    image: dadrus/heimdall:latest
    volumes:
      - ./config.yaml:/heimdall/conf/config.yaml:ro
      - ./rules.yaml:/heimdall/conf/rules.yaml:ro
    command: -c /heimdall/conf/config.yaml serve decision

  upstream:  (3)
    image: containous/whoami:latest
    labels:
      - traefik.enable=true
      - traefik.http.services.whoami.loadbalancer.server.port=80
      - traefik.http.routers.whoami.rule=PathPrefix("/")
      - traefik.http.routers.whoami.middlewares=heimdall

This setup contains three services:

1is Traefik, which is used to dispatch the incoming requests and also forward all of them to heimdall first.
2is heimdall, configured to use the configuration and the rule files from above
3is a small service, which just echoes back whatever it receives.

Use

Send some request to heimdall’s decision service endpoint.

If you’ve started heimdall as described in Run Standalone, that can be achieved by making a call to heimdall’s decision endpoint:

$ curl -v 127.0.0.1:4456/foobar

If you’ve started heimdall as described in Run Integrated, that can be achieved by making a call to the port 9090 exposed by Traefik:

$ curl -v 127.0.0.1:9090/foobar

In both cases, the default rule will apply, and you’ll receive a 401 Unauthorized response.

Try sending requests to the /public and the /anonymous endpoints and see what happens. In both cases, the response will be an HTTP 200 OK. And the response from the /anonymous endpoint will also contain an Authorization header containing a JWT, e.g. as shown below.

*   Trying 127.0.0.1:4456...
* Connected to 127.0.0.1 (127.0.0.1) port 4456 (#0)
> GET /anonymous HTTP/1.1
> Host: 127.0.0.1:4456
> User-Agent: curl/7.74.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Date: Thu, 04 Aug 2022 07:45:16 GMT
< Content-Length: 0
< Authorization: Bearer eyJhbGciOiJQUzI1NiIsImtpZCI6IjJkZGIxZDM3MWU1MGFjNDQ5ZGJhNjcyNj
ZmZDRjMzU0OWZjNmRmYTYiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjE2NTYxNjY1MTYsImlhdCI6MTY1NjE2NjIxN
iwiaXNzIjoiaGVpbWRhbGwiLCJqdGkiOiIxYjdlODdjYi0zYjdjLTQ1ZDAtYWEyZi00MTRhYmI2YjBlMzciLCJ
uYmYiOjE2NTYxNjYyMTYsInN1YiI6ImFub255bW91cyJ9.MY6fjk7K6ZNn57Mrjy6UGI1cvIMCOOEJoCQF45PH
Q34BfoPxMuTRjdVUZPX4xnT4suyWySsaU1wisgXv4CuMf4WsEUCPKOH8NKv5Zty6eXjTdWQpekDWYsHpVVwz8U
HLmrRASlo_JKErj64wPbRcQWyLMR9X-4cR28ZuH3IbyXh4-XlGNEMAVWYFaZGv1QlEd7jcw3jSVK0b5AtY-NUc
VQlccWpqWD43AE-3spchqboFuiuW5IxFGd4Mc0Dp6uepuQ-XiWEFg9rxnaxl-Grr3LfSY83oML53Akrl4lGtVB
u55QVVjduv_b2ykRnqh7Im9lSivokuVMEuSE8bN2qnqg
<
* Connection #0 to host 127.0.0.1 left intact

You should also be able to see similar output as below from the heimdall’s instance.

...
2022-08-04T07:45:16+02:00 INF TX started _client_ip=127.0.0.1 _http_host=127.0.0.1:4456 _http_method=GET
 _http_path=/foobar _http_scheme=http _http_user_agent=curl/7.74.0 _tx_start=1659599116
2022-08-04T07:45:16+02:00 INF TX finished _access_granted=true _body_bytes_sent=0 _client_ip=127.0.0.1
 _http_host=127.0.0.1:4456 _http_method=GET _http_path=/foobar _http_scheme=http _http_status_code=200
 _http_user_agent=curl/7.74.0 _subject=anonymous _tx_duration_ms=0 _tx_start=1659599116

By the way, this quickstart is also available on GitHub.

Last updated on Jun 28, 2023