Skip to content

Commit

Permalink
change: allow missing consumer
Browse files Browse the repository at this point in the history
If consumer is required, use consumerRestriction plugin to restrict.

This change makes it possible to support limit rate per consumer
coexists with limit rate without consumer.
Signed-off-by: spacewander <[email protected]>
  • Loading branch information
spacewander committed Oct 18, 2024
1 parent 88fb116 commit f090586
Show file tree
Hide file tree
Showing 15 changed files with 123 additions and 53 deletions.
11 changes: 1 addition & 10 deletions api/pkg/filtermanager/filtermanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -384,16 +384,7 @@ func (m *filterManager) DecodeHeaders(headers capi.RequestHeaderMap, endStream b
// we check consumer at the end of authn filters, so we can have multiple authn filters
// configured and the consumer will be set by any of them
c, ok := m.callbacks.consumer.(*consumer.Consumer)
if !ok {
api.LogInfo("reject for consumer not found")
m.localReply(&api.LocalResponse{
Code: 401,
Msg: "consumer not found",
}, true)
return
}

if len(c.FilterConfigs) > 0 {
if ok && len(c.FilterConfigs) > 0 {
api.LogDebugf("merge filters from consumer: %s", c.Name())

c.InitOnce.Do(func() {
Expand Down
3 changes: 3 additions & 0 deletions e2e/tests/consumer.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,6 @@ spec:
config:
keys:
- name: Authorization
consumerRestriction:
config:
denyIfNoConsumer: true
3 changes: 3 additions & 0 deletions e2e/tests/consumer_in_gateway.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ spec:
config:
keys:
- name: Authorization
consumerRestriction:
config:
denyIfNoConsumer: true
---
apiVersion: htnn.mosn.io/v1
kind: FilterPolicy
Expand Down
4 changes: 4 additions & 0 deletions plugins/plugins/consumerrestriction/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ type Rule struct {
}

func (conf *config) Init(cb api.ConfigCallbackHandler) error {
if conf.GetDenyIfNoConsumer() {
return nil
}

rules := conf.GetDeny()
if rules == nil {
rules = conf.GetAllow()
Expand Down
12 changes: 6 additions & 6 deletions plugins/plugins/consumerrestriction/filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,15 @@ type filter struct {
config *config
}

func (f *filter) reject(msg string) api.ResultAction {
return &api.LocalResponse{Code: 403, Msg: msg}
}

func (f *filter) DecodeHeaders(headers api.RequestHeaderMap, endStream bool) api.ResultAction {
consumer := f.callbacks.GetConsumer()
if consumer == nil {
api.LogInfo("consumerRestriction: consumer not found")
return f.reject("consumer not found")
return &api.LocalResponse{Code: 401, Msg: "consumer not found"}
}

if f.config.GetDenyIfNoConsumer() {
return api.Continue
}

consumerName := consumer.Name()
Expand All @@ -49,7 +49,7 @@ func (f *filter) DecodeHeaders(headers api.RequestHeaderMap, endStream bool) api

if methodMatched != f.config.allow {
api.LogInfof("consumerRestriction: consumer %s not allowed", consumerName)
return f.reject("consumer not allowed")
return &api.LocalResponse{Code: 403, Msg: "consumer not allowed"}
}

return api.Continue
Expand Down
19 changes: 15 additions & 4 deletions plugins/tests/integration/hmac_auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,21 @@ func TestHmacAuth(t *testing.T) {
}{
{
name: "sanity",
config: controlplane.NewSinglePluginConfig("hmacAuth", map[string]interface{}{
"signatureHeader": "x-sign-hdr",
"accessKeyHeader": "x-ak",
"dateHeader": "x-date",
config: controlplane.NewPluginConfig([]*model.FilterConfig{
{
Name: "hmacAuth",
Config: map[string]interface{}{
"signatureHeader": "x-sign-hdr",
"accessKeyHeader": "x-ak",
"dateHeader": "x-date",
},
},
{
Name: "consumerRestriction",
Config: map[string]interface{}{
"deny_if_no_consumer": true,
},
},
}),
run: func(t *testing.T) {
hdr := http.Header{}
Expand Down
47 changes: 35 additions & 12 deletions plugins/tests/integration/key_auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"github.com/stretchr/testify/assert"

"mosn.io/htnn/api/pkg/filtermanager"
"mosn.io/htnn/api/pkg/filtermanager/model"
"mosn.io/htnn/api/plugins/tests/integration/controlplane"
"mosn.io/htnn/api/plugins/tests/integration/dataplane"
)
Expand Down Expand Up @@ -51,10 +52,21 @@ func TestKeyAuth(t *testing.T) {
}{
{
name: "key in the header",
config: controlplane.NewSinglePluginConfig("keyAuth", map[string]interface{}{
"keys": []interface{}{
map[string]interface{}{
"name": "Authorization",
config: controlplane.NewPluginConfig([]*model.FilterConfig{
{
Name: "keyAuth",
Config: map[string]interface{}{
"keys": []interface{}{
map[string]interface{}{
"name": "Authorization",
},
},
},
},
{
Name: "consumerRestriction",
Config: map[string]interface{}{
"deny_if_no_consumer": true,
},
},
}),
Expand All @@ -72,15 +84,26 @@ func TestKeyAuth(t *testing.T) {
},
{
name: "key in the query",
config: controlplane.NewSinglePluginConfig("keyAuth", map[string]interface{}{
"keys": []interface{}{
map[string]interface{}{
"name": "Authorization",
"source": "HEADER",
config: controlplane.NewPluginConfig([]*model.FilterConfig{
{
Name: "keyAuth",
Config: map[string]interface{}{
"keys": []interface{}{
map[string]interface{}{
"name": "Authorization",
"source": "HEADER",
},
map[string]interface{}{
"name": "ak",
"source": "QUERY",
},
},
},
map[string]interface{}{
"name": "ak",
"source": "QUERY",
},
{
Name: "consumerRestriction",
Config: map[string]interface{}{
"deny_if_no_consumer": true,
},
},
}),
Expand Down
2 changes: 1 addition & 1 deletion site/content/en/docs/concept/consumer.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ Each Consumer plugin configured on the Route will proceed through the following
1. If the match is unsuccessful, return a 401 HTTP status code.
2. If the match is successful, move on to the next plugin.

If no Consumer is matched after all the Consumer plugins have been executed, a 401 HTTP status code will be returned.
Unlike Kong/APISIX, requests that do not match a Consumer are not interrupted. If you want to ensure that only authenticated consumers can access backend services, we need to use it in conjunction with the [consumerRestriction plugin](../reference/plugins/consumer_restriction.md).

In addition to that, we can configure additional plugins for consumers under the `filters` field. These plugins are only executed after the consumer has been authenticated. Take the following configuration as an example:

Expand Down
13 changes: 7 additions & 6 deletions site/content/en/docs/reference/plugins/consumer_restriction.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ title: Consumer Restriction

## Description

The `consumerRestriction` plugin determines whether the current consumer has access permission based on the configuration. If there is no current consumer or the consumer does not have access permission, a 403 HTTP status code is returned.
The `consumerRestriction` plugin determines whether the current consumer has access permission based on the configuration. If there is no current consumer, a 401 HTTP status code is returned. If the consumer does not have access permission, a 403 HTTP status code is returned.

## Attribute

Expand All @@ -16,12 +16,13 @@ The `consumerRestriction` plugin determines whether the current consumer has acc

## Configuration

| Name | Type | Required | Validation | Description |
|-------|-------|----------|------------|--------------------------------------|
| allow | Rules | False | | List of rules allowing access access |
| deny | Rules | False | | List of rules denying access access |
| Name | Type | Required | Validation | Description |
|------------------|-------|----------|------------|----------------------------------------------|
| allow | Rules | False | | List of rules allowing access access |
| deny | Rules | False | | List of rules denying access access |
| denyIfNoConsumer | bool | False | | Deny request if there is no matched consumer |

Only one of `allow` or `deny` can be configured.
Only one of `allow` or `deny` or `denyIfNoConsumer` can be configured.

### Rules

Expand Down
2 changes: 1 addition & 1 deletion site/content/zh-hans/docs/concept/consumer.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ spec:
1. 如果匹配失败,返回 401 HTTP 状态码。
2. 如果匹配成功,则执行下一个插件。

如果执行完全部消费者插件后,仍然没有匹配到消费者,则返回 401 HTTP 状态码
和 Kong/APISIX 不同的是,请求没有匹配到消费者时不会被中断。如果想在保证只有经过认证的消费者才能访问后端服务,我们需要额外配合 [consumerRestriction 插件](../reference/plugins/consumer_restriction.md) 一起使用

除此之外,我们还可以在 `filters` 字段下给消费者配置额外的插件。这些插件只有在通过认证之后才会执行。以下面的配置为例:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ title: Consumer Restriction

## 说明

`consumerRestriction` 插件根据配置,判断当前的消费者是否有访问权限。如果当前不存在消费者或消费者没有访问权限,则返回 403 HTTP 状态码。
`consumerRestriction` 插件根据配置,判断当前的消费者是否有访问权限。如果当前不存在消费者,则返回 403 HTTP 状态码。如果消费者没有访问权限,则返回 403 HTTP 状态码。

## 属性

Expand All @@ -16,12 +16,13 @@ title: Consumer Restriction

## 配置

| 名称 | 类型 | 必选 | 校验规则 | 说明 |
|-------|-------|------|----------|--------------------|
| allow | Rules || | 允许访问的规则列表 |
| deny | Rules || | 禁止访问的规则列表 |
| 名称 | 类型 | 必选 | 校验规则 | 说明 |
|------------------|-------|------|----------|----------------------------------|
| allow | Rules || | 允许访问的规则列表 |
| deny | Rules || | 禁止访问的规则列表 |
| denyIfNoConsumer | bool || | 如果没有匹配到消费者,则禁止访问 |

`allow``deny` 之间只能配置一个。
`allow``deny``denyIfNoConsumer` 之间只能配置一个。

### Rules

Expand Down
3 changes: 2 additions & 1 deletion tools/cmd/linter/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -487,7 +487,8 @@ func lintConfigurationByCategory(category string) error {
}

name := filepath.Base(path)[:len(filepath.Base(path))-len(ext)]
pb := filepath.Join("types", category, name, "config.proto")
goPkgName := strings.ReplaceAll(name, "_", "")
pb := filepath.Join("types", category, goPkgName, "config.proto")
ms, err := readProto(pb)
if err != nil {
return err
Expand Down
30 changes: 24 additions & 6 deletions types/plugins/consumerrestriction/config.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions types/plugins/consumerrestriction/config.pb.validate.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions types/plugins/consumerrestriction/config.proto
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,6 @@ message Config {
option (validate.required) = true;
Rules allow = 1;
Rules deny = 2;
bool deny_if_no_consumer = 3;
}
}

0 comments on commit f090586

Please sign in to comment.