APISIX 支持通过 TLS 扩展 SNI 实现加载特定的 SSL 证书以实现对 https 的支持。

SNI(Server Name Indication)是用来改善 SSL 和 TLS 的一项特性,它允许客户端在服务器端向其发送证书之前向服务器端发送请求的域名,服务器端根据客户端请求的域名选择合适的SSL证书发送给客户端。

单一域名指定

通常情况下一个 SSL 证书只包含一个静态域名,配置一个 ssl 参数对象,它包括 certkeysni三个属性,详细如下:

  • cert: SSL 密钥对的公钥,pem 格式
  • key: SSL 密钥对的私钥,pem 格式
  • snis: SSL 证书所指定的一个或多个域名,注意在设置这个参数之前,你需要确保这个证书对应的私钥是有效的。

为了简化示例,我们会使用下面的 Python 脚本:

  1. #!/usr/bin/env python
  2. # coding: utf-8
  3. # save this file as ssl.py
  4. import sys
  5. # sudo pip install requests
  6. import requests
  7. if len(sys.argv) <= 3:
  8. print("bad argument")
  9. sys.exit(1)
  10. with open(sys.argv[1]) as f:
  11. cert = f.read()
  12. with open(sys.argv[2]) as f:
  13. key = f.read()
  14. sni = sys.argv[3]
  15. api_key = "edd1c9f034335f136f87ad84b625c8f1"
  16. resp = requests.put("http://127.0.0.1:9080/apisix/admin/ssl/1", json={
  17. "cert": cert,
  18. "key": key,
  19. "snis": [sni],
  20. }, headers={
  21. "X-API-KEY": api_key,
  22. })
  23. print(resp.status_code)
  24. print(resp.text)
  1. # 创建 SSL 对象
  2. ./ssl.py t.crt t.key test.com
  3. # 创建 Router 对象
  4. curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -i -d '
  5. {
  6. "uri": "/hello",
  7. "hosts": ["test.com"],
  8. "methods": ["GET"],
  9. "upstream": {
  10. "type": "roundrobin",
  11. "nodes": {
  12. "127.0.0.1:1980": 1
  13. }
  14. }
  15. }'
  16. # 测试一下
  17. curl --resolve 'test.com:9443:127.0.0.1' https://test.com:9443/hello -vvv
  18. * Added test.com:9443:127.0.0.1 to DNS cache
  19. * About to connect() to test.com port 9443 (#0)
  20. * Trying 127.0.0.1...
  21. * Connected to test.com (127.0.0.1) port 9443 (#0)
  22. * Initializing NSS with certpath: sql:/etc/pki/nssdb
  23. * skipping SSL peer certificate verification
  24. * SSL connection using TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
  25. * Server certificate:
  26. * subject: CN=test.com,O=iresty,L=ZhuHai,ST=GuangDong,C=CN
  27. * start date: Jun 24 22:18:05 2019 GMT
  28. * expire date: May 31 22:18:05 2119 GMT
  29. * common name: test.com
  30. * issuer: CN=test.com,O=iresty,L=ZhuHai,ST=GuangDong,C=CN
  31. > GET /hello HTTP/1.1
  32. > User-Agent: curl/7.29.0
  33. > Host: test.com:9443
  34. > Accept: */*

泛域名

一个 SSL 证书的域名也可能包含泛域名,如*.test.com,它代表所有以test.com结尾的域名都可以使用该证书。 比如*.test.com,可以匹配 www.test.commail.test.com甚至a.b.test.com

看下面这个例子,请注意我们把 *.test.com 作为 sni 传递进来:

  1. ./ssl.py t.crt t.key '*.test.com'
  2. curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -i -d '
  3. {
  4. "uri": "/hello",
  5. "hosts": ["*.test.com"],
  6. "methods": ["GET"],
  7. "upstream": {
  8. "type": "roundrobin",
  9. "nodes": {
  10. "127.0.0.1:1980": 1
  11. }
  12. }
  13. }'
  14. # 测试一下
  15. curl --resolve 'www.test.com:9443:127.0.0.1' https://www.test.com:9443/hello -vvv
  16. * Added test.com:9443:127.0.0.1 to DNS cache
  17. * About to connect() to test.com port 9443 (#0)
  18. * Trying 127.0.0.1...
  19. * Connected to test.com (127.0.0.1) port 9443 (#0)
  20. * Initializing NSS with certpath: sql:/etc/pki/nssdb
  21. * skipping SSL peer certificate verification
  22. * SSL connection using TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
  23. * Server certificate:
  24. * subject: CN=test.com,O=iresty,L=ZhuHai,ST=GuangDong,C=CN
  25. * start date: Jun 24 22:18:05 2019 GMT
  26. * expire date: May 31 22:18:05 2119 GMT
  27. * common name: test.com
  28. * issuer: CN=test.com,O=iresty,L=ZhuHai,ST=GuangDong,C=CN
  29. > GET /hello HTTP/1.1
  30. > User-Agent: curl/7.29.0
  31. > Host: test.com:9443
  32. > Accept: */*

多域名的情况

如果一个 SSL 证书包含多个独立域名,比如www.test.commail.test.com, 你可以把它们都放入 snis 数组中,就像这样:

  1. {
  2. "snis": ["www.test.com", "mail.test.com"]
  3. }

单域名多证书的情况

如果你期望为一个域名配置多张证书,例如以此来同时支持使用 ECC 和 RSA 的密钥交换算法,那么你可以将额外的证书和私钥(第一张证书和其私钥依然使用 certkey)配置在 certskeys 中。

  • certs:PEM 格式的 SSL 证书列表
  • keys:PEM 格式的 SSL 证书私钥列表

APISIX 会将相同下标的证书和私钥配对使用,因此 certskeys 列表的长度必须一致。