Heimdall is inspired by the ZeroTrust idea and tries to adopt it to some extent to web applications.
Heimdall authenticates and authorizes incoming HTTP requests as well as enriches these with further contextual information and finally transforms resulting subject information into a format, required by the upstream services. And all of that can be controlled by each and every backend service individually.
It is supposed to be used either as
- a Reverse Proxy in front of your upstream API or web server that rejects unauthorized requests and forwards authorized ones to your end points, or as
- a Decision Service, which integrates with your API Gateway (Kong, NGNIX, Envoy, Traefik, etc.) and then acts as a Policy Decision Point.
Create a configuration file named heimdall.yaml
:
log:
level: info
mechanisms:
authenticators:
- id: anonymous_authenticator
type: anonymous
finalizers:
- id: create_jwt
type: jwt
default_rule:
methods:
- GET
- POST
execute:
- authenticator: anonymous_authenticator
- finalizer: create_jwt
Start heimdall:
docker run -t -p 4456:4456 -v $PWD:/heimdall/conf \
dadrus/heimdall:latest serve decision -c /heimdall/conf/heimdall.yaml
Call the decision service endpoint to emulate behavior of an API-Gateway:
curl -v 127.0.0.1:4456/foobar
You should now see similar output to the following snippet:
* Trying 127.0.0.1:4456...
* Connected to 127.0.0.1 (127.0.0.1) port 4456 (#0)
> GET /foobar 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
< Server: Heimdall Decision API
< Date: Sat, 25 Jun 2022 14:10:16 GMT
< Content-Length: 0
< Authorization: Bearer eyJhbGciOiJQUzI1NiIsImtpZCI6IjJkZGIxZDM3MWU1MGFjNDQ5ZGJhNjcyNj
ZmZDRjMzU0OWZjNmRmYTYiLCJ0eXAiOiJKV1QifQeyJleHAiOjE2NTYxNjY1MTYsImlhdCI6MTY1NjE2NjIxNi
wiaXNzIjoiaGVpbWRhbGwiLCJqdGkiOiIxYjdlODdjYi0zYjdjLTQ1ZDAtYWEyZi00MTRhYmI2YjBlMzciLCJu
YmYiOjE2NTYxNjYyMTYsInN1YiI6ImFub255bW91cyJ9MY6fjk7K6ZNn57Mrjy6UGI1cvIMCOOEJoCQF45PHQ3
4BfoPxMuTRjdVUZPX4xnT4suyWySsaU1wisgXv4CuMf4WsEUCPKOH8NKv5Zty6eXjTdWQpekDWYsHpVVwz8UHL
mrRASlo_JKErj64wPbRcQWyLMR9X-4cR28ZuH3IbyXh4-XlGNEMAVWYFaZGv1QlEd7jcw3jSVK0b5AtY-NUcVQ
lccWpqWD43AE-3spchqboFuiuW5IxFGd4Mc0Dp6uepuQ-XiWEFg9rxnaxl-Grr3LfSY83oML53Akrl4lGtVBu5
5QVVjduv_b2ykRnqh7Im9lSivokuVMEuSE8bN2qnqg
<
* Connection #0 to host 127.0.0.1 left intact
What did you actually do? ;)
- You've created a very simple configuration with a default rule, which instructs heimdall to create a JSON Web Token (JWT) with the 'sub' claim set to 'anonymous' for every request on every URL for the HTTP methods GET and POST. You've seen the resulting JWT in the snipped above.
- You've started heimdall in the decision operation mode
- And sent an HTTP GET request to an imaginary
foobar
endpoint. This is also what an API-Gateway will do before forwarding the received request to an upstream'sfoobar
endpoint. - Heimdall answered with an HTTP
200 OK
response and set the expectedAuthorization
header, which the API-Gateway would forward to the upstream service together with the original request.
To run the following configuration, you need docker-compose.
Create a config file (config.yaml
) with the following content:
log:
level: info
mechanisms:
authenticators:
- id: anonymous_authenticator
type: anonymous
authorizers:
- id: deny_all_requests
type: deny
- id: allow_all_requests
type: allow
finalizers:
- id: create_jwt
type: jwt
default_rule:
execute:
- authenticator: anonymous_authenticator
- authorizer: deny_all_requests
- finalizer: create_jwt
providers:
file_system:
src: /heimdall/conf/rule.yaml
watch: true
Create a rule file (rule.yaml
) with the following contents:
version: "1alpha4"
rules:
- id: test-rule
match:
routes:
- path: /**
forward_to:
host: upstream
execute:
- authorizer: allow_all_requests
Create a docker-compose.yaml
file with the following contents and modify it to include the correct paths to your config.yaml
and rule.yaml
files:
version: "3"
services:
heimdall:
image: dadrus/heimdall:latest
volumes:
# Mount your config file:
- ./config.yaml:/heimdall/conf/config.yaml:ro
# Mount your rule file:
- ./rule.yaml:/heimdall/conf/rule.yaml:ro
ports:
- 4455:4455
command: -c /heimdall/conf/config.yaml serve proxy
upstream:
image: containous/whoami:latest
Start the docker compose environment:
docker-compose up
Call the proxy service endpoint to emulate behavior of a client application:
curl -v 127.0.0.1:4455/foobar
You should now see similar output to the following snippet:
* Trying 127.0.0.1:4455...
* Connected to 127.0.0.1 (127.0.0.1) port 4455 (#0)
> GET /foobar HTTP/1.1
> Host: 127.0.0.1:4455
> User-Agent: curl/7.74.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Date: Thu, 04 Aug 2022 07:53:41 GMT
< Content-Type: text/plain; charset=utf-8
< Content-Length: 872
<
Hostname: 4f809f75f31b
IP: 127.0.0.1
IP: 172.22.0.3
RemoteAddr: 172.22.0.2:42100
GET /foobar HTTP/1.1
Host: upstream
User-Agent: curl/7.74.0
Accept: */*
Authorization: Bearer eyJhbGciOiJQUzI1NiIsImtpZCI6IjNhYjFiMDdmMmMyNjlkMWVlMTRjNzQ2NDA4
OTAyZjRlNWQ1MDAyOTgiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjE2NTkzMzczMjEsImlhdCI6MTY1OTMzNzAyMSw
iaXNzIjoiaGVpbWRhbGwiLCJqdGkiOiJjMmEzNjczMy04ZDBjLTQzYWQtOGFkNi0xM2Q4NGVhNDI1MTgiLCJuY
mYiOjE2NTkzMzcwMjEsInN1YiI6ImFub255bW91cyJ9.gw-h15LaUUYV-Sjk6Vf-kZflnZxn88lejVIIatKliv
FkeUz8oo9x9juKBSzr4nIVWjGZ_atGVmLoKshudHdnpvABx5cgBaz2_KDgifVzGORE1zld9vGDpU7IPjOyC9-M
b7vOOA1fq9pbQ4nfXw100AJJKFXSct9cYa3163kk_s-jEIPclhB0ZiPqGI-t_GiYJBCVKOTJPkkLKB51KCgn2y
PvO3qLCwO81JdCSFG9k2WLjWZlQe-a8u4El-2qctx8yB-vBFPIaQlwCJh66of3hcUs98IoVlMLGdTJSI4pX9nK
s8OMxVO37eI501gZXXkF5IiSsRAqV_o8pMcGZ47Ztg
Forwarded: for=172.22.0.1;proto=http
X-Forwarded-For: 172.22.0.1
* Connection #0 to host 127.0.0.1 left intact
What did you actually do? ;)
- You've created a very simple configuration with a default rule, with preconfigured defaults. The used authenticator instructs heimdall to create an anonymous subject for every request on every URL for the HTTP methods GET and POST. The default authenticator rejects any request and the default finalizer creates a JWT from the subject mentioned above.
- You've created a very simple rule, which reuses the default authenticator and finalizer and configures an authorizer, which allows any request to pass through.
- You've created and started a docker compose environment with heimdall operated in proxy mode and a "upstream" service, which responds with everything it receives.
- And sent an HTTP GET request to an imaginary
foobar
endpoint. - Heimdall run the request through its pipeline and forwarded the enriched (
Authorization
header) request to the "upstream" service, which just returned all it has received to the caller.
- Documentation - Checkout the documentation for more details.
- GitHub - Visit heimdall on GitHub.
As of today heimdall is built as a multi-platform image for the following platforms:
- linux/amd64
- linux/arm64
- linux/arm/v7
If you need support for other platforms, don't hesitate to file an issue at GitHub. Contributions are very welcome as well!
All images adhere to the following patterns:
-
For stable, respectively released versions, image tags have the suffix of the corresponding version and have the
dadrus/heimdall:<version>
form. E.g. an image tagged withdadrus/heimdall:0.11.1-alpha
is the image for the released0.11.1-alpha
version of heimdall. In addition, there is adadrus/heimdall:latest
tag referencing the latest released version as well. -
Development images are created from the main branch by heimdall's continuous integration and are tagged with the
dev
and with thedev-<SHA>
suffix, where the SHA is the commit in heimdall main from which it was created. For example, after a build at commit730b2206
, an image will be created fordadrus/heimdall:dev-730b2206fdfc688ca42bcdf0e344d8fa6bfba232
and the imagedadrus/heimdall:dev
will be tagged to it until the next build.
Each published image is signed using Cosign. The signatures are located in the same repository and have the tag pattern sha256-<SHA256>.sig
. An SBOM is attached to each image as an attestation, created via Cosign as well. These objects are also present in this repository with tags adhering to the sha256-<SHA256>.att
name pattern. Both, the images and the SBOM attestations are signed using keyless signing feature. Please refer to heimdall's Documentation on how to verify both and extract the SBOM.
Heimdall is licensed under Apache-2.0 license.