Skip to content

Commit

Permalink
feat!: Support for URL rewriting while forwarding the processed reque…
Browse files Browse the repository at this point in the history
…st to the upstream service (#703)
  • Loading branch information
dadrus authored Jun 28, 2023
1 parent d277887 commit be62972
Show file tree
Hide file tree
Showing 50 changed files with 1,374 additions and 316 deletions.
4 changes: 2 additions & 2 deletions charts/heimdall/README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,14 @@ This chart does also support a demo installation, which can be enabled by using
+
[source,bash]
----
$ curl -v -H "Host: demo-app" <you cluster ip and port>/heimdall-demo/public
$ curl -v -H "Host: demo-app" <you cluster ip and port>/heimdall-demo/pub/foo
----

* Send requests to an endpoint which expects a JWT with a `sub` claim set to `anonymous`
+
[source,bash]
----
$ curl -v -H "Host: demo-app" <you cluster ip and port>/heimdall-demo/anonymous
$ curl -v -H "Host: demo-app" <you cluster ip and port>/heimdall-demo/anon/foo
----

* All other endpoints are not allowed to be called and will result in HTTP 403
Expand Down
38 changes: 34 additions & 4 deletions charts/heimdall/crds/ruleset.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ spec:
singular: ruleset
listKind: RuleSetList
versions:
- name: v1alpha1
- name: v1alpha2
served: true
storage: true
schema:
Expand Down Expand Up @@ -77,9 +77,39 @@ spec:
enum:
- regex
- glob
upstream:
description: Schema, host and port of the upstream service to forward the request to. Required only if heimdall is used in proxy operation mode.
type: string
forward_to:
description: Where to forward the request to. Required only if heimdall is used in proxy operation mode.
type: object
x-kubernetes-validations:
- rule: "has(self.host)"
message: "no host is defined for the forward_to property"
properties:
host:
description: Host and port of the upstream service to forward the request to
type: string
rewrite:
description: Configures middlewares to rewrite parts of the URL
type: object
x-kubernetes-validations:
- rule: "has(self.scheme) || has(self.strip_path_prefix) || has(self.add_path_prefix) || has(self.strip_query_parameters)"
message: "rewrite is defined, but does not contain any middleware"
- rule: "has(self.strip_query_parameters) ? size(self.strip_query_parameters) > 0 : true"
message: "no query parameters defined in strip_query_parameters"
properties:
scheme:
description: If you want to overwrite the used HTTP scheme, set it here
type: string
strip_path_prefix:
description: If you want to cut a prefix from the URL path, set it here
type: string
add_path_prefix:
description: If you want to add a prefix to the URL path, set it here
type: string
strip_query_parameters:
description: If you want to remove some query parameters, specify it here
type: array
items:
type: string
methods:
description: The allowed HTTP methods
type: array
Expand Down
12 changes: 7 additions & 5 deletions charts/heimdall/templates/demo/test-rule.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
# SPDX-License-Identifier: Apache-2.0

{{- if .Values.demo.enabled }}
apiVersion: heimdall.dadrus.github.com/v1alpha1
apiVersion: heimdall.dadrus.github.com/v1alpha2
kind: RuleSet
metadata:
name: {{ include "heimdall.demo.fullname" . }}-test-rule
Expand All @@ -26,16 +26,18 @@ spec:
rules:
- id: public-access
match:
url: http://<**>/public
upstream: http://{{ include "heimdall.demo.fullname" . }}.heimdall-demo.svc.cluster.local:8080
url: http://<**>/pub/<**>
forward_to:
host: {{ include "heimdall.demo.fullname" . }}.heimdall-demo.svc.cluster.local:8080
execute:
- authenticator: noop_authenticator
- authorizer: allow_all_requests
- unifier: noop_unifier
- id: anonymous-access
match:
url: http://<**>/anonymous
upstream: http://{{ include "heimdall.demo.fullname" . }}.heimdall-demo.svc.cluster.local:8080
url: http://<**>/anon/<**>
forward_to:
host: {{ include "heimdall.demo.fullname" . }}.heimdall-demo.svc.cluster.local:8080
execute:
- authorizer: allow_all_requests

Expand Down
3 changes: 2 additions & 1 deletion cmd/serve/decision.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ func createDecisionApp(cmd *cobra.Command) (*fx.App, error) {
fx.NopLogger,
fx.Supply(
config.ConfigurationPath(configPath),
config.EnvVarPrefix(envPrefix)),
config.EnvVarPrefix(envPrefix),
config.DecisionMode),
internal.Module,
}

