Skip to content

Commit

Permalink
feat: support store route's cert in secrets manager (#9247)
Browse files Browse the repository at this point in the history
  • Loading branch information
soulbird authored Apr 12, 2023
1 parent 95fc225 commit ab0a867
Show file tree
Hide file tree
Showing 10 changed files with 420 additions and 18 deletions.
1 change: 1 addition & 0 deletions .github/workflows/fips.yml
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ jobs:
- name: Linux launch common services
run: |
make ci-env-up project_compose_ci=ci/pod/docker-compose.common.yml
sudo ./ci/init-common-test-service.sh
- name: Cache images
id: cache-images
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/gm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ jobs:
- name: Linux launch common services
run: |
make ci-env-up project_compose_ci=ci/pod/docker-compose.common.yml
sudo ./ci/init-common-test-service.sh
- name: Linux Before install
env:
Expand Down
15 changes: 13 additions & 2 deletions apisix/schema_def.lua
Original file line number Diff line number Diff line change
Expand Up @@ -725,8 +725,19 @@ _M.ssl = {
default = "server",
enum = {"server", "client"}
},
cert = certificate_scheme,
key = private_key_schema,
cert = {
oneOf = {
certificate_scheme,
-- TODO: uniformly define the schema of secret_uri
{ type = "string", pattern = "^\\$(secret|env)://"}
}
},
key = {
oneOf = {
private_key_schema,
{ type = "string", pattern = "^\\$(secret|env)://"}
}
},
sni = {
type = "string",
pattern = host_def_pat,
Expand Down
21 changes: 17 additions & 4 deletions apisix/secret.lua
Original file line number Diff line number Diff line change
Expand Up @@ -110,14 +110,27 @@ function _M.init_worker()
end


local function parse_secret_uri(secret_uri)
local function check_secret_uri(secret_uri)
-- Avoid the error caused by has_prefix to cause a crash.
if type(secret_uri) ~= "string" then
return nil, "error secret_uri type: " .. type(secret_uri)
return false, "error secret_uri type: " .. type(secret_uri)
end

if not string.has_prefix(secret_uri, PREFIX) and
not string.has_prefix(upper(secret_uri), core.env.PREFIX) then
return false, "error secret_uri prefix: " .. secret_uri
end

if not string.has_prefix(secret_uri, PREFIX) then
return nil, "error secret_uri prefix: " .. secret_uri
return true
end

_M.check_secret_uri = check_secret_uri


local function parse_secret_uri(secret_uri)
local is_secret_uri, err = check_secret_uri(secret_uri)
if not is_secret_uri then
return is_secret_uri, err
end

local path = sub(secret_uri, #PREFIX + 1)
Expand Down
21 changes: 15 additions & 6 deletions apisix/ssl.lua
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
--
local core = require("apisix.core")
local ngx_ssl = require("ngx.ssl")
local secret = require("apisix.secret")
local ngx_encode_base64 = ngx.encode_base64
local ngx_decode_base64 = ngx.decode_base64
local aes = require("resty.aes")
Expand Down Expand Up @@ -252,9 +253,13 @@ function _M.check_ssl_conf(in_dp, conf)
end
end

local ok, err = validate(conf.cert, conf.key)
if not ok then
return nil, err
if not secret.check_secret_uri(conf.cert) and
not secret.check_secret_uri(conf.key) then

local ok, err = validate(conf.cert, conf.key)
if not ok then
return nil, err
end
end

if conf.type == "client" then
Expand All @@ -268,9 +273,13 @@ function _M.check_ssl_conf(in_dp, conf)
end

for i = 1, numcerts do
local ok, err = validate(conf.certs[i], conf.keys[i])
if not ok then
return nil, "failed to handle cert-key pair[" .. i .. "]: " .. err
if not secret.check_secret_uri(conf.cert[i]) and
not secret.check_secret_uri(conf.key[i]) then

local ok, err = validate(conf.certs[i], conf.keys[i])
if not ok then
return nil, "failed to handle cert-key pair[" .. i .. "]: " .. err
end
end
end

Expand Down
5 changes: 4 additions & 1 deletion apisix/ssl/router/radixtree_sni.lua
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ local get_request = require("resty.core.base").get_request
local router_new = require("apisix.utils.router").new
local core = require("apisix.core")
local apisix_ssl = require("apisix.ssl")
local secret = require("apisix.secret")
local ngx_ssl = require("ngx.ssl")
local config_util = require("apisix.core.config_util")
local ipairs = ipairs
Expand Down Expand Up @@ -212,7 +213,9 @@ function _M.match_and_set(api_ctx, match_only, alt_sni)

ngx_ssl.clear_certs()

ok, err = _M.set_cert_and_key(sni, matched_ssl.value)
local new_ssl_value = secret.fetch_secrets(matched_ssl.value) or matched_ssl.value

ok, err = _M.set_cert_and_key(sni, new_ssl_value)
if not ok then
return false, err
end
Expand Down
4 changes: 2 additions & 2 deletions docs/en/latest/admin-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -1154,8 +1154,8 @@ SSL resource request address: /apisix/admin/ssls/{id}
| Parameter | Required | Type | Description | Example |
| ------------ | -------- | ------------------------ | -------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |
| cert | True | Certificate | HTTPS certificate. | |
| key | True | Private key | HTTPS private key. | |
| cert | True | Certificate | HTTPS certificate. This field supports saving the value in Secret Manager using the [APISIX Secret](../terminology/secret.md) resource. | |
| key | True | Private key | HTTPS private key. This field supports saving the value in Secret Manager using the [APISIX Secret](../terminology/secret.md) resource. | |
| certs | False | An array of certificates | Used for configuring multiple certificates for the same domain excluding the one provided in the `cert` field. | |
| keys | False | An array of private keys | Private keys to pair with the `certs`. | |
| client.ca | False | Certificate | Sets the CA certificate that verifies the client. Requires OpenResty 1.19+. | |
Expand Down
4 changes: 2 additions & 2 deletions docs/zh/latest/admin-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -1162,8 +1162,8 @@ SSL 资源请求地址:/apisix/admin/ssls/{id}

| 名称 | 必选项 | 类型 | 描述 | 示例 |
| ----------- | ------ | -------------- | ------------------------------------------------------------------------------------------------------ | ------------------------------------------------ |
| cert || 证书 | HTTP 证书。 | |
| key || 私钥 | HTTPS 证书私钥 | |
| cert || 证书 | HTTP 证书。该字段支持使用 [APISIX Secret](../terminology/secret.md) 资源,将值保存在 Secret Manager 中。 | |
| key || 私钥 | HTTPS 证书私钥。该字段支持使用 [APISIX Secret](../terminology/secret.md) 资源,将值保存在 Secret Manager 中。 | |
| certs || 证书字符串数组 | 当你想给同一个域名配置多个证书时,除了第一个证书需要通过 `cert` 传递外,剩下的证书可以通过该参数传递上来。 | |
| keys || 私钥字符串数组 | `certs` 对应的证书私钥,需要与 `certs` 一一对应。 | |
| client.ca || 证书 | 设置将用于客户端证书校验的 `CA` 证书。该特性需要 OpenResty 为 1.19 及以上版本。 | |
Expand Down
87 changes: 87 additions & 0 deletions t/gm/gm.t
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,22 @@
# specific language governing permissions and limitations
# under the License.

BEGIN {
$ENV{TEST_ENV_GMSSL_CRT_ENC} = "-----BEGIN CERTIFICATE-----
MIIB2DCCAX6gAwIBAgIBAzAKBggqgRzPVQGDdTBFMQswCQYDVQQGEwJBQTELMAkG
A1UECAwCQkIxCzAJBgNVBAoMAkNDMQswCQYDVQQLDAJERDEPMA0GA1UEAwwGc3Vi
IGNhMB4XDTIyMTEwMjAzMTkzNloXDTMyMTAzMDAzMTkzNlowSTELMAkGA1UEBhMC
QUExCzAJBgNVBAgMAkJCMQswCQYDVQQKDAJDQzELMAkGA1UECwwCREQxEzARBgNV
BAMMCnNlcnZlciBlbmMwWjAUBggqgRzPVQGCLQYIKoEcz1UBgi0DQgAED+MQrLrZ
9PbMmz/44Kb73Qc7FlMs7u034XImjJREBAn1KzZ7jqcYfCiV/buhmu1sLhMXnB69
mERtf1tAaXcgIaNaMFgwCQYDVR0TBAIwADALBgNVHQ8EBAMCAzgwHQYDVR0OBBYE
FBxHDo0gHhMoYkDeHWySTIJy5BZpMB8GA1UdIwQYMBaAFCTrpmbUig3JfveqAIGJ
6n+vAk2AMAoGCCqBHM9VAYN1A0gAMEUCIHtXgpOxcb3mZv2scRZHZz5YGFr45dfk
VfLkF9BkrB/xAiEA8EeUg7nCFfgHzrfgB7v0wgN1Hrgj8snTUO6IDfkBKYM=
-----END CERTIFICATE-----
";
}

use t::APISIX;

if (-f "/usr/local/tongsuo/bin/openssl") {
Expand Down Expand Up @@ -168,3 +184,74 @@ location /t {
--- response_body
--- error_log
SSL_do_handshake() failed
=== TEST 5: set ssl: server_enc with secret ref
--- 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_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 = "$env://TEST_ENV_GMSSL_CRT_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 6: 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]
Loading

0 comments on commit ab0a867

Please sign in to comment.