Skip to content

Commit

Permalink
add cors plugin
Browse files Browse the repository at this point in the history
Signed-off-by: spacewander <[email protected]>
  • Loading branch information
spacewander committed Mar 11, 2024
1 parent 086a923 commit dd02e2c
Show file tree
Hide file tree
Showing 11 changed files with 349 additions and 2 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ deploy-cert-manager:
$(KUBECTL) wait --timeout=5m -n cert-manager deployment/cert-manager-cainjector --for=condition=Available

.PHONY: deploy-controller
deploy-controller: kubectl
deploy-controller: kind kubectl
cd controller/ && KIND=$(KIND) KIND_OPTION="-n htnn" KUBECTL=$(KUBECTL) make deploy
$(KUBECTL) wait --timeout=5m -n controller-system deployment/controller-controller-manager --for=condition=Available

Expand Down
2 changes: 1 addition & 1 deletion controller/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ deploy: deploy-image manifests kustomize ## Deploy controller to the K8s cluster
$(KUSTOMIZE) build config/default | $(KUBECTL) apply -f -

.PHONY: undeploy
undeploy: ## Undeploy controller from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion.
undeploy: kustomize ## Undeploy controller from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion.
$(KUSTOMIZE) build config/default | $(KUBECTL) delete --ignore-not-found=$(ignore-not-found) -f -

##@ Build Dependencies
Expand Down
17 changes: 17 additions & 0 deletions controller/internal/translation/testdata/plugins/cors.in.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
apiVersion: mosn.io/v1
kind: HTTPFilterPolicy
metadata:
name: policy
namespace: default
spec:
targetRef:
group: networking.istio.io
kind: VirtualService
name: default
filters:
cors:
config:
allowOriginStringMatch:
- safeRegex:
regex: .*\.envoyproxy\.io
allowMethods: "GET"
46 changes: 46 additions & 0 deletions controller/internal/translation/testdata/plugins/cors.out.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
- metadata:
creationTimestamp: null
name: htnn-h-default.local
spec:
configPatches:
- applyTo: HTTP_ROUTE
match:
routeConfiguration:
vhost:
name: default.local:80
route:
name: default/default
patch:
operation: MERGE
value:
typed_per_filter_config:
htnn.filters.http.cors:
'@type': type.googleapis.com/envoy.extensions.filters.http.cors.v3.CorsPolicy
allowMethods: GET
allowOriginStringMatch:
- safeRegex:
regex: .*\.envoyproxy\.io
status: {}
- metadata:
creationTimestamp: null
name: htnn-http-filter
spec:
configPatches:
- applyTo: HTTP_FILTER
match:
context: GATEWAY
listener:
filterChain:
filter:
name: envoy.filters.network.http_connection_manager
subFilter:
name: htnn.filters.http.golang
patch:
operation: INSERT_BEFORE
value:
disabled: true
name: htnn.filters.http.cors
typed_config:
'@type': type.googleapis.com/envoy.extensions.filters.http.cors.v3.Cors
priority: 100
status: {}
61 changes: 61 additions & 0 deletions controller/plugins/cors/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Copyright The HTNN Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cors

import (
cors "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/cors/v3"

"mosn.io/htnn/pkg/filtermanager/api"
"mosn.io/htnn/pkg/plugins"
)

const (
Name = "cors"
)

func init() {
plugins.RegisterHttpPlugin(Name, &plugin{})
}

type plugin struct {
plugins.PluginMethodDefaultImpl
}

func (p *plugin) Type() plugins.PluginType {
return plugins.TypeSecurity

Check warning on line 37 in controller/plugins/cors/config.go

View check run for this annotation

Codecov / codecov/patch

controller/plugins/cors/config.go#L36-L37

Added lines #L36 - L37 were not covered by tests
}

func (p *plugin) Order() plugins.PluginOrder {
return plugins.PluginOrder{
Position: plugins.OrderPositionOuter,
Operation: plugins.OrderOperationInsertLast,
}
}

func (p *plugin) Config() api.PluginConfig {
return &cors.CorsPolicy{}

Check warning on line 48 in controller/plugins/cors/config.go

View check run for this annotation

Codecov / codecov/patch

controller/plugins/cors/config.go#L47-L48

Added lines #L47 - L48 were not covered by tests
}

func (p *plugin) RouteConfigTypeURL() string {
return "type.googleapis.com/envoy.extensions.filters.http.cors.v3.CorsPolicy"
}