Expand Down
3 changes: 2 additions & 1 deletion cmd/serve/proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ func createProxyApp(cmd *cobra.Command) (*fx.App, error) {
fx.NopLogger,
fx.Supply(
config.ConfigurationPath(configPath),
config.EnvVarPrefix(envPrefix)),
config.EnvVarPrefix(envPrefix),
config.ProxyMode),
internal.Module,
proxy.Module,
)
Expand Down
14 changes: 12 additions & 2 deletions cmd/validate/ruleset.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import (

// NewValidateRulesCommand represents the "validate rules" command.
func NewValidateRulesCommand() *cobra.Command {
return &cobra.Command{
cmd := &cobra.Command{
Use: "rules [path to ruleset]",
Short: "Validates heimdall's ruleset",
Args: cobra.ExactArgs(1),
Expand All @@ -31,6 +31,11 @@ func NewValidateRulesCommand() *cobra.Command {
cmd.Println("Rule set is valid")
},
}

cmd.PersistentFlags().Bool("proxy-mode", false,
"If specified, rule set validation considers usage in proxy operation mode")

return cmd
}

func validateRuleSet(cmd *cobra.Command, args []string) error {
Expand All @@ -44,6 +49,11 @@ func validateRuleSet(cmd *cobra.Command, args []string) error {
return ErrNoConfigFile
}

opMode := config.DecisionMode
if proxyMode, _ := cmd.Flags().GetBool("proxy-mode"); proxyMode {
opMode = config.ProxyMode
}

conf, err := config.NewConfiguration(
config.EnvVarPrefix(envPrefix),
config.ConfigurationPath(configPath),
Expand All @@ -59,7 +69,7 @@ func validateRuleSet(cmd *cobra.Command, args []string) error {
return err
}

rFactory, err := rules.NewRuleFactory(mFactory, conf, logger)
rFactory, err := rules.NewRuleFactory(mFactory, conf, opMode, logger)
if err != nil {
return err
}
Expand Down
32 changes: 27 additions & 5 deletions cmd/validate/ruleset_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func TestValidateRuleset(t *testing.T) {
{
uc: "everything is valid",
confFile: "test_data/config.yaml",
rulesFile: "test_data/ruleset.yaml",
rulesFile: "test_data/valid-ruleset.yaml",
},
} {
t.Run(tc.uc, func(t *testing.T) {
Expand Down Expand Up @@ -70,16 +70,30 @@ func TestRunValidateRulesCommand(t *testing.T) {
uc string
confFile string
rulesFile string
proxyMode bool
expError string
}{
{
uc: "validation fails",
expError: "no config file",
},
{
uc: "everything is valid",
uc: "everything is valid for decision mode usage",
confFile: "test_data/config.yaml",
rulesFile: "test_data/valid-ruleset.yaml",
},
{
uc: "invalid for proxy usage",
proxyMode: true,
confFile: "test_data/config.yaml",
rulesFile: "test_data/invalid-ruleset-for-proxy-usage.yaml",
expError: "no forward_to",
},
{
uc: "everything is valid for proxy mode usage",
proxyMode: true,
confFile: "test_data/config.yaml",
rulesFile: "test_data/ruleset.yaml",
rulesFile: "test_data/valid-ruleset.yaml",
},
} {
t.Run(tc.uc, func(t *testing.T) {
Expand All @@ -95,11 +109,19 @@ func TestRunValidateRulesCommand(t *testing.T) {

cmd.Flags().StringP("config", "c", "", "Path to heimdall's configuration file.")

flags := []string{}

if len(tc.confFile) != 0 {
err := cmd.ParseFlags([]string{"--config", tc.confFile})
require.NoError(t, err)
flags = append(flags, "--config", tc.confFile)
}

if tc.proxyMode {
flags = append(flags, "--proxy-mode")
}

err = cmd.ParseFlags(flags)
require.NoError(t, err)

// WHEN
cmd.Run(cmd, []string{tc.rulesFile})

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
version: "1"
version: "1alpha2"
name: test-rule-set
rules:
- id: rule:foo
match:
url: http://foo.bar/<**>
strategy: glob
upstream: http://bar.foo
# methods: # reuses default
# - GET
# - POST
Expand Down
40 changes: 40 additions & 0 deletions cmd/validate/test_data/valid-ruleset.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
version: "1alpha2"
name: test-rule-set
rules:
- id: rule:foo
match:
url: http://foo.bar/<**>
strategy: glob
forward_to:
host: bar.foo
rewrite:
strip_path_prefix: /foo
add_path_prefix: /baz
strip_query_parameters: [boo]
# methods: # reuses default
# - GET
# - POST
execute:
- authenticator: unauthorized_authenticator
- authenticator: jwt_authenticator1
config:
assertions:
allowed_algorithms:
- RS256
issuers:
- http://127.0.0.1:4444/
scopes:
- profile
- authenticator: hydra_authenticator
- contextualizer: subscription_contextualizer
- authorizer: allow_all_authorizer
- unifier: jwt
config:
claims: |
{"foo": "bar"}
- unifier: bla
config:
headers:
foo-bar: bla
- unifier: blabla
# no on_error (reuses default)
Loading

0 comments on commit be62972

Please sign in to comment.