From f44863d7948555d04d4db8ea34a6f59789a40956 Mon Sep 17 00:00:00 2001 From: nic-chen Date: Sun, 25 Apr 2021 08:19:35 +0800 Subject: [PATCH 1/8] feat: support HTPS for Manager API --- api/cmd/managerapi.go | 18 ++++++++++++++++++ api/conf/conf.yaml | 5 +++++ api/internal/conf/conf.go | 22 +++++++++++++++++++++- api/test/shell/cli_test.sh | 29 +++++++++++++++++++++++++++++ 4 files changed, 73 insertions(+), 1 deletion(-) diff --git a/api/cmd/managerapi.go b/api/cmd/managerapi.go index 6ea6c91fe6..68292948fd 100644 --- a/api/cmd/managerapi.go +++ b/api/cmd/managerapi.go @@ -124,6 +124,24 @@ func NewManagerAPICommand() *cobra.Command { } }() + // HTTPS + if conf.SSLCert != "" && conf.SSLKey != "" { + addrSSL := fmt.Sprintf("%s:%d", conf.ServerHost, conf.SSLPort) + serverSSL := &http.Server{ + Addr: addrSSL, + Handler: r, + ReadTimeout: time.Duration(1000) * time.Millisecond, + WriteTimeout: time.Duration(5000) * time.Millisecond, + } + go func() { + err := serverSSL.ListenAndServeTLS(conf.SSLCert, conf.SSLKey) + if err != nil && err != http.ErrServerClosed { + utils.CloseAll() + log.Fatalf("listen and serv fail: %s", err) + } + }() + } + printInfo() sig := <-quit diff --git a/api/conf/conf.yaml b/api/conf/conf.yaml index 8d4414b1a8..555883b662 100644 --- a/api/conf/conf.yaml +++ b/api/conf/conf.yaml @@ -21,6 +21,11 @@ conf: # The default value is 0.0.0.0, if want to specify, please enable it. # This value accepts IPv4, IPv6, and hostname. port: 9000 # The port on which the `Manager API` should listen. + # ssl: + # port: 9001 # The port on which the `Manager API` should listen for HTTPS. + # cert: "/tmp/cert/example.crt" # Path of your SSL cert. + # key: "/tmp/cert/example.key" # Path of your SSL key. + allow_list: # If we don't set any IP list, then any IP access is allowed by default. - 127.0.0.1 # The rules are checked in sequence until the first match is found. - ::1 # In this example, access is allowed only for IPv4 network 127.0.0.1, and for IPv6 network ::1. diff --git a/api/internal/conf/conf.go b/api/internal/conf/conf.go index 40af0072a6..9e2e5931ad 100644 --- a/api/internal/conf/conf.go +++ b/api/internal/conf/conf.go @@ -46,6 +46,9 @@ var ( WorkDir = "." ServerHost = "0.0.0.0" ServerPort = 80 + SSLPort = 443 + SSLCert string + SSLKey string ETCDConfig *Etcd ErrorLogLevel = "warn" ErrorLogPath = "logs/error.log" @@ -73,6 +76,12 @@ type Etcd struct { Prefix string } +type SSL struct { + Port int + Cert string `yaml:"cert"` + Key string `yaml:"key"` +} + type Listen struct { Host string Port int @@ -95,6 +104,7 @@ type Log struct { type Conf struct { Etcd Etcd Listen Listen + SSL SSL Log Log AllowList []string `yaml:"allow_list"` MaxCpu int `yaml:"max_cpu"` @@ -152,11 +162,21 @@ func setConf() { if config.Conf.Listen.Port != 0 { ServerPort = config.Conf.Listen.Port } - if config.Conf.Listen.Host != "" { ServerHost = config.Conf.Listen.Host } + // SSL + if config.Conf.SSL.Port != 0 { + SSLPort = config.Conf.SSL.Port + } + if config.Conf.SSL.Cert != "" { + SSLCert = config.Conf.SSL.Cert + } + if config.Conf.SSL.Key != "" { + SSLKey = config.Conf.SSL.Key + } + // for etcd if len(config.Conf.Etcd.Endpoints) > 0 { initEtcdConfig(config.Conf.Etcd) diff --git a/api/test/shell/cli_test.sh b/api/test/shell/cli_test.sh index d1c3b90831..332d4b15fe 100755 --- a/api/test/shell/cli_test.sh +++ b/api/test/shell/cli_test.sh @@ -273,6 +273,35 @@ sleep 6 clean_up +# HTTPS test +currentDir=$(pwd) +if [[ $KERNEL = "Darwin" ]]; then + sed -i "" 's@# ssl:@ssl:@' conf/conf.yaml + sed -i "" 's@# port: 9001@ port: 9001@' conf/conf.yaml + sed -i "" "s@# cert: \"/tmp/cert/example.crt\"@ cert: \"$currentDir/test/certs/test2.crt\"@" conf/conf.yaml + sed -i "" "s@# cert: \"/tmp/cert/example.key\"@ cert: \"$currentDir/test/certs/test2.key\"@" conf/conf.yaml +else + sed -i 's@# ssl:@ssl:@' conf/conf.yaml + sed -i 's@# port: 9001@ port: 9001@' conf/conf.yaml + sed -i "s@# cert: \"/tmp/cert/example.crt\"@ cert: \"$currentDir/test/certs/test2.crt\"@" conf/conf.yaml + sed -i "s@# cert: \"/tmp/cert/example.key\"@ cert: \"$currentDir/test/certs/test2.key\"@" conf/conf.yaml +fi + +./manager-api & +sleep 3 + +# access by HTTPS +code=$(curl -k -i -m 20 -o /dev/null -s -w %{http_code} --resolve 'www.test2.com:9001:127.0.0.1' http://www.test2.com:9001) +if [ ! $code -eq 200 ]; then + echo "failed: verify HTTPS failed" + exit 1 +fi + +./manager-api stop +sleep 6 +clean_up + + # etcd basic auth # add root user curl -L http://localhost:2379/v3/auth/user/add -d '{"name": "root", "password": "root"}' From 5c6ccb49b9d04cf7480c50087a7edaf87c8d2188 Mon Sep 17 00:00:00 2001 From: nic-chen Date: Mon, 26 Apr 2021 09:50:10 +0800 Subject: [PATCH 2/8] fix: error --- api/test/shell/cli_test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/test/shell/cli_test.sh b/api/test/shell/cli_test.sh index 332d4b15fe..ffe6095136 100755 --- a/api/test/shell/cli_test.sh +++ b/api/test/shell/cli_test.sh @@ -291,7 +291,7 @@ fi sleep 3 # access by HTTPS -code=$(curl -k -i -m 20 -o /dev/null -s -w %{http_code} --resolve 'www.test2.com:9001:127.0.0.1' http://www.test2.com:9001) +code=$(curl -k -i -m 20 -o /dev/null -s -w %{http_code} --resolve 'www.test2.com:9001:127.0.0.1' https://www.test2.com:9001) if [ ! $code -eq 200 ]; then echo "failed: verify HTTPS failed" exit 1 From 1e7bef89c37878162d58bfdf1a8c2ca01711cf00 Mon Sep 17 00:00:00 2001 From: nic-chen Date: Mon, 26 Apr 2021 14:14:54 +0800 Subject: [PATCH 3/8] fix: cli test failed --- api/test/shell/cli_test.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/test/shell/cli_test.sh b/api/test/shell/cli_test.sh index ffe6095136..17c8e43e3f 100755 --- a/api/test/shell/cli_test.sh +++ b/api/test/shell/cli_test.sh @@ -279,12 +279,12 @@ if [[ $KERNEL = "Darwin" ]]; then sed -i "" 's@# ssl:@ssl:@' conf/conf.yaml sed -i "" 's@# port: 9001@ port: 9001@' conf/conf.yaml sed -i "" "s@# cert: \"/tmp/cert/example.crt\"@ cert: \"$currentDir/test/certs/test2.crt\"@" conf/conf.yaml - sed -i "" "s@# cert: \"/tmp/cert/example.key\"@ cert: \"$currentDir/test/certs/test2.key\"@" conf/conf.yaml + sed -i "" "s@# key: \"/tmp/cert/example.key\"@ cert: \"$currentDir/test/certs/test2.key\"@" conf/conf.yaml else sed -i 's@# ssl:@ssl:@' conf/conf.yaml sed -i 's@# port: 9001@ port: 9001@' conf/conf.yaml sed -i "s@# cert: \"/tmp/cert/example.crt\"@ cert: \"$currentDir/test/certs/test2.crt\"@" conf/conf.yaml - sed -i "s@# cert: \"/tmp/cert/example.key\"@ cert: \"$currentDir/test/certs/test2.key\"@" conf/conf.yaml + sed -i "s@# key: \"/tmp/cert/example.key\"@ key: \"$currentDir/test/certs/test2.key\"@" conf/conf.yaml fi ./manager-api & From 216f0aad39b5d91f33f43e7ea939d5ad1e01c8a5 Mon Sep 17 00:00:00 2001 From: nic-chen Date: Mon, 26 Apr 2021 14:29:24 +0800 Subject: [PATCH 4/8] fix: cli test failed --- api/test/shell/cli_test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/test/shell/cli_test.sh b/api/test/shell/cli_test.sh index 17c8e43e3f..3adb01e0f0 100755 --- a/api/test/shell/cli_test.sh +++ b/api/test/shell/cli_test.sh @@ -291,7 +291,7 @@ fi sleep 3 # access by HTTPS -code=$(curl -k -i -m 20 -o /dev/null -s -w %{http_code} --resolve 'www.test2.com:9001:127.0.0.1' https://www.test2.com:9001) +code=$(curl -k -i -m 20 -o /dev/null -s -w %{http_code} --resolve 'www.test2.com:9001:127.0.0.1' https://www.test2.com:9001/apisix/admin/tool/version) if [ ! $code -eq 200 ]; then echo "failed: verify HTTPS failed" exit 1 From ba1808f720de7c461368ec60af31b3f476f3572c Mon Sep 17 00:00:00 2001 From: nic-chen Date: Mon, 26 Apr 2021 16:39:03 +0800 Subject: [PATCH 5/8] fix: review --- api/cmd/managerapi.go | 9 +++++++++ api/conf/conf.yaml | 8 ++++++-- api/internal/conf/conf.go | 4 +++- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/api/cmd/managerapi.go b/api/cmd/managerapi.go index 68292948fd..28ab660ebc 100644 --- a/api/cmd/managerapi.go +++ b/api/cmd/managerapi.go @@ -18,6 +18,7 @@ package cmd import ( "context" + "crypto/tls" "fmt" "net/http" "os" @@ -48,6 +49,9 @@ func printInfo() { fmt.Fprint(os.Stdout, "The manager-api is running successfully!\n\n") printVersion() fmt.Fprintf(os.Stdout, "%-8s: %s:%d\n", "Listen", conf.ServerHost, conf.ServerPort) + if conf.SSLCert != "" && conf.SSLKey != "" { + fmt.Fprintf(os.Stdout, "%-8s: %s:%d\n", "HTTPS Listen", conf.SSLHost, conf.SSLPort) + } fmt.Fprintf(os.Stdout, "%-8s: %s\n", "Loglevel", conf.ErrorLogLevel) fmt.Fprintf(os.Stdout, "%-8s: %s\n\n", "Logfile", conf.ErrorLogPath) } @@ -132,6 +136,11 @@ func NewManagerAPICommand() *cobra.Command { Handler: r, ReadTimeout: time.Duration(1000) * time.Millisecond, WriteTimeout: time.Duration(5000) * time.Millisecond, + TLSConfig: &tls.Config{ + // Causes servers to use Go's default ciphersuite preferences, + // which are tuned to avoid attacks. Does nothing on clients. + PreferServerCipherSuites: true, + }, } go func() { err := serverSSL.ListenAndServeTLS(conf.SSLCert, conf.SSLKey) diff --git a/api/conf/conf.yaml b/api/conf/conf.yaml index 555883b662..f2425cf35d 100644 --- a/api/conf/conf.yaml +++ b/api/conf/conf.yaml @@ -15,14 +15,18 @@ # limitations under the License. # +# yamllint disable rule:comments-indentation conf: - listen: # yamllint disable rule:comments-indentation + listen: # host: 127.0.0.1 # the address on which the `Manager API` should listen. # The default value is 0.0.0.0, if want to specify, please enable it. # This value accepts IPv4, IPv6, and hostname. port: 9000 # The port on which the `Manager API` should listen. + # ssl: - # port: 9001 # The port on which the `Manager API` should listen for HTTPS. + # host: 127.0.0.1 # the address on which the `Manager API` should listen for HTTPS. + # The default value is 0.0.0.0, if want to specify, please enable it. + # port: 9001 # The port on which the `Manager API` should listen for HTTPS. # cert: "/tmp/cert/example.crt" # Path of your SSL cert. # key: "/tmp/cert/example.key" # Path of your SSL key. diff --git a/api/internal/conf/conf.go b/api/internal/conf/conf.go index 9e2e5931ad..bb9e93c2c9 100644 --- a/api/internal/conf/conf.go +++ b/api/internal/conf/conf.go @@ -46,6 +46,7 @@ var ( WorkDir = "." ServerHost = "0.0.0.0" ServerPort = 80 + SSLHost = "0.0.0.0" SSLPort = 443 SSLCert string SSLKey string @@ -77,7 +78,8 @@ type Etcd struct { } type SSL struct { - Port int + Host string `yaml:"host"` + Port int `yaml:"port"` Cert string `yaml:"cert"` Key string `yaml:"key"` } From dbdef86a501cd1727916d7022acd9e735a5b6be4 Mon Sep 17 00:00:00 2001 From: nic-chen Date: Tue, 27 Apr 2021 18:03:03 +0800 Subject: [PATCH 6/8] fix: code code optimization --- api/cmd/managerapi.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/api/cmd/managerapi.go b/api/cmd/managerapi.go index 28ab660ebc..a1be776391 100644 --- a/api/cmd/managerapi.go +++ b/api/cmd/managerapi.go @@ -20,9 +20,11 @@ import ( "context" "crypto/tls" "fmt" + "net" "net/http" "os" "os/signal" + "strconv" "syscall" "time" @@ -130,7 +132,7 @@ func NewManagerAPICommand() *cobra.Command { // HTTPS if conf.SSLCert != "" && conf.SSLKey != "" { - addrSSL := fmt.Sprintf("%s:%d", conf.ServerHost, conf.SSLPort) + addrSSL := net.JoinHostPort(conf.ServerHost, strconv.Itoa(conf.SSLPort)) serverSSL := &http.Server{ Addr: addrSSL, Handler: r, From 651cfe3c9d82b8d107b60c5b8e336b66cb736710 Mon Sep 17 00:00:00 2001 From: nic-chen Date: Wed, 28 Apr 2021 17:43:24 +0800 Subject: [PATCH 7/8] fix review --- api/cmd/managerapi.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/cmd/managerapi.go b/api/cmd/managerapi.go index a1be776391..97f60ae16b 100644 --- a/api/cmd/managerapi.go +++ b/api/cmd/managerapi.go @@ -148,7 +148,7 @@ func NewManagerAPICommand() *cobra.Command { err := serverSSL.ListenAndServeTLS(conf.SSLCert, conf.SSLKey) if err != nil && err != http.ErrServerClosed { utils.CloseAll() - log.Fatalf("listen and serv fail: %s", err) + log.Fatalf("listen and serve for HTTPS failed: %s", err) } }() } From 356ca4cc097ff1339b4c4d9e622c31edcae7a25d Mon Sep 17 00:00:00 2001 From: nic-chen Date: Thu, 29 Apr 2021 23:27:31 +0800 Subject: [PATCH 8/8] fix CI --- .github/workflows/test-frontend-multiple-node-build.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.github/workflows/test-frontend-multiple-node-build.yml b/.github/workflows/test-frontend-multiple-node-build.yml index 3301c7e8d2..33daefca4f 100644 --- a/.github/workflows/test-frontend-multiple-node-build.yml +++ b/.github/workflows/test-frontend-multiple-node-build.yml @@ -8,15 +8,9 @@ on: push: branches: - master - paths-ignore: - - 'docs/**' - - 'api/**' pull_request: branches: - master - paths-ignore: - - 'docs/**' - - 'api/**' # A workflow run is made up of one or more jobs that can run sequentially or in parallel jobs: