You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by me...@apache.org on 2020/12/30 14:57:08 UTC

[apisix] branch master updated: chore: improve HTTPS doc (#3149)

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

membphis 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 13ab490  chore: improve HTTPS doc (#3149)
13ab490 is described below

commit 13ab4903b4581f60466d4563ed74443c90acf943
Author: 罗泽轩 <sp...@gmail.com>
AuthorDate: Wed Dec 30 22:57:00 2020 +0800

    chore: improve HTTPS doc (#3149)
---
 apisix/schema_def.lua      |   3 +-
 doc/admin-api.md           |   5 +-
 doc/https.md               |  75 ++++++++++++++++----
 doc/stand-alone.md         |   3 +-
 doc/zh-cn/admin-api.md     |   5 +-
 doc/zh-cn/https.md         |  77 +++++++++++++++++----
 doc/zh-cn/stand-alone.md   |   3 +-
 t/admin/ssl2.t             |  29 ++++++++
 t/config-center-yaml/ssl.t | 168 +++++++++++++++++++++++++++++++++------------
 9 files changed, 293 insertions(+), 75 deletions(-)

diff --git a/apisix/schema_def.lua b/apisix/schema_def.lua
index 2732edb..322f289 100644
--- a/apisix/schema_def.lua
+++ b/apisix/schema_def.lua
@@ -621,7 +621,8 @@ _M.ssl = {
             items = {
                 type = "string",
                 pattern = [[^\*?[0-9a-zA-Z-.]+$]],
-            }
+            },
+            minItems = 1,
         },
         certs = {
             type = "array",
diff --git a/doc/admin-api.md b/doc/admin-api.md
index 94ea65c..ffa8df4 100644
--- a/doc/admin-api.md
+++ b/doc/admin-api.md
@@ -667,10 +667,11 @@ Return response from etcd currently.
 |key|True|Private key|https private key||
 |certs|False|An array of certificate|when you need to configure multiple certificate for the same domain, you can pass extra https certificates (excluding the one given as cert) in this field||
 |keys|False|An array of private key|https private keys. The keys should be paired with certs above||
-|sni|True|Match Rules|https SNI||
+|snis|True|Match Rules|a non-empty arrays of https SNI||
 |labels|False |Match Rules|Key/value pairs to specify attributes|{"version":"v2","build":"16","env":"production"}|
 |create_time|False| Auxiliary|epoch timestamp in second, will be created automatically if missing | 1602883670|
 |update_time|False| Auxiliary|epoch timestamp in second, will be created automatically if missing | 1602883670|
+|status|False|Auxiliary|enable this SSL, default `1`.|`1` to enable, `0` to disable|
 
 Config Example:
 
@@ -679,7 +680,7 @@ Config Example:
     "id": "1",      # id
     "cert": "cert", # Certificate
     "key": "key",   # Private key
-    "sni": "sni"    # https SNI
+    "snis": ["t.com"]    # https SNI
 }
 ```
 
diff --git a/doc/https.md b/doc/https.md
index 7e4bd7a..53092a5 100644
--- a/doc/https.md
+++ b/doc/https.md
@@ -29,14 +29,53 @@ It is most common for an SSL certificate to contain only one domain. We can crea
 
 * `cert`: PEM-encoded public certificate of the SSL key pair.
 * `key`: PEM-encoded private key of the SSL key pair.
-* `sni`: Hostname to associate with this certificate as SNIs. To set this attribute this certificate must have a valid private key associated with it.
+* `snis`: Hostname(s) to associate with this certificate as SNIs. To set this attribute this certificate must have a valid private key associated with it.
+
+We will use the python script below to simplify the example:
+```python
+#!/usr/bin/env python
+# coding: utf-8
+# save this file as ssl.py
+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:
+    cert = f.read()
+with open(sys.argv[2]) as f:
+    key = f.read()
+sni = sys.argv[3]
+api_key = "edd1c9f034335f136f87ad84b625c8f1"
+resp = requests.put("http://127.0.0.1:9080/apisix/admin/ssl/1", json={
+    "cert": cert,
+    "key": key,
+    "snis": [sni],
+}, headers={
+    "X-API-KEY": api_key,
+})
+print(resp.status_code)
+print(resp.text)
+```
 
 ```shell
-curl http://127.0.0.1:9080/apisix/admin/ssl/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
+# create SSL object
+./ssl.py t.crt t.key test.com
+
+# create Router object
+curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -i -d '
 {
-    "cert": "...",
-    "key": "....",
-    "sni": "test.com"
+    "uri": "/hello",
+    "hosts": ["test.com"],
+    "methods": ["GET"],
+    "upstream": {
+        "type": "roundrobin",
+        "nodes": {
+            "127.0.0.1:1980": 1
+        }
+    }
 }'
 
 # make a test
@@ -66,14 +105,22 @@ curl --resolve 'test.com:9443:127.0.0.1' https://test.com:9443/hello  -vvv
 Sometimes, one SSL certificate may contain a wildcard domain like `*.test.com`,
 that means it can accept more than one domain, eg: `www.test.com` or `mail.test.com`.
 
-Here is an example, please pay attention on the field `sni`.
+Here is an example, note that the value we pass as `sni` is `*.test.com`.
 
 ```shell
-curl http://127.0.0.1:9080/apisix/admin/ssl/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
+./ssl.py t.crt t.key '*.test.com'
+
+curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -i -d '
 {
-    "cert": "...",
-    "key": "....",
-    "sni": "*.test.com"
+    "uri": "/hello",
+    "hosts": ["*.test.com"],
+    "methods": ["GET"],
+    "upstream": {
+        "type": "roundrobin",
+        "nodes": {
+            "127.0.0.1:1980": 1
+        }
+    }
 }'
 
 # make a test
@@ -101,8 +148,12 @@ curl --resolve 'www.test.com:9443:127.0.0.1' https://www.test.com:9443/hello  -v
 ### multiple domain
 
 If your SSL certificate may contain more than one domain, like `www.test.com`
-and `mail.test.com`, then you can more ssl object for each domain, that is a
-most simple way.
+and `mail.test.com`, then you can add them into the `snis` array. For example:
+```json
+{
+    "snis": ["www.test.com", "mail.test.com"]
+}
+```
 
 ### multiple certificates for a single domain
 
diff --git a/doc/stand-alone.md b/doc/stand-alone.md
index 25fa1f0..9aa8fc6 100644
--- a/doc/stand-alone.md
+++ b/doc/stand-alone.md
@@ -223,6 +223,7 @@ ssl:
             IeVtU6fH57Ddn59VPbF20m8RCSkmBvSdcbyBmqlZSBE+fKwCliKl6u/GH0BNAWKz
             r8yiEiskqRmy7P7MY9hDmEbG
             -----END PRIVATE KEY-----
-        sni: "yourdomain.com"
+        snis:
+            - "yourdomain.com"
 #END
 ```
diff --git a/doc/zh-cn/admin-api.md b/doc/zh-cn/admin-api.md
index cea7667..c6a83ae 100644
--- a/doc/zh-cn/admin-api.md
+++ b/doc/zh-cn/admin-api.md
@@ -675,10 +675,11 @@ HTTP/1.1 200 OK
 |key|必需|私钥|https 证书私钥||
 |certs|可选|证书字符串数组|当你想给同一个域名配置多个证书时,除了第一个证书需要通过cert传递外,剩下的证书可以通过该参数传递上来||
 |keys|可选|私钥字符串数组|certs 对应的证书私钥,注意要跟 certs 一一对应||
-|sni|必需|匹配规则|https 证书SNI||
+|snis|必需|匹配规则|非空数组形式,可以匹配多个 SNI||
 |labels|可选|匹配规则|标识附加属性的键值对|{"version":"v2","build":"16","env":"production"}|
 |create_time|可选|辅助|单位为秒的 epoch 时间戳,如果不指定则自动创建|1602883670|
 |update_time|可选|辅助|单位为秒的 epoch 时间戳,如果不指定则自动创建|1602883670|
+|status|可选 |辅助| 是否启用此SSL, 缺省 `1`。|`1` 表示启用,`0` 表示禁用|
 
 ssl 对象 json 配置内容:
 
@@ -687,7 +688,7 @@ ssl 对象 json 配置内容:
     "id": "1",          # id
     "cert": "cert",     # 证书
     "key": "key",       # 私钥
-    "sni": "sni"        # host 域名
+    "snis": ["t.com"]   # HTTPS 握手时客户端发送的 SNI
 }
 ```
 
diff --git a/doc/zh-cn/https.md b/doc/zh-cn/https.md
index d034f66..8bb2bc6 100644
--- a/doc/zh-cn/https.md
+++ b/doc/zh-cn/https.md
@@ -31,14 +31,53 @@ SNI(Server Name Indication)是用来改善 SSL 和 TLS 的一项特性,它允
 
 * `cert`: SSL 密钥对的公钥,pem 格式
 * `key`: SSL 密钥对的私钥,pem 格式
-* `sni`: SSL 证书所指定的域名,注意在设置这个参数之前,你需要确保这个证书对应的私钥是有效的。
+* `snis`: SSL 证书所指定的一个或多个域名,注意在设置这个参数之前,你需要确保这个证书对应的私钥是有效的。
+
+为了简化示例,我们会使用下面的 python 脚本:
+```python
+#!/usr/bin/env python
+# coding: utf-8
+# save this file as ssl.py
+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:
+    cert = f.read()
+with open(sys.argv[2]) as f:
+    key = f.read()
+sni = sys.argv[3]
+api_key = "edd1c9f034335f136f87ad84b625c8f1"
+resp = requests.put("http://127.0.0.1:9080/apisix/admin/ssl/1", json={
+    "cert": cert,
+    "key": key,
+    "snis": [sni],
+}, headers={
+    "X-API-KEY": api_key,
+})
+print(resp.status_code)
+print(resp.text)
+```
 
 ```shell
-curl http://127.0.0.1:9080/apisix/admin/ssl/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
+# 创建 SSL 对象
+./ssl.py t.crt t.key test.com
+
+# 创建 Router 对象
+curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -i -d '
 {
-    "cert": "...",
-    "key": "....",
-    "sni": "test.com"
+    "uri": "/hello",
+    "hosts": ["test.com"],
+    "methods": ["GET"],
+    "upstream": {
+        "type": "roundrobin",
+        "nodes": {
+            "127.0.0.1:1980": 1
+        }
+    }
 }'
 
 # 测试一下
@@ -68,14 +107,22 @@ curl --resolve 'test.com:9443:127.0.0.1' https://test.com:9443/hello  -vvv
 一个 SSL 证书的域名也可能包含泛域名,如`*.test.com`,它代表所有以`test.com`结尾的域名都可以使用该证书。
 比如`*.test.com`,可以匹配 `www.test.com`、`mail.test.com`甚至`a.b.test.com`。
 
-看下面这个例子,请注意 `sni` 这个属性:
+看下面这个例子,请注意我们把 `*.test.com` 作为 sni 传递进来:
 
 ```shell
-curl http://127.0.0.1:9080/apisix/admin/ssl/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
+./ssl.py t.crt t.key '*.test.com'
+
+curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -i -d '
 {
-    "cert": "...",
-    "key": "....",
-    "sni": "*.test.com"
+    "uri": "/hello",
+    "hosts": ["*.test.com"],
+    "methods": ["GET"],
+    "upstream": {
+        "type": "roundrobin",
+        "nodes": {
+            "127.0.0.1:1980": 1
+        }
+    }
 }'
 
 # 测试一下
@@ -102,8 +149,14 @@ curl --resolve 'www.test.com:9443:127.0.0.1' https://www.test.com:9443/hello  -v
 
 ### 多域名的情况
 
-如果一个 SSL 证书包含多个独立域名,比如`www.test.com`和`mail.test.com`,通配符方式又会导致匹配不严谨。
-所以针对不同域名,设置不同 SSL 证书对象即可。
+如果一个 SSL 证书包含多个独立域名,比如`www.test.com`和`mail.test.com`,
+你可以把它们都放入 `snis` 数组中,就像这样:
+
+```json
+{
+    "snis": ["www.test.com", "mail.test.com"]
+}
+```
 
 ### 单域名多证书的情况
 
diff --git a/doc/zh-cn/stand-alone.md b/doc/zh-cn/stand-alone.md
index 03ce3e1..9355e30 100644
--- a/doc/zh-cn/stand-alone.md
+++ b/doc/zh-cn/stand-alone.md
@@ -224,6 +224,7 @@ ssl:
             IeVtU6fH57Ddn59VPbF20m8RCSkmBvSdcbyBmqlZSBE+fKwCliKl6u/GH0BNAWKz
             r8yiEiskqRmy7P7MY9hDmEbG
             -----END PRIVATE KEY-----
-        sni: "yourdomain.com"
+        snis:
+            - "yourdomain.com"
 #END
 ```
diff --git a/t/admin/ssl2.t b/t/admin/ssl2.t
index a616bb0..6614eef 100644
--- a/t/admin/ssl2.t
+++ b/t/admin/ssl2.t
@@ -336,3 +336,32 @@ wzarryret/7GFW1/3cz+hTj9/d45i25zArr3Pocfpur5mfz3fJO8jg==
 --- error_code: 400
 --- response_body
 {"error_msg":"failed to handle cert-key pair[1]: failed to parse key: PEM_read_bio_PrivateKey() failed"}
+
+
+
+=== TEST 10: empty snis
+--- config
+    location /t {
+        content_by_lua_block {
+            local json = require("toolkit.json")
+            local t = require("lib.test_admin")
+            local ssl_cert = t.read_file("t/certs/apisix.crt")
+            local ssl_key =  t.read_file("t/certs/apisix.key")
+            local data = {cert = ssl_cert, key = ssl_key, snis = {}}
+            local code, message, res = t.test('/apisix/admin/ssl/1',
+                ngx.HTTP_PUT,
+                json.encode(data)
+            )
+
+            if code >= 300 then
+                ngx.status = code
+                ngx.print(message)
+                return
+            end
+
+            ngx.say(res)
+        }
+    }
+--- error_code: 400
+--- response_body
+{"error_msg":"invalid configuration: property \"snis\" validation failed: expect array to have at least 1 items"}
diff --git a/t/config-center-yaml/ssl.t b/t/config-center-yaml/ssl.t
index 715bc54..673cdc0 100644
--- a/t/config-center-yaml/ssl.t
+++ b/t/config-center-yaml/ssl.t
@@ -53,6 +53,53 @@ _EOC_
     if (!$block->no_error_log) {
         $block->set_value("no_error_log", "[error]\n[alert]");
     }
+
+    if ($block->sslhandshake) {
+        my $sslhandshake = $block->sslhandshake;
+
+        $block->set_value("config", <<_EOC_)
+listen unix:\$TEST_NGINX_HTML_DIR/nginx.sock ssl;
+
+location /t {
+    content_by_lua_block {
+        -- sync
+        ngx.sleep(0.2)
+
+        do
+            local sock = ngx.socket.tcp()
+
+            sock:settimeout(2000)
+
+            local ok, err = sock:connect("unix:\$TEST_NGINX_HTML_DIR/nginx.sock")
+            if not ok then
+                ngx.say("failed to connect: ", err)
+                return
+            end
+
+            $sslhandshake
+            local req = "GET /hello HTTP/1.0\\r\\nHost: test.com\\r\\nConnection: close\\r\\n\\r\\n"
+            local bytes, err = sock:send(req)
+            if not bytes then
+                ngx.say("failed to send http request: ", err)
+                return
+            end
+
+            local line, err = sock:receive()
+            if not line then
+                ngx.say("failed to receive: ", err)
+                return
+            end
+
+            ngx.say("received: ", line)
+
+            local ok, err = sock:close()
+            ngx.say("close: ", ok, " ", err)
+        end  -- do
+        -- collectgarbage()
+    }
+}
+_EOC_
+    }
 });
 
 run_tests();
@@ -115,53 +162,86 @@ ssl:
             IeVtU6fH57Ddn59VPbF20m8RCSkmBvSdcbyBmqlZSBE+fKwCliKl6u/GH0BNAWKz
             r8yiEiskqRmy7P7MY9hDmEbG
             -----END PRIVATE KEY-----
-        sni: "test.com"
---- config
-listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;
-
-location /t {
-    content_by_lua_block {
-        -- sync
-        ngx.sleep(0.2)
-
-        do
-            local sock = ngx.socket.tcp()
-
-            sock:settimeout(2000)
-
-            local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock")
-            if not ok then
-                ngx.say("failed to connect: ", err)
-                return
-            end
-
-            local sess, err = sock:sslhandshake(nil, "test.com", false)
-            if not sess then
-                ngx.say("failed to do SSL handshake: ", err)
-                return
-            end
-
-            local req = "GET /hello HTTP/1.0\r\nHost: test.com\r\nConnection: close\r\n\r\n"
-            local bytes, err = sock:send(req)
-            if not bytes then
-                ngx.say("failed to send http request: ", err)
-                return
-            end
+        snis:
+            - "t.com"
+            - "test.com"
+--- sslhandshake
+local sess, err = sock:sslhandshake(nil, "test.com", false)
+if not sess then
+    ngx.say("failed to do SSL handshake: ", err)
+    return
+end
+--- response_body
+received: HTTP/1.1 200 OK
+close: 1 nil
+--- error_log
+lua ssl server name: "test.com"
 
-            local line, err = sock:receive()
-            if not line then
-                ngx.say("failed to receive: ", err)
-                return
-            end
 
-            ngx.say("received: ", line)
 
-            local ok, err = sock:close()
-            ngx.say("close: ", ok, " ", err)
-        end  -- do
-        -- collectgarbage()
-    }
-}
+=== TEST 2: single sni
+--- apisix_yaml
+ssl:
+    -
+        cert: |
+            -----BEGIN CERTIFICATE-----
+            MIIDrzCCApegAwIBAgIJAI3Meu/gJVTLMA0GCSqGSIb3DQEBCwUAMG4xCzAJBgNV
+            BAYTAkNOMREwDwYDVQQIDAhaaGVqaWFuZzERMA8GA1UEBwwISGFuZ3pob3UxDTAL
+            BgNVBAoMBHRlc3QxDTALBgNVBAsMBHRlc3QxGzAZBgNVBAMMEmV0Y2QuY2x1c3Rl
+            ci5sb2NhbDAeFw0yMDEwMjgwMzMzMDJaFw0yMTEwMjgwMzMzMDJaMG4xCzAJBgNV
+            BAYTAkNOMREwDwYDVQQIDAhaaGVqaWFuZzERMA8GA1UEBwwISGFuZ3pob3UxDTAL
+            BgNVBAoMBHRlc3QxDTALBgNVBAsMBHRlc3QxGzAZBgNVBAMMEmV0Y2QuY2x1c3Rl
+            ci5sb2NhbDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJ/qwxCR7g5S
+            s9+VleopkLi5pAszEkHYOBpwF/hDeRdxU0I0e1zZTdTlwwPy2vf8m3kwoq6fmNCt
+            tdUUXh5Wvgi/2OA8HBBzaQFQL1Av9qWwyES5cx6p0ZBwIrcXQIsl1XfNSUpQNTSS
+            D44TGduXUIdeshukPvMvLWLezynf2/WlgVh/haWtDG99r/Gj3uBdjl0m/xGvKvIv
+            NFy6EdgG9fkwcIalutjrUnGl9moGjwKYu4eXW2Zt5el0d1AHXUsqK4voe0p+U2Nz
+            quDmvxteXWdlsz8o5kQT6a4DUtWhpPIfNj9oZfPRs3LhBFQ74N70kVxMOCdec1lU
+            bnFzLIMGlz0CAwEAAaNQME4wHQYDVR0OBBYEFFHeljijrr+SPxlH5fjHRPcC7bv2
+            MB8GA1UdIwQYMBaAFFHeljijrr+SPxlH5fjHRPcC7bv2MAwGA1UdEwQFMAMBAf8w
+            DQYJKoZIhvcNAQELBQADggEBAG6NNTK7sl9nJxeewVuogCdMtkcdnx9onGtCOeiQ
+            qvh5Xwn9akZtoLMVEdceU0ihO4wILlcom3OqHs9WOd6VbgW5a19Thh2toxKidHz5
+            rAaBMyZsQbFb6+vFshZwoCtOLZI/eIZfUUMFqMXlEPrKru1nSddNdai2+zi5rEnM
+            HCot43+3XYuqkvWlOjoi9cP+C4epFYrxpykVbcrtbd7TK+wZNiK3xtDPnVzjdNWL
+            geAEl9xrrk0ss4nO/EreTQgS46gVU+tLC+b23m2dU7dcKZ7RDoiA9bdVc4a2IsaS
+            2MvLL4NZ2nUh8hAEHiLtGMAV3C6xNbEyM07hEpDW6vk6tqk=
+            -----END CERTIFICATE-----
+        key: |
+            -----BEGIN PRIVATE KEY-----
+            MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCf6sMQke4OUrPf
+            lZXqKZC4uaQLMxJB2DgacBf4Q3kXcVNCNHtc2U3U5cMD8tr3/Jt5MKKun5jQrbXV
+            FF4eVr4Iv9jgPBwQc2kBUC9QL/alsMhEuXMeqdGQcCK3F0CLJdV3zUlKUDU0kg+O
+            Exnbl1CHXrIbpD7zLy1i3s8p39v1pYFYf4WlrQxvfa/xo97gXY5dJv8RryryLzRc
+            uhHYBvX5MHCGpbrY61JxpfZqBo8CmLuHl1tmbeXpdHdQB11LKiuL6HtKflNjc6rg
+            5r8bXl1nZbM/KOZEE+muA1LVoaTyHzY/aGXz0bNy4QRUO+De9JFcTDgnXnNZVG5x
+            cyyDBpc9AgMBAAECggEAatcEtehZPJaCeClPPF/Cwbe9YoIfe4BCk186lHI3z7K1
+            5nB7zt+bwVY0AUpagv3wvXoB5lrYVOsJpa9y5iAb3GqYMc/XDCKfD/KLea5hwfcn
+            BctEn0LjsPVKLDrLs2t2gBDWG2EU+udunwQh7XTdp2Nb6V3FdOGbGAg2LgrSwP1g
+            0r4z14F70oWGYyTQ5N8UGuyryVrzQH525OYl38Yt7R6zJ/44FVi/2TvdfHM5ss39
+            SXWi00Q30fzaBEf4AdHVwVCRKctwSbrIOyM53kiScFDmBGRblCWOxXbiFV+d3bjX
+            gf2zxs7QYZrFOzOO7kLtHGua4itEB02497v+1oKDwQKBgQDOBvCVGRe2WpItOLnj
+            SF8iz7Sm+jJGQz0D9FhWyGPvrN7IXGrsXavA1kKRz22dsU8xdKk0yciOB13Wb5y6
+            yLsr/fPBjAhPb4h543VHFjpAQcxpsH51DE0b2oYOWMmz+rXGB5Jy8EkP7Q4njIsc
+            2wLod1dps8OT8zFx1jX3Us6iUQKBgQDGtKkfsvWi3HkwjFTR+/Y0oMz7bSruE5Z8
+            g0VOHPkSr4XiYgLpQxjbNjq8fwsa/jTt1B57+By4xLpZYD0BTFuf5po+igSZhH8s
+            QS5XnUnbM7d6Xr/da7ZkhSmUbEaMeHONSIVpYNgtRo4bB9Mh0l1HWdoevw/w5Ryt
+            L/OQiPhfLQKBgQCh1iG1fPh7bbnVe/HI71iL58xoPbCwMLEFIjMiOFcINirqCG6V
+            LR91Ytj34JCihl1G4/TmWnsH1hGIGDRtJLCiZeHL70u32kzCMkI1jOhFAWqoutMa
+            7obDkmwraONIVW/kFp6bWtSJhhTQTD4adI9cPCKWDXdcCHSWj0Xk+U8HgQKBgBng
+            t1HYhaLzIZlP/U/nh3XtJyTrX7bnuCZ5FhKJNWrYjxAfgY+NXHRYCKg5x2F5j70V
+            be7pLhxmCnrPTMKZhik56AaTBOxVVBaYWoewhUjV4GRAaK5Wc8d9jB+3RizPFwVk
+            V3OU2DJ1SNZ+W2HBOsKrEfwFF/dgby6i2w6MuAP1AoGBAIxvxUygeT/6P0fHN22P
+            zAHFI4v2925wYdb7H//D8DIADyBwv18N6YH8uH7L+USZN7e4p2k8MGGyvTXeC6aX
+            IeVtU6fH57Ddn59VPbF20m8RCSkmBvSdcbyBmqlZSBE+fKwCliKl6u/GH0BNAWKz
+            r8yiEiskqRmy7P7MY9hDmEbG
+            -----END PRIVATE KEY-----
+        sni: "test.com"
+--- sslhandshake
+local sess, err = sock:sslhandshake(nil, "test.com", false)
+if not sess then
+    ngx.say("failed to do SSL handshake: ", err)
+    return
+end
 --- response_body
 received: HTTP/1.1 200 OK
 close: 1 nil