You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by bz...@apache.org on 2022/03/14 01:25:29 UTC
[apisix-dashboard] branch master updated: feat: add security header (#2341)
This is an automated email from the ASF dual-hosted git repository.
bzp2010 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix-dashboard.git
The following commit(s) were added to refs/heads/master by this push:
new edca223 feat: add security header (#2341)
edca223 is described below
commit edca223f0169cbba24d62a56f2d39b01c97756b9
Author: Yu.Bozhong <y....@foxmail.com>
AuthorDate: Mon Mar 14 09:25:21 2022 +0800
feat: add security header (#2341)
Co-authored-by: 阿杰鲁 <im...@gmail.com>
---
api/conf/conf.yaml | 8 ++++++++
api/internal/conf/conf.go | 35 +++++++++++++++++++++++++++++++++++
api/internal/filter/cors.go | 33 ++++++++++++++++++++++++++++-----
api/test/shell/cli_test.sh | 26 ++++++++++++++++++++++++++
4 files changed, 97 insertions(+), 5 deletions(-)
diff --git a/api/conf/conf.yaml b/api/conf/conf.yaml
index 3090194..84e1f3a 100644
--- a/api/conf/conf.yaml
+++ b/api/conf/conf.yaml
@@ -60,6 +60,14 @@ conf:
# such as absolute path on Windows: winfile:///C:\access.log
# log example: 2020-12-09T16:38:09.039+0800 INFO filter/logging.go:46 /apisix/admin/routes/r1 {"status": 401, "host": "127.0.0.1:9000", "query": "asdfsafd=adf&a=a", "requestId": "3d50ecb8-758c-46d1-af5b-cd9d1c820156", "latency": 0, "remoteIP": "127.0.0.1", "method": "PUT", "errs": []}
max_cpu: 0 # supports tweaking with the number of OS threads are going to be used for parallelism. Default value: 0 [will use max number of available cpu cores considering hyperthreading (if any)]. If the value is negative, is will not touch the existing parallelism profile.
+ # security:
+ # access_control_allow_origin: "http://httpbin.org"
+ # access_control_allow_credentials: true # support using custom cors configration
+ # access_control_allow_headers: "Authorization"
+ # access_control-allow_methods: "*"
+ # x_frame_options: "deny"
+ # content_security_policy: ""default-src 'self'; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'""
+
authentication:
secret:
diff --git a/api/internal/conf/conf.go b/api/internal/conf/conf.go
index b0a7328..5087edf 100644
--- a/api/internal/conf/conf.go
+++ b/api/internal/conf/conf.go
@@ -61,6 +61,7 @@ var (
ImportSizeLimit = 10 * 1024 * 1024
AllowList []string
Plugins = map[string]bool{}
+ SecurityConf Security
)
type MTLS struct {
@@ -110,6 +111,7 @@ type Conf struct {
Log Log
AllowList []string `mapstructure:"allow_list"`
MaxCpu int `mapstructure:"max_cpu"`
+ Security Security
}
type User struct {
@@ -129,6 +131,15 @@ type Config struct {
Plugins []string
}
+type Security struct {
+ AllowCredentials string `mapstructure:"access_control_allow_credentials"`
+ AllowOrigin string `mapstructure:"access_control_allow_origin"`
+ AllowMethods string `mapstructure:"access_control-allow_methods"`
+ AllowHeaders string `mapstructure:"access_control_allow_headers"`
+ XFrameOptions string `mapstructure:"x_frame_options"`
+ ContentSecurityPolicy string `mapstructure:"content_security_policy"`
+}
+
// TODO: we should no longer use init() function after remove all handler's integration tests
// ENV=test is for integration tests only, other ENV should call "InitConf" explicitly
func init() {
@@ -246,6 +257,9 @@ func setupConfig() {
// set plugin
initPlugins(config.Plugins)
+
+ // security configuration
+ initSecurity(config.Conf.Security)
}
func setupEnv() {
@@ -316,3 +330,24 @@ func initParallelism(choiceCores int) {
}
runtime.GOMAXPROCS(choiceCores)
}
+
+// initialize security settings
+func initSecurity(conf Security) {
+ var se Security
+ // if conf == se, then conf is empty, we should use default value
+ if conf != se {
+ SecurityConf = conf
+ if conf.ContentSecurityPolicy == "" {
+ SecurityConf.ContentSecurityPolicy = "default-src 'self'; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'"
+ }
+ if conf.XFrameOptions == "" {
+ SecurityConf.XFrameOptions = "deny"
+ }
+ return
+ }
+
+ SecurityConf = Security{
+ XFrameOptions: "deny",
+ ContentSecurityPolicy: "default-src 'self'; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'",
+ }
+}
diff --git a/api/internal/filter/cors.go b/api/internal/filter/cors.go
index b33c62b..28ca331 100644
--- a/api/internal/filter/cors.go
+++ b/api/internal/filter/cors.go
@@ -16,14 +16,37 @@
*/
package filter
-import "github.com/gin-gonic/gin"
+import (
+ "github.com/gin-gonic/gin"
+
+ "github.com/apisix/manager-api/internal/conf"
+)
func CORS() gin.HandlerFunc {
return func(c *gin.Context) {
- c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
- c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
- c.Writer.Header().Set("Access-Control-Allow-Headers", "Authorization")
- c.Writer.Header().Set("Access-Control-Allow-Methods", "*")
+ if conf.SecurityConf.AllowOrigin != "" {
+ c.Writer.Header().Set("Access-Control-Allow-Origin", conf.SecurityConf.AllowOrigin)
+ }
+
+ if conf.SecurityConf.AllowHeaders != "" {
+ c.Writer.Header().Set("Access-Control-Allow-Headers", conf.SecurityConf.AllowHeaders)
+ }
+
+ if conf.SecurityConf.AllowMethods != "" {
+ c.Writer.Header().Set("Access-Control-Allow-Methods", conf.SecurityConf.AllowMethods)
+ }
+
+ if conf.SecurityConf.AllowCredentials != "" {
+ c.Writer.Header().Set("Access-Control-Allow-Credentials", conf.SecurityConf.AllowCredentials)
+ }
+
+ if conf.SecurityConf.XFrameOptions != "" {
+ c.Writer.Header().Set("X-Frame-Options", conf.SecurityConf.XFrameOptions)
+ }
+
+ if conf.SecurityConf.ContentSecurityPolicy != "" {
+ c.Writer.Header().Set("Content-Security-Policy", conf.SecurityConf.ContentSecurityPolicy)
+ }
if c.Request.Method == "OPTIONS" {
c.AbortWithStatus(204)
return
diff --git a/api/test/shell/cli_test.sh b/api/test/shell/cli_test.sh
index be1d3a8..d41f270 100755
--- a/api/test/shell/cli_test.sh
+++ b/api/test/shell/cli_test.sh
@@ -454,6 +454,32 @@ stop_dashboard() {
recover_service_file
}
+#15
+@test "Check Security configuration" {
+ recover_conf
+
+ start_dashboard 3
+
+ # check response header without custom header
+ run curl -i http://127.0.0.1:9000
+
+ [ $(echo "$output" | grep -c "X-Frame-Options: deny") -eq '1' ]
+
+ stop_dashboard 6
+
+ sed -i 's@# security:@security:@' ${CONF_FILE}
+ sed -i 's@# x_frame_options: "deny"@ x_frame_options: "test"@' ${CONF_FILE}
+
+ start_dashboard 3
+
+ # check response header with custom header
+ run curl -i http://127.0.0.1:9000
+
+[ $(echo "$output" | grep -c "X-Frame-Options: test") -eq '1' ]
+
+ stop_dashboard 6
+}
+
#post
@test "Clean test environment" {
# kill etcd