This is an automated email from the ASF dual-hosted git repository.

spacewander pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix.git


The following commit(s) were added to refs/heads/master by this push:
     new 82c23495f feat: add GM support (#8389)
82c23495f is described below

commit 82c23495fc0de7814b8b81138487eebdfa09cef9
Author: 罗泽轩 <[email protected]>
AuthorDate: Thu Nov 24 16:13:28 2022 +0800

    feat: add GM support (#8389)
    
    Co-authored-by: Sylvia <[email protected]>
---
 .github/workflows/gm.yml     | 114 ++++++++++++++++++++++++++
 apisix/plugins/gm.lua        | 175 +++++++++++++++++++++++++++++++++++++++
 conf/config-default.yaml     |   1 +
 docs/zh/latest/config.json   |   3 +-
 docs/zh/latest/plugins/gm.md | 191 +++++++++++++++++++++++++++++++++++++++++++
 t/certs/client_enc.crt       |  12 +++
 t/certs/client_enc.key       |   5 ++
 t/certs/client_sign.crt      |  12 +++
 t/certs/client_sign.key      |   5 ++
 t/certs/gm_ca.crt            |  26 ++++++
 t/certs/server_enc.crt       |  12 +++
 t/certs/server_enc.key       |   5 ++
 t/certs/server_sign.crt      |  12 +++
 t/certs/server_sign.key      |   5 ++
 t/gm/gm.t                    | 170 ++++++++++++++++++++++++++++++++++++++
 15 files changed, 747 insertions(+), 1 deletion(-)

diff --git a/.github/workflows/gm.yml b/.github/workflows/gm.yml
new file mode 100644
index 000000000..cb1444e0d
--- /dev/null
+++ b/.github/workflows/gm.yml
@@ -0,0 +1,114 @@
+name: CI GM
+
+on:
+  push:
+    branches: [master]
+    paths-ignore:
+      - 'docs/**'
+      - '**/*.md'
+  pull_request:
+    branches: [master]
+    paths-ignore:
+      - 'docs/**'
+      - '**/*.md'
+
+concurrency:
+  group: ${{ github.workflow }}-${{ github.ref == 'refs/heads/master' && 
github.run_number || github.ref }}
+  cancel-in-progress: true
+
+permissions:
+  contents: read
+
+jobs:
+  build:
+    strategy:
+      fail-fast: false
+      matrix:
+        platform:
+          - ubuntu-20.04
+        os_name:
+          - linux_openresty
+        test_dir:
+          # TODO: cover all tests by adding a CI cron job
+          - t/gm
+
+    runs-on: ${{ matrix.platform }}
+    timeout-minutes: 90
+    env:
+      SERVER_NAME: ${{ matrix.os_name }}
+      OPENRESTY_VERSION: default
+
+    steps:
+      - name: Check out code
+        uses: actions/[email protected]
+        with:
+          submodules: recursive
+
+      - name: Cache deps
+        uses: actions/cache@v3
+        env:
+          cache-name: cache-deps
+        with:
+          path: deps
+          key: ${{ runner.os }}-${{ env.cache-name }}-${{ matrix.os_name 
}}-${{ hashFiles('rockspec/apisix-master-0.rockspec') }}
+
+      - name: Cache Tongsuo compilation
+        id: cache-tongsuo
+        uses: actions/cache@v3
+        env:
+          cache-name: cache-tongsuo
+        with:
+          path: ./tongsuo
+          # TODO: use a fixed release once they have created one.
+          # See https://github.com/Tongsuo-Project/Tongsuo/issues/318
+          key: ${{ runner.os }}-${{ env.cache-name }}-${{ matrix.os_name 
}}-tongsuo-ver
+
+      - name: Compile SSL lib
+        shell: bash
+        if: steps.cache-tongsuo.outputs.cache-hit != 'true'
+        run: |
+          git clone https://github.com/api7/tongsuo --depth 1
+          pushd tongsuo
+          # build binary
+          ./config enable-ntls -static
+          make -j2
+          mv apps/openssl apps/static-openssl
+          ./config shared enable-ntls -g --prefix=/usr/local/tongsuo
+          make -j2
+          popd
+
+      - name: Install SSL lib
+        run: |
+          pushd tongsuo
+          sudo make install_sw
+          sudo cp apps/static-openssl /usr/local/tongsuo/bin/openssl
+          export PATH=/usr/local/tongsuo/bin:$PATH
+          openssl version
+          popd
+
+      - name: Linux launch common services
+        run: |
+          make ci-env-up project_compose_ci=ci/pod/docker-compose.common.yml
+
+      - name: Linux Before install
+        run: |
+          sudo ./ci/${{ matrix.os_name }}_runner.sh before_install
+
+      - name: Linux Do install
+        run: |
+          export OR_PREFIX=/usr/local/openresty-debug
+          export openssl_prefix=/usr/local/tongsuo
+          export zlib_prefix=$OR_PREFIX/zlib
+          export pcre_prefix=$OR_PREFIX/pcre
+
+          export cc_opt="-DNGX_LUA_ABORT_AT_PANIC -I${zlib_prefix}/include 
-I${pcre_prefix}/include -I${openssl_prefix}/include"
+          export ld_opt="-L${zlib_prefix}/lib -L${pcre_prefix}/lib 
-L${openssl_prefix}/lib64 
-Wl,-rpath,${zlib_prefix}/lib:${pcre_prefix}/lib:${openssl_prefix}/lib64"
+          sudo --preserve-env=OPENRESTY_VERSION \
+            --preserve-env=cc_opt,ld_opt \
+            ./ci/${{ matrix.os_name }}_runner.sh do_install
+
+      - name: Linux Script
+        env:
+          TEST_FILE_SUB_DIR: ${{ matrix.test_dir }}
+        run: |
+          sudo -E ./ci/${{ matrix.os_name }}_runner.sh script
diff --git a/apisix/plugins/gm.lua b/apisix/plugins/gm.lua
new file mode 100644
index 000000000..929dac6f0
--- /dev/null
+++ b/apisix/plugins/gm.lua
@@ -0,0 +1,175 @@
+-- Licensed to the Apache Software Foundation (ASF) under one
+-- or more contributor license agreements.  See the NOTICE file
+-- distributed with this work for additional information
+-- regarding copyright ownership.  The ASF licenses this file
+-- to you 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.
+
+-- local common libs
+local require = require
+local pcall = pcall
+local ffi = require("ffi")
+local C = ffi.C
+local get_request = require("resty.core.base").get_request
+local core = require("apisix.core")
+local radixtree_sni = require("apisix.ssl.router.radixtree_sni")
+local apisix_ssl = require("apisix.ssl")
+local _, ssl = pcall(require, "resty.apisix.ssl")
+local error = error
+
+
+ffi.cdef[[
+unsigned long Tongsuo_version_num(void)
+]]
+
+
+-- local function
+local function set_pem_ssl_key(sni, enc_cert, enc_pkey, sign_cert, sign_pkey)
+    local r = get_request()
+    if r == nil then
+        return false, "no request found"
+    end
+
+    local parsed_enc_cert, err = apisix_ssl.fetch_cert(sni, enc_cert)
+    if not parsed_enc_cert then
+        return false, "failed to parse enc PEM cert: " .. err
+    end
+
+    local parsed_sign_cert, err = apisix_ssl.fetch_cert(sni, sign_cert)
+    if not parsed_sign_cert then
+        return false, "failed to parse sign PEM cert: " .. err
+    end
+
+    local ok, err = ssl.set_gm_cert(parsed_enc_cert, parsed_sign_cert)
+    if not ok then
+        return false, "failed to set PEM cert: " .. err
+    end
+
+    local parsed_enc_pkey, err = apisix_ssl.fetch_pkey(sni, enc_pkey)
+    if not parsed_enc_pkey then
+        return false, "failed to parse enc PEM priv key: " .. err
+    end
+
+    local parsed_sign_pkey, err = apisix_ssl.fetch_pkey(sni, sign_pkey)
+    if not parsed_sign_pkey then
+        return false, "failed to parse sign PEM priv key: " .. err
+    end
+
+    ok, err = ssl.set_gm_priv_key(parsed_enc_pkey, parsed_sign_pkey)
+    if not ok then
+        return false, "failed to set PEM priv key: " .. err
+    end
+
+    return true
+end
+
+
+local original_set_cert_and_key
+local function set_cert_and_key(sni, value)
+    if value.gm then
+        -- process as GM certificate
+        -- For GM dual certificate, the `cert` and `key` will be encryption 
cert/key.
+        -- The first item in `certs` and `keys` will be sign cert/key.
+        local enc_cert = value.cert
+        local enc_pkey = value.key
+        local sign_cert = value.certs[1]
+        local sign_pkey = value.keys[1]
+        return set_pem_ssl_key(sni, enc_cert, enc_pkey, sign_cert, sign_pkey)
+    end
+    return original_set_cert_and_key(sni, value)
+end
+
+
+local original_check_ssl_conf
+local function check_ssl_conf(in_dp, conf)
+    if conf.gm then
+        -- process as GM certificate
+        -- For GM dual certificate, the `cert` and `key` will be encryption 
cert/key.
+        -- The first item in `certs` and `keys` will be sign cert/key.
+        local ok, err = original_check_ssl_conf(in_dp, conf)
+        -- check cert/key first in the original method
+        if not ok then
+            return nil, err
+        end
+
+        -- Currently, APISIX doesn't check the cert type (ECDSA / RSA). So we 
skip the
+        -- check for now in this plugin.
+        local num_certs = conf.certs and #conf.certs or 0
+        local num_keys = conf.keys and #conf.keys or 0
+        if num_certs ~= 1 or num_keys ~= 1 then
+            return nil, "sign cert/key are required"
+        end
+        return true
+    end
+    return original_check_ssl_conf(in_dp, conf)
+end
+
+
+-- module define
+local plugin_name = "gm"
+
+-- plugin schema
+local plugin_schema = {
+    type = "object",
+    properties = {
+    },
+}
+
+local _M = {
+    version  = 0.1,            -- plugin version
+    priority = -43,
+    name     = plugin_name,    -- plugin name
+    schema   = plugin_schema,  -- plugin schema
+}
+
+
+function _M.init()
+    if not pcall(function () return C.Tongsuo_version_num end) then
+        error("need to build Tongsuo 
(https://github.com/Tongsuo-Project/Tongsuo) " ..
+              "into the APISIX-Base")
+    end
+
+    ssl.enable_ntls()
+    original_set_cert_and_key = radixtree_sni.set_cert_and_key
+    radixtree_sni.set_cert_and_key = set_cert_and_key
+    original_check_ssl_conf = apisix_ssl.check_ssl_conf
+    apisix_ssl.check_ssl_conf = check_ssl_conf
+
+    if core.schema.ssl.properties.gm ~= nil then
+        error("Field 'gm' is occupied")
+    end
+
+    -- inject a mark to distinguish GM certificate
+    core.schema.ssl.properties.gm = {
+        type = "boolean"
+    }
+end
+
+
+function _M.destroy()
+    ssl.disable_ntls()
+    radixtree_sni.set_cert_and_key = original_set_cert_and_key
+    apisix_ssl.check_ssl_conf = original_check_ssl_conf
+    core.schema.ssl.properties.gm = nil
+end
+
+-- module interface for schema check
+-- @param `conf` user defined conf data
+-- @param `schema_type` defined in `apisix/core/schema.lua`
+-- @return <boolean>
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(plugin_schema, conf)
+end
+
+
+return _M
diff --git a/conf/config-default.yaml b/conf/config-default.yaml
index 6e714b577..6f9de38bd 100755
--- a/conf/config-default.yaml
+++ b/conf/config-default.yaml
@@ -464,6 +464,7 @@ plugins:                          # plugin list (sorted by 
priority)
   #- log-rotate                    # priority: 100
   # <- recommend to use priority (0, 100) for your custom plugins
   - example-plugin                 # priority: 0
+  #- gm                            # priority: -43
   - aws-lambda                     # priority: -1899
   - azure-functions                # priority: -1900
   - openwhisk                      # priority: -1901
diff --git a/docs/zh/latest/config.json b/docs/zh/latest/config.json
index 2657f45ce..8f2f3efe8 100644
--- a/docs/zh/latest/config.json
+++ b/docs/zh/latest/config.json
@@ -99,7 +99,8 @@
             "plugins/referer-restriction",
             "plugins/consumer-restriction",
             "plugins/csrf",
-            "plugins/public-api"
+            "plugins/public-api",
+            "plugins/gm"
           ]
         },
         {
diff --git a/docs/zh/latest/plugins/gm.md b/docs/zh/latest/plugins/gm.md
new file mode 100644
index 000000000..75dd8e072
--- /dev/null
+++ b/docs/zh/latest/plugins/gm.md
@@ -0,0 +1,191 @@
+---
+title: GM
+keywords:
+  - APISIX
+  - Plugin
+  - GM
+description: 本文介绍了关于 Apache APISIX gm 插件的基本信息及使用方法。
+---
+
+<!--
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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.
+#
+-->
+
+## 描述
+
+`gm` 插件能启用国密相关的功能。目前支持通过该插件动态配置国密双证书。
+
+:::note 相关介绍
+国密就是国产化的密码算法。在我们日常开发过程中会接触到各种各样的密码算法,如 RSA、SHA256 
等等。为了达到更高的安全等级,许多大公司和国家会制定自己的密码算法。国密就是这样一组由中国国家密码管理局制定的密码算法。在国际形势越发复杂多变的今天,密码算法的国产化替代,在一些领域已经成为了一股势不可挡的潮流。
+:::
+
+## 启用插件
+
+**该插件要求 Apache APISIX 运行在编译了 Tongsuo 的 APISIX-Base 上。**
+
+首先,我们需要安装 Tongsuo(此处我们选择编译出 Tongsuo 的动态链接库):
+
+```
+# TODO: use a fixed release once they have created one.
+# See https://github.com/Tongsuo-Project/Tongsuo/issues/318
+git clone https://github.com/api7/tongsuo --depth 1
+pushd tongsuo
+./config shared enable-ntls -g --prefix=/usr/local/tongsuo
+make -j2
+sudo make install_sw
+```
+
+其次,我们需要构建 APISIX-Base,让它使用 Tongsuo 作为 SSL 库:
+
+```
+export OR_PREFIX=/usr/local/openresty
+export openssl_prefix=/usr/local/tongsuo
+export zlib_prefix=$OR_PREFIX/zlib
+export pcre_prefix=$OR_PREFIX/pcre
+
+export cc_opt="-DNGX_LUA_ABORT_AT_PANIC -I${zlib_prefix}/include 
-I${pcre_prefix}/include -I${openssl_prefix}/include"
+export ld_opt="-L${zlib_prefix}/lib -L${pcre_prefix}/lib 
-L${openssl_prefix}/lib64 
-Wl,-rpath,${zlib_prefix}/lib:${pcre_prefix}/lib:${openssl_prefix}/lib64"
+./build-apisix-base.sh
+```
+
+该插件默认是禁用状态,你需要将其添加到配置文件(`./conf/config.yaml`)中才可以启用它:
+
+```yaml
+plugins:
+  - ...
+  - gm
+```
+
+由于 APISIX 的默认 cipher 中不包含国密 cipher,所以我们还需要在配置文件(`./conf/config.yaml`)中设置 
cipher:
+
+```yaml
+apisix:
+  ...
+  ssl:
+    ...
+    # 可按实际情况调整。错误的 cipher 会导致 “no shared cipher” 或 “no ciphers available” 报错。
+    ssl_ciphers: HIGH:!aNULL:!MD5
+
+```
+
+配置完成后,重新加载 APISIX,此时 APISIX 将会启用国密相关的逻辑。
+
+## 测试插件
+
+在测试插件之前,我们需要准备好国密双证书。Tongsuo 提供了生成[SM2 
双证书](https://www.yuque.com/tsdoc/ts/sulazb)的教程。
+
+在下面的例子中,我们将用到如下的证书:
+
+```
+# 客户端加密证书和密钥
+t/certs/client_enc.crt
+t/certs/client_enc.key
+# 客户端签名证书和密钥
+t/certs/client_sign.crt
+t/certs/client_sign.key
+# CA 和中间 CA 打包在一起的文件,用于设置受信任的 CA
+t/certs/gm_ca.crt
+# 服务端加密证书和密钥
+t/certs/server_enc.crt
+t/certs/server_enc.key
+# 服务端签名证书和密钥
+t/certs/server_sign.crt
+t/certs/server_sign.key
+```
+
+此外,我们还需要准备 Tongsuo 命令行工具。
+
+```
+./config enable-ntls -static
+make -j2
+# 生成的命令行工具在 apps 目录下
+mv apps/openssl ..
+```
+
+你也可以采用非静态编译的方式,不过就需要根据具体环境,自己解决动态链接库的路径问题了。
+
+以下示例展示了如何在指定域名中启用 `gm` 插件:
+
+创建对应的 SSL 对象:
+
+```python
+#!/usr/bin/env python
+# coding: utf-8
+
+import sys
+# sudo pip install requests
+import requests
+
+if len(sys.argv) <= 3:
+    print("bad argument")
+    sys.exit(1)
+with open(sys.argv[1]) as f:
+    enc_cert = f.read()
+with open(sys.argv[2]) as f:
+    enc_key = f.read()
+with open(sys.argv[3]) as f:
+    sign_cert = f.read()
+with open(sys.argv[4]) as f:
+    sign_key = f.read()
+api_key = "edd1c9f034335f136f87ad84b625c8f1"
+resp = requests.put("http://127.0.0.1:9180/apisix/admin/ssls/1";, json={
+    "cert": enc_cert,
+    "key": enc_key,
+    "certs": [sign_cert],
+    "keys": [sign_key],
+    "gm": True,
+    "snis": ["localhost"],
+}, headers={
+    "X-API-KEY": api_key,
+})
+print(resp.status_code)
+print(resp.text)
+```
+
+将上面的脚本保存为 `./create_gm_ssl.py`,运行:
+
+```shell
+./create_gm_ssl.py t/certs/server_enc.crt  t/certs/server_enc.key 
t/certs/server_sign.crt t/certs/server_sign.key
+```
+
+输出结果如下所示:
+
+```
+200
+{"key":"\/apisix\/ssls\/1","value":{"keys":["Yn...
+```
+
+完成上述准备后,可以使用如下命令测试插件是否启用成功:
+
+```shell
+./openssl s_client -connect localhost:9443 -servername localhost -cipher 
ECDHE-SM2-WITH-SM4-SM3 -enable_ntls -ntls -verifyCAfile t/certs/gm_ca.crt 
-sign_cert t/certs/client_sign.crt -sign_key t/certs/client_sign.key -enc_cert 
t/certs/client_enc.crt -enc_key t/certs/client_enc.key
+```
+
+这里 `./openssl` 是前面得到的 Tongsuo 命令行工具。9443 是 APISIX 默认的 HTTPS 端口。
+
+如果一切正常,可以看到连接建立了起来,并输出如下信息:
+
+```
+...
+New, NTLSv1.1, Cipher is ECDHE-SM2-SM4-CBC-SM3
+...
+```
+
+## 禁用插件
+
+如果不再使用此插件,可将 `gm` 插件从 `./conf/config.yaml` 配置文件中移除,然后重启 APISIX 
或者通过插件热加载的接口触发插件的卸载。
diff --git a/t/certs/client_enc.crt b/t/certs/client_enc.crt
new file mode 100644
index 000000000..4e5bc78bc
--- /dev/null
+++ b/t/certs/client_enc.crt
@@ -0,0 +1,12 @@
+-----BEGIN CERTIFICATE-----
+MIIB2TCCAX6gAwIBAgIBBTAKBggqgRzPVQGDdTBFMQswCQYDVQQGEwJBQTELMAkG
+A1UECAwCQkIxCzAJBgNVBAoMAkNDMQswCQYDVQQLDAJERDEPMA0GA1UEAwwGc3Vi
+IGNhMB4XDTIyMTEwMjAzMTkzNloXDTMyMTAzMDAzMTkzNlowSTELMAkGA1UEBhMC
+QUExCzAJBgNVBAgMAkJCMQswCQYDVQQKDAJDQzELMAkGA1UECwwCREQxEzARBgNV
+BAMMCmNsaWVudCBlbmMwWjAUBggqgRzPVQGCLQYIKoEcz1UBgi0DQgAEYYuPPz5e
+0QMSGPeBfVbK02GwYhSieSCuc12WsNw+ZQEiaN3NJ2Mh0EAH95eWVutKAeMwKwQZ
+q7QgnSoo3io8hKNaMFgwCQYDVR0TBAIwADALBgNVHQ8EBAMCAzgwHQYDVR0OBBYE
+FEL0AwvahirH+kdK5Poq+e0yhii1MB8GA1UdIwQYMBaAFCTrpmbUig3JfveqAIGJ
+6n+vAk2AMAoGCCqBHM9VAYN1A0kAMEYCIQDx+KxdaJ7YX5gR492EgiGn7//HsjOU
+B7+jyTVvkNzN2AIhAIbDKNJQ2i5Edcw/nDIWJQLec7NZui3QfC/gr9AuCfHN
+-----END CERTIFICATE-----
diff --git a/t/certs/client_enc.key b/t/certs/client_enc.key
new file mode 100644
index 000000000..154aba1d3
--- /dev/null
+++ b/t/certs/client_enc.key
@@ -0,0 +1,5 @@
+-----BEGIN PRIVATE KEY-----
+MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQgq7+Y1ql10Uvv2vTf
+AHR26o4B3RJTJO5XTh0BNLdtB7OhRANCAARhi48/Pl7RAxIY94F9VsrTYbBiFKJ5
+IK5zXZaw3D5lASJo3c0nYyHQQAf3l5ZW60oB4zArBBmrtCCdKijeKjyE
+-----END PRIVATE KEY-----
diff --git a/t/certs/client_sign.crt b/t/certs/client_sign.crt
new file mode 100644
index 000000000..562742b10
--- /dev/null
+++ b/t/certs/client_sign.crt
@@ -0,0 +1,12 @@
+-----BEGIN CERTIFICATE-----
+MIIB2TCCAX+gAwIBAgIBBDAKBggqgRzPVQGDdTBFMQswCQYDVQQGEwJBQTELMAkG
+A1UECAwCQkIxCzAJBgNVBAoMAkNDMQswCQYDVQQLDAJERDEPMA0GA1UEAwwGc3Vi
+IGNhMB4XDTIyMTEwMjAzMTkzNloXDTMyMTAzMDAzMTkzNlowSjELMAkGA1UEBhMC
+QUExCzAJBgNVBAgMAkJCMQswCQYDVQQKDAJDQzELMAkGA1UECwwCREQxFDASBgNV
+BAMMC2NsaWVudCBzaWduMFowFAYIKoEcz1UBgi0GCCqBHM9VAYItA0IABFZcc94m
+hTZRKis639AnlAbS0cKQv73GP5RzBdNlLpAaUwi4hqAh0ZUIcTH/5ZbOTal9MvHA
+gOLjVxv197o+fNejWjBYMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgbAMB0GA1UdDgQW
+BBTkdzyphRCxqD6m6j/AUhMSEBASRDAfBgNVHSMEGDAWgBQk66Zm1IoNyX73qgCB
+iep/rwJNgDAKBggqgRzPVQGDdQNIADBFAiB2rcm1UI84yPYT5q6vjucBNPw01cHM
+3/Hc9fhiuYPyHwIhAIiPPPj4XI2l98C+DBaqoZtSiwDK2IA6Q8lf9SmHdFPN
+-----END CERTIFICATE-----
diff --git a/t/certs/client_sign.key b/t/certs/client_sign.key
new file mode 100644
index 000000000..22daee0bc
--- /dev/null
+++ b/t/certs/client_sign.key
@@ -0,0 +1,5 @@
+-----BEGIN PRIVATE KEY-----
+MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQgIsAr+s4TL7K4AQWO
+PbXrJfHoO5yE2V7oYQxUBsieOQOhRANCAARWXHPeJoU2USorOt/QJ5QG0tHCkL+9
+xj+UcwXTZS6QGlMIuIagIdGVCHEx/+WWzk2pfTLxwIDi41cb9fe6PnzX
+-----END PRIVATE KEY-----
diff --git a/t/certs/gm_ca.crt b/t/certs/gm_ca.crt
new file mode 100644
index 000000000..1e7216e64
--- /dev/null
+++ b/t/certs/gm_ca.crt
@@ -0,0 +1,26 @@
+-----BEGIN CERTIFICATE-----
+MIIB3zCCAYWgAwIBAgIBADAKBggqgRzPVQGDdTBGMQswCQYDVQQGEwJBQTELMAkG
+A1UECAwCQkIxCzAJBgNVBAoMAkNDMQswCQYDVQQLDAJERDEQMA4GA1UEAwwHcm9v
+dCBjYTAeFw0yMjExMDIwMzE5MzZaFw0zMjEwMzAwMzE5MzZaMEYxCzAJBgNVBAYT
+AkFBMQswCQYDVQQIDAJCQjELMAkGA1UECgwCQ0MxCzAJBgNVBAsMAkREMRAwDgYD
+VQQDDAdyb290IGNhMFowFAYIKoEcz1UBgi0GCCqBHM9VAYItA0IABB+V1+bwQsP4
+IMZEVCu3LSekz9SIhxWVVtlqdQYZG55S46PmAqICzrO3KFJ/IPtMx9wKn3L6V5M8
+hAc/UwOAnKCjYzBhMB0GA1UdDgQWBBRV7bTJ6vT1fliZR42/+E+fEBfTSjAfBgNV
+HSMEGDAWgBRV7bTJ6vT1fliZR42/+E+fEBfTSjAPBgNVHRMBAf8EBTADAQH/MA4G
+A1UdDwEB/wQEAwIBhjAKBggqgRzPVQGDdQNIADBFAiEA1SGACV1wj158Spgh+HOW
+oOr7rTO2fR4cK9Zx7eUvmAECIHbKbsw5szaC/EH7CdsHdFgrj2tWaXiQUnx/rxM/
+upAQ
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIB4TCCAYegAwIBAgIBATAKBggqgRzPVQGDdTBGMQswCQYDVQQGEwJBQTELMAkG
+A1UECAwCQkIxCzAJBgNVBAoMAkNDMQswCQYDVQQLDAJERDEQMA4GA1UEAwwHcm9v
+dCBjYTAeFw0yMjExMDIwMzE5MzZaFw0zMjEwMzAwMzE5MzZaMEUxCzAJBgNVBAYT
+AkFBMQswCQYDVQQIDAJCQjELMAkGA1UECgwCQ0MxCzAJBgNVBAsMAkREMQ8wDQYD
+VQQDDAZzdWIgY2EwWjAUBggqgRzPVQGCLQYIKoEcz1UBgi0DQgAElA5ey1dYWNkT
+zfvwcKEhX1vHL+Kjil+egM6QssbNrts2S0M07L77XDe1q2zPpHjo0MR05x862/tZ
+j87OgmEE0KNmMGQwHQYDVR0OBBYEFCTrpmbUig3JfveqAIGJ6n+vAk2AMB8GA1Ud
+IwQYMBaAFFXttMnq9PV+WJlHjb/4T58QF9NKMBIGA1UdEwEB/wQIMAYBAf8CAQAw
+DgYDVR0PAQH/BAQDAgGGMAoGCCqBHM9VAYN1A0gAMEUCIArKNHWYLmd3thQmJv89
+o0wr6O2q26WJuy6y7Eu14rdFAiEA7XNZ0JGMXPKiG5Hl7nmL8ooTrVhrmRrvs9No
+Y8rH88Y=
+-----END CERTIFICATE-----
diff --git a/t/certs/server_enc.crt b/t/certs/server_enc.crt
new file mode 100644
index 000000000..81328c57c
--- /dev/null
+++ b/t/certs/server_enc.crt
@@ -0,0 +1,12 @@
+-----BEGIN CERTIFICATE-----
+MIIB2DCCAX6gAwIBAgIBAzAKBggqgRzPVQGDdTBFMQswCQYDVQQGEwJBQTELMAkG
+A1UECAwCQkIxCzAJBgNVBAoMAkNDMQswCQYDVQQLDAJERDEPMA0GA1UEAwwGc3Vi
+IGNhMB4XDTIyMTEwMjAzMTkzNloXDTMyMTAzMDAzMTkzNlowSTELMAkGA1UEBhMC
+QUExCzAJBgNVBAgMAkJCMQswCQYDVQQKDAJDQzELMAkGA1UECwwCREQxEzARBgNV
+BAMMCnNlcnZlciBlbmMwWjAUBggqgRzPVQGCLQYIKoEcz1UBgi0DQgAED+MQrLrZ
+9PbMmz/44Kb73Qc7FlMs7u034XImjJREBAn1KzZ7jqcYfCiV/buhmu1sLhMXnB69
+mERtf1tAaXcgIaNaMFgwCQYDVR0TBAIwADALBgNVHQ8EBAMCAzgwHQYDVR0OBBYE
+FBxHDo0gHhMoYkDeHWySTIJy5BZpMB8GA1UdIwQYMBaAFCTrpmbUig3JfveqAIGJ
+6n+vAk2AMAoGCCqBHM9VAYN1A0gAMEUCIHtXgpOxcb3mZv2scRZHZz5YGFr45dfk
+VfLkF9BkrB/xAiEA8EeUg7nCFfgHzrfgB7v0wgN1Hrgj8snTUO6IDfkBKYM=
+-----END CERTIFICATE-----
diff --git a/t/certs/server_enc.key b/t/certs/server_enc.key
new file mode 100644
index 000000000..0c264c199
--- /dev/null
+++ b/t/certs/server_enc.key
@@ -0,0 +1,5 @@
+-----BEGIN PRIVATE KEY-----
+MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQgPJQ040ba9lVszMF0
+7S7pzqdxyIMc2fXKr6EsU0vRk2ahRANCAAQP4xCsutn09sybP/jgpvvdBzsWUyzu
+7TfhciaMlEQECfUrNnuOpxh8KJX9u6Ga7WwuExecHr2YRG1/W0BpdyAh
+-----END PRIVATE KEY-----
diff --git a/t/certs/server_sign.crt b/t/certs/server_sign.crt
new file mode 100644
index 000000000..3cd5e87e5
--- /dev/null
+++ b/t/certs/server_sign.crt
@@ -0,0 +1,12 @@
+-----BEGIN CERTIFICATE-----
+MIIB2TCCAX+gAwIBAgIBAjAKBggqgRzPVQGDdTBFMQswCQYDVQQGEwJBQTELMAkG
+A1UECAwCQkIxCzAJBgNVBAoMAkNDMQswCQYDVQQLDAJERDEPMA0GA1UEAwwGc3Vi
+IGNhMB4XDTIyMTEwMjAzMTkzNloXDTMyMTAzMDAzMTkzNlowSjELMAkGA1UEBhMC
+QUExCzAJBgNVBAgMAkJCMQswCQYDVQQKDAJDQzELMAkGA1UECwwCREQxFDASBgNV
+BAMMC3NlcnZlciBzaWduMFowFAYIKoEcz1UBgi0GCCqBHM9VAYItA0IABKGSiyHA
+5oIeT13uNL3yxK+t9rKhAMHTzDTQA01ZylHYLn6XdksWugZsP6tTx/2+17NmRkaH
+H3wztf6ciD3WZnCjWjBYMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgbAMB0GA1UdDgQW
+BBT28BTySSlomaLtnuex4LvaY8tM1TAfBgNVHSMEGDAWgBQk66Zm1IoNyX73qgCB
+iep/rwJNgDAKBggqgRzPVQGDdQNIADBFAiB4qGB+bD47KxSfSgqcedXVTd+JlL4f
+174uhGLSzNkOZwIhAI33LdJlaw+60YlZqQxzffI+gbqXpSN82+3W4vAsONN0
+-----END CERTIFICATE-----
diff --git a/t/certs/server_sign.key b/t/certs/server_sign.key
new file mode 100644
index 000000000..b2ae174c3
--- /dev/null
+++ b/t/certs/server_sign.key
@@ -0,0 +1,5 @@
+-----BEGIN PRIVATE KEY-----
+MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQgMjFhLon1CAQdzFWt
+0Mre0juQiCDbXOY8ljWqSzQN9EehRANCAAShkoshwOaCHk9d7jS98sSvrfayoQDB
+08w00ANNWcpR2C5+l3ZLFroGbD+rU8f9vtezZkZGhx98M7X+nIg91mZw
+-----END PRIVATE KEY-----
diff --git a/t/gm/gm.t b/t/gm/gm.t
new file mode 100644
index 000000000..761c4977f
--- /dev/null
+++ b/t/gm/gm.t
@@ -0,0 +1,170 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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.
+
+use t::APISIX;
+
+if (-f "/usr/local/tongsuo/bin/openssl") {
+    plan 'no_plan';
+} else {
+    plan(skip_all => "only for GM tests");
+}
+
+repeat_each(1);
+no_long_string();
+no_root_location();
+
+add_block_preprocessor(sub {
+    my ($block) = @_;
+
+    # setup default conf.yaml
+    my $extra_yaml_config = $block->extra_yaml_config // <<_EOC_;
+plugins:
+    - gm
+_EOC_
+
+    $block->set_value("extra_yaml_config", $extra_yaml_config);
+
+    if (!$block->request) {
+        $block->set_value("request", "GET /t");
+    }
+});
+
+run_tests;
+
+__DATA__
+
+=== TEST 1: set ssl
+--- config
+location /t {
+    content_by_lua_block {
+        local core = require("apisix.core")
+        local t = require("lib.test_admin")
+
+        local f = assert(io.open("t/certs/server_enc.crt"))
+        local cert_enc = f:read("*a")
+        f:close()
+
+        local f = assert(io.open("t/certs/server_sign.crt"))
+        local cert_sign = f:read("*a")
+        f:close()
+
+        local f = assert(io.open("t/certs/server_enc.key"))
+        local pkey_enc = f:read("*a")
+        f:close()
+
+        local f = assert(io.open("t/certs/server_sign.key"))
+        local pkey_sign = f:read("*a")
+        f:close()
+
+        local data = {cert = cert_enc,
+            key = pkey_enc,
+            certs = {cert_sign},
+            keys = {pkey_sign},
+            sni = "localhost",
+            gm = true,
+        }
+
+        local code, body = t.test('/apisix/admin/ssls/1',
+            ngx.HTTP_PUT,
+            core.json.encode(data)
+        )
+
+        if code >= 300 then
+            ngx.status = code
+            ngx.say(body)
+            return
+        end
+
+        local code, body = t.test('/apisix/admin/routes/1',
+            ngx.HTTP_PUT,
+            [[{
+                "upstream": {
+                    "nodes": {
+                        "127.0.0.1:1980": 1
+                    },
+                    "type": "roundrobin"
+                },
+                "uri": "/echo"
+            }]]
+        )
+
+        ngx.say(body)
+    }
+}
+--- response_body
+passed
+
+
+
+=== TEST 2: hit
+--- exec
+/usr/local/tongsuo/bin/openssl s_client -connect localhost:1994 -servername 
localhost -cipher ECDHE-SM2-WITH-SM4-SM3 -enable_ntls -ntls -verifyCAfile 
t/certs/gm_ca.crt -sign_cert t/certs/client_sign.crt -sign_key 
t/certs/client_sign.key -enc_cert t/certs/client_enc.crt -enc_key 
t/certs/client_enc.key
+--- response_body eval
+qr/^CONNECTED/
+--- no_error_log
+SSL_do_handshake() failed
+[error]
+
+
+
+=== TEST 3: reject bad SSL
+--- config
+location /t {
+    content_by_lua_block {
+        local core = require("apisix.core")
+        local t = require("lib.test_admin")
+
+        local f = assert(io.open("t/certs/server_enc.crt"))
+        local cert_enc = f:read("*a")
+        f:close()
+
+        local f = assert(io.open("t/certs/server_enc.key"))
+        local pkey_enc = f:read("*a")
+        f:close()
+
+        local data = {
+            cert = cert_enc,
+            key = pkey_enc,
+            sni = "localhost",
+            gm = true,
+        }
+
+        local code, body = t.test('/apisix/admin/ssls/1',
+            ngx.HTTP_PUT,
+            core.json.encode(data)
+        )
+
+        if code >= 300 then
+            ngx.status = code
+            ngx.print(body)
+            return
+        end
+    }
+}
+--- error_code: 400
+--- response_body
+{"error_msg":"sign cert\/key are required"}
+
+
+
+=== TEST 4: hit with gm disabled
+--- extra_yaml_config
+--- exec
+/usr/local/tongsuo/bin/openssl s_client -connect localhost:1994 -servername 
localhost -cipher ECDHE-SM2-WITH-SM4-SM3 -enable_ntls -ntls -verifyCAfile 
t/certs/gm_ca.crt -sign_cert t/certs/client_sign.crt -sign_key 
t/certs/client_sign.key -enc_cert t/certs/client_enc.crt -enc_key 
t/certs/client_enc.key
+--- response_body
+--- error_log
+SSL_do_handshake() failed

Reply via email to