func (p *plugin) HTTPFilterConfigPlaceholder() map[string]interface{} {
return map[string]interface{}{
"typed_config": map[string]interface{}{
"@type": "type.googleapis.com/envoy.extensions.filters.http.cors.v3.Cors",
},
}
}
1 change: 1 addition & 0 deletions controller/plugins/plugins.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package plugins
import (
_ "mosn.io/htnn/controller/plugins/bandwidth_limit"
_ "mosn.io/htnn/controller/plugins/buffer"
_ "mosn.io/htnn/controller/plugins/cors"
_ "mosn.io/htnn/controller/plugins/fault"
_ "mosn.io/htnn/controller/plugins/local_ratelimit"
_ "mosn.io/htnn/controller/plugins/lua"
Expand Down
4 changes: 4 additions & 0 deletions e2e/pkg/suite/suite.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,10 @@ func (suite *Suite) Head(path string, header http.Header) (*http.Response, error
return suite.do("HEAD", path, header, nil)
}

func (suite *Suite) Options(path string, header http.Header) (*http.Response, error) {
return suite.do("OPTIONS", path, header, nil)
}

func (suite *Suite) Get(path string, header http.Header) (*http.Response, error) {
return suite.do("GET", path, header, nil)
}
Expand Down
46 changes: 46 additions & 0 deletions e2e/tests/cors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright The HTNN Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package tests

import (
"net/http"
"strings"
"testing"

"github.com/stretchr/testify/require"

"mosn.io/htnn/e2e/pkg/suite"
)

func init() {
suite.Register(suite.Test{
Manifests: []string{"base/httproute.yml"},
Run: func(t *testing.T, suite *suite.Suite) {
hdr := http.Header{}
origin := "http://x.default.local"
hdr.Set("Origin", origin)
meth := "POST"
hdr.Set("Access-Control-Request-Method", meth)
rsp, err := suite.Options("/echo", hdr)
require.NoError(t, err)
require.Equal(t, 200, rsp.StatusCode)
require.Equal(t, origin, rsp.Header.Get("Access-Control-Allow-Origin"))
require.Equal(t, meth, rsp.Header.Get("Access-Control-Allow-Methods"))
rsp, _ = suite.Post("/echo", hdr, strings.NewReader(""))
require.Equal(t, 200, rsp.StatusCode)
require.Equal(t, origin, rsp.Header.Get("Access-Control-Allow-Origin"))
},
})
}
16 changes: 16 additions & 0 deletions e2e/tests/cors.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
apiVersion: mosn.io/v1
kind: HTTPFilterPolicy
metadata:
name: policy
spec:
targetRef:
group: gateway.networking.k8s.io
kind: HTTPRoute
name: default
filters:
cors:
config:
allowOriginStringMatch:
- safeRegex:
regex: .*\.default\.local
allowMethods: POST
78 changes: 78 additions & 0 deletions site/content/en/docs/reference/plugins/cors.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
---
title: CORS
---

## Description

The `cors` plugin handles Cross-Origin Resource Sharing requests by leveraging Envoy's `cors` filter.

## Attribute

| | |
|-------|----------|
| Type | Security |
| Order | Outer |

## Configuration

See the corresponding [Envoy documentation](https://www.envoyproxy.io/docs/envoy/v1.28.0/configuration/http/http_filters/cors_filter).

## Usage

Assuming we have the following HTTPRoute attached to `localhost:10000`, with a backend server listening on port `8080`:

```yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: default
spec:
parentRefs:
- name: default
namespace: default
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: backend
port: 8080
```
By applying the configuration below, Cross-Origin Resource Sharing requests sent to `http://localhost:10000/` will be processed. If the `Origin` header of an OPTIONS request matches the regular expression `.*\.default\.local`, then the corresponding response will include the configured `Access-Control-Allow-*` response headers.

```yaml
apiVersion: mosn.io/v1
kind: HTTPFilterPolicy
metadata:
name: policy
spec:
targetRef:
group: gateway.networking.k8s.io
kind: HTTPRoute
name: default
filters:
cors:
config:
allowOriginStringMatch:
- safeRegex:
regex: ".*\.default\.local"
allowMethods: POST
```

Let's try it out:

```
$ curl -X OPTIONS http://localhost:10000/ -H "Origin: https://x.efault.local" -H "Access-Control-Request-Method: GET" -i
HTTP/1.1 200 OK
server: istio-envoy
...
$ curl -X OPTIONS http://localhost:10000/ -H "Origin: https://x.default.local" -H "Access-Control-Request-Method: GET" -i
HTTP/1.1 200 OK
access-control-allow-origin: https://x.default.local
access-control-allow-methods: POST
server: istio-envoy
...
```
78 changes: 78 additions & 0 deletions site/content/zh-hans/docs/reference/plugins/cors.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
---
title: CORS
---

## 说明

`cors` 插件通过利用 Envoy 的 `cors` 过滤器处理跨域资源共享的请求。

## 属性

| | |
|-------|----------|
| Type | Security |
| Order | Outer |

## 配置

请参阅相应的 [Envoy 文档](https://www.envoyproxy.io/docs/envoy/v1.28.0/configuration/http/http_filters/cors_filter)

## 用法

假设我们有下面附加到 `localhost:10000` 的 HTTPRoute,并且有一个后端服务器监听端口 `8080`

```yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: default
spec:
parentRefs:
- name: default
namespace: default
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: backend
port: 8080
```
通过应用下面的配置,发送到 `http://localhost:10000/` 的处理跨域资源共享的请求将会被处理。如果 OPTIONS 请求的请求头的 `Origin` 匹配到正则表达式 `.*\.default\.local`,那么对应的响应中会有配置的 `Access-Control-Allow-*` 响应头。

```yaml
apiVersion: mosn.io/v1
kind: HTTPFilterPolicy
metadata:
name: policy
spec:
targetRef:
group: gateway.networking.k8s.io
kind: HTTPRoute
name: default
filters:
cors:
config:
allowOriginStringMatch:
- safeRegex:
regex: ".*\.default\.local"
allowMethods: POST
```

让我们试一下:

```
$ curl -X OPTIONS http://localhost:10000/ -H "Origin: https://x.efault.local" -H "Access-Control-Request-Method: GET" -i
HTTP/1.1 200 OK
server: istio-envoy
...

$ curl -X OPTIONS http://localhost:10000/ -H "Origin: https://x.default.local" -H "Access-Control-Request-Method: GET" -i
HTTP/1.1 200 OK
access-control-allow-origin: https://x.default.local
access-control-allow-methods: POST
server: istio-envoy
...
```

0 comments on commit dd02e2c

Please sign in to comment.