diff --git a/lib/resty/apisix/ssl.lua b/lib/resty/apisix/ssl.lua index 89fdcc1..541bfbe 100644 --- a/lib/resty/apisix/ssl.lua +++ b/lib/resty/apisix/ssl.lua @@ -5,21 +5,43 @@ local get_request = base.get_request local FFI_OK = base.FFI_OK local C = ffi.C local ffi_str = ffi.string +local subsystem = ngx.config.subsystem + + +base.allows_subsystem("http", "stream") + +local ngx_lua_ffi_apisix_set_gm_cert +local ngx_lua_ffi_apisix_set_gm_priv_key +local ngx_lua_ffi_apisix_enable_ntls + +if subsystem == "http" then + ffi.cdef[[ + typedef intptr_t ngx_flag_t; + int ngx_http_apisix_set_gm_cert(void *r, void *cdata, char **err, ngx_flag_t type); + int ngx_http_apisix_set_gm_priv_key(void *r, void *cdata, char **err, ngx_flag_t type); + int ngx_http_apisix_enable_ntls(void *r, int enabled); + ]] + + ngx_lua_ffi_apisix_set_gm_cert = C.ngx_http_apisix_set_gm_cert + ngx_lua_ffi_apisix_set_gm_priv_key = C.ngx_http_apisix_set_gm_priv_key + ngx_lua_ffi_apisix_enable_ntls = C.ngx_http_apisix_enable_ntls + +elseif subsystem == 'stream' then + ffi.cdef[[ + typedef intptr_t ngx_flag_t; + int ngx_stream_apisix_set_gm_cert(void *r, void *cdata, char **err, ngx_flag_t type); + int ngx_stream_apisix_set_gm_priv_key(void *r, void *cdata, char **err, ngx_flag_t type); + int ngx_stream_apisix_enable_ntls(void *r, int enabled); + ]] + + ngx_lua_ffi_apisix_set_gm_cert = C.ngx_stream_apisix_set_gm_cert + ngx_lua_ffi_apisix_set_gm_priv_key = C.ngx_stream_apisix_set_gm_priv_key + ngx_lua_ffi_apisix_enable_ntls = C.ngx_stream_apisix_enable_ntls +end -base.allows_subsystem("http") - - -ffi.cdef[[ - typedef intptr_t ngx_flag_t; - int ngx_http_apisix_set_gm_cert(void *r, void *cdata, char **err, ngx_flag_t type); - int ngx_http_apisix_set_gm_priv_key(void *r, void *cdata, char **err, ngx_flag_t type); - int ngx_http_apisix_enable_ntls(void *r, int enabled); -]] - - -local NGX_HTTP_APISIX_SSL_ENC = 1 -local NGX_HTTP_APISIX_SSL_SIGN = 2 +local NGX_APISIX_SSL_ENC = 1 +local NGX_APISIX_SSL_SIGN = 2 local _M = {} @@ -29,12 +51,12 @@ function _M.set_gm_cert(enc_cert, sign_cert) error("no request found") end - local rc = C.ngx_http_apisix_set_gm_cert(r, enc_cert, errmsg, NGX_HTTP_APISIX_SSL_ENC) + local rc = ngx_lua_ffi_apisix_set_gm_cert(r, enc_cert, errmsg, NGX_APISIX_SSL_ENC) if rc ~= FFI_OK then return nil, ffi_str(errmsg[0]) end - local rc = C.ngx_http_apisix_set_gm_cert(r, sign_cert, errmsg, NGX_HTTP_APISIX_SSL_SIGN) + local rc = ngx_lua_ffi_apisix_set_gm_cert(r, sign_cert, errmsg, NGX_APISIX_SSL_SIGN) if rc ~= FFI_OK then return nil, ffi_str(errmsg[0]) end @@ -49,12 +71,12 @@ function _M.set_gm_priv_key(enc_pkey, sign_pkey) error("no request found") end - local rc = C.ngx_http_apisix_set_gm_priv_key(r, enc_pkey, errmsg, NGX_HTTP_APISIX_SSL_ENC) + local rc = ngx_lua_ffi_apisix_set_gm_priv_key(r, enc_pkey, errmsg, NGX_APISIX_SSL_ENC) if rc ~= FFI_OK then return nil, ffi_str(errmsg[0]) end - local rc = C.ngx_http_apisix_set_gm_priv_key(r, sign_pkey, errmsg, NGX_HTTP_APISIX_SSL_SIGN) + local rc = ngx_lua_ffi_apisix_set_gm_priv_key(r, sign_pkey, errmsg, NGX_APISIX_SSL_SIGN) if rc ~= FFI_OK then return nil, ffi_str(errmsg[0]) end @@ -69,7 +91,7 @@ function _M.enable_ntls() error("no request found") end - C.ngx_http_apisix_enable_ntls(r, 1) + ngx_lua_ffi_apisix_enable_ntls(r, 1) end @@ -79,7 +101,7 @@ function _M.disable_ntls() error("no request found") end - C.ngx_http_apisix_enable_ntls(r, 0) + ngx_lua_ffi_apisix_enable_ntls(r, 0) end diff --git a/patch/1.19.3/nginx-stream-enable_ntls.patch b/patch/1.19.3/nginx-stream-enable_ntls.patch new file mode 100644 index 0000000..6d0abc1 --- /dev/null +++ b/patch/1.19.3/nginx-stream-enable_ntls.patch @@ -0,0 +1,29 @@ +diff --git a/src/stream/ngx_stream_ssl_module.c b/src/stream/ngx_stream_ssl_module.c +index 79f30a86..d39c11fc 100644 +--- a/src/stream/ngx_stream_ssl_module.c ++++ b/src/stream/ngx_stream_ssl_module.c +@@ -8,6 +8,10 @@ + #include + #include + #include ++#if (NGX_STREAM_APISIX) ++// #include ++#include ++#endif + + + typedef ngx_int_t (*ngx_ssl_variable_handler_pt)(ngx_connection_t *c, +@@ -375,6 +379,13 @@ ngx_stream_ssl_init_connection(ngx_ssl_t *ssl, ngx_connection_t *c) + return NGX_ERROR; + } + ++#if (TONGSUO_VERSION_NUMBER && NGX_STREAM_APISIX) ++ if (ngx_stream_apisix_is_ntls_enabled(s)) { ++ SSL_enable_ntls(c->ssl->connection); ++ // fprintf(stderr, "==ngx_stream_apisix_is_ntls_enabled:SSL_enable_ntls==="); ++ } ++#endif ++ + rc = ngx_ssl_handshake(c); + + if (rc == NGX_ERROR) { diff --git a/src/stream/ngx_stream_apisix_module.c b/src/stream/ngx_stream_apisix_module.c index f21654e..ca7a98e 100644 --- a/src/stream/ngx_stream_apisix_module.c +++ b/src/stream/ngx_stream_apisix_module.c @@ -1,18 +1,31 @@ #include #include +// #include "../ngx_stream_lua_common.h" #include "ngx_stream_apisix_module.h" +#define NGX_STREAM_APISIX_SSL_ENC 1 +#define NGX_STREAM_APISIX_SSL_SIGN 2 + + +typedef struct { + ngx_flag_t enable_ntls; +} ngx_stream_apisix_main_conf_t; + + typedef struct { unsigned proxy_ssl_enabled:1; } ngx_stream_apisix_ctx_t; +static void *ngx_stream_apisix_create_main_conf(ngx_conf_t *cf); + + static ngx_stream_module_t ngx_stream_apisix_module_ctx = { NULL, /* preconfiguration */ NULL, /* postconfiguration */ - NULL, /* create main configuration */ + ngx_stream_apisix_create_main_conf, /* create main configuration */ NULL, /* init main configuration */ NULL, /* create server configuration */ @@ -36,6 +49,20 @@ ngx_module_t ngx_stream_apisix_module = { }; +static void * +ngx_stream_apisix_create_main_conf(ngx_conf_t *cf) +{ + ngx_stream_apisix_main_conf_t *conf; + + conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_apisix_main_conf_t)); + if (conf == NULL) { + return NULL; + } + + return conf; +} + + ngx_int_t ngx_stream_apisix_upstream_enable_tls(ngx_stream_lua_request_t *r) { @@ -66,3 +93,159 @@ ngx_stream_apisix_is_proxy_ssl_enabled(ngx_stream_session_t *s) return ctx != NULL && ctx->proxy_ssl_enabled; } + + + +int +ngx_stream_apisix_set_gm_cert(ngx_stream_lua_request_t *r, void *cdata, char **err, ngx_flag_t type) +{ +#ifndef TONGSUO_VERSION_NUMBER + + *err = "only Tongsuo supported"; + return NGX_ERROR; + +#else + int i; + X509 *x509 = NULL; + ngx_ssl_conn_t *ssl_conn; + STACK_OF(X509) *chain = cdata; + + if (r->connection == NULL || r->connection->ssl == NULL) { + *err = "bad request"; + return NGX_ERROR; + } + + ssl_conn = r->connection->ssl->connection; + if (ssl_conn == NULL) { + *err = "bad ssl conn"; + return NGX_ERROR; + } + + if (sk_X509_num(chain) < 1) { + *err = "invalid certificate chain"; + goto failed; + } + + x509 = sk_X509_value(chain, 0); + if (x509 == NULL) { + *err = "sk_X509_value() failed"; + goto failed; + } + + if (type == NGX_STREAM_APISIX_SSL_ENC) { + if (SSL_use_enc_certificate(ssl_conn, x509) == 0) { + *err = "SSL_use_enc_certificate() failed"; + goto failed; + } + } else { + if (SSL_use_sign_certificate(ssl_conn, x509) == 0) { + *err = "SSL_use_sign_certificate() failed"; + goto failed; + } + } + + x509 = NULL; + + /* read rest of the chain */ + + for (i = 1; i < sk_X509_num(chain); i++) { + + x509 = sk_X509_value(chain, i); + if (x509 == NULL) { + *err = "sk_X509_value() failed"; + goto failed; + } + + if (SSL_add1_chain_cert(ssl_conn, x509) == 0) { + *err = "SSL_add1_chain_cert() failed"; + goto failed; + } + } + + *err = NULL; + return NGX_OK; + +failed: + + ERR_clear_error(); + + return NGX_ERROR; + +#endif +} + + +int +ngx_stream_apisix_set_gm_priv_key(ngx_stream_lua_request_t *r, + void *cdata, char **err, ngx_flag_t type) +{ +#ifndef TONGSUO_VERSION_NUMBER + + *err = "only Tongsuo supported"; + return NGX_ERROR; + +#else + + EVP_PKEY *pkey = NULL; + ngx_ssl_conn_t *ssl_conn; + + if (r->connection == NULL || r->connection->ssl == NULL) { + *err = "bad request"; + return NGX_ERROR; + } + + ssl_conn = r->connection->ssl->connection; + if (ssl_conn == NULL) { + *err = "bad ssl conn"; + return NGX_ERROR; + } + + pkey = cdata; + if (pkey == NULL) { + *err = "invalid private key failed"; + goto failed; + } + + if (type == NGX_STREAM_APISIX_SSL_ENC) { + if (SSL_use_enc_PrivateKey(ssl_conn, pkey) == 0) { + *err = "SSL_use_enc_PrivateKey() failed"; + goto failed; + } + } else { + if (SSL_use_sign_PrivateKey(ssl_conn, pkey) == 0) { + *err = "SSL_use_sign_PrivateKey() failed"; + goto failed; + } + } + + return NGX_OK; + +failed: + + ERR_clear_error(); + + return NGX_ERROR; + +#endif +} + + +int +ngx_stream_apisix_enable_ntls(ngx_stream_lua_request_t *r, int enabled) +{ + ngx_stream_apisix_main_conf_t *acf; + + acf = ngx_stream_get_module_main_conf(r->session, ngx_stream_apisix_module); + acf->enable_ntls = enabled; + return NGX_OK; +} + + +ngx_flag_t +ngx_stream_apisix_is_ntls_enabled(ngx_stream_session_t *s) +{ + ngx_stream_apisix_main_conf_t *acf; + + acf = ngx_stream_get_module_main_conf(s, ngx_stream_apisix_module); + return acf->enable_ntls; +} diff --git a/src/stream/ngx_stream_apisix_module.h b/src/stream/ngx_stream_apisix_module.h index a8fcdc8..1e89b05 100644 --- a/src/stream/ngx_stream_apisix_module.h +++ b/src/stream/ngx_stream_apisix_module.h @@ -6,6 +6,6 @@ ngx_int_t ngx_stream_apisix_is_proxy_ssl_enabled(ngx_stream_session_t *s); - +ngx_flag_t ngx_stream_apisix_is_ntls_enabled(ngx_stream_session_t *s); #endif /* _NGX_STREAM_APISIX_H_INCLUDED_ */ diff --git a/t/stream/gm.t b/t/stream/gm.t new file mode 100644 index 0000000..a829302 --- /dev/null +++ b/t/stream/gm.t @@ -0,0 +1,155 @@ +use t::APISIX_NGINX; + +my $openssl_version = eval { `/export/servers/tongsuo_jfe/bin/openssl version 2>&1` }; +if ($openssl_version !~ m/Tongsuo/) { + plan(skip_all => "need Tongsuo"); +} else { + plan 'no_plan'; +} + + +add_block_preprocessor(sub { + my ($block) = @_; + + my $stream_config = $block->stream_config // ''; + $stream_config .= <<_EOC_; + init_by_lua_block { + function set_gm_cert_and_key() + local ngx_ssl = require "ngx.ssl" + local ssl = require "resty.apisix.ssl" + + ngx_ssl.clear_certs() + + local f = assert(io.open("t/certs/server_enc.crt")) + local cert_enc = f:read("*a") + f:close() + + local cert_enc, err = ngx_ssl.parse_pem_cert(cert_enc) + if not cert_enc then + ngx.log(ngx.ERR, "failed to parse pem cert: ", err) + return + end + + local f = assert(io.open("t/certs/server_sign.crt")) + local cert_sign = f:read("*a") + f:close() + + local cert_sign, err = ngx_ssl.parse_pem_cert(cert_sign) + if not cert_enc then + ngx.log(ngx.ERR, "failed to parse pem cert: ", err) + return + end + + local ok, err = ssl.set_gm_cert(cert_enc, cert_sign) + if not ok then + ngx.log(ngx.ERR, "failed to set cert: ", err) + return + end + + local f = assert(io.open("t/certs/server_enc.key")) + local pkey_data = f:read("*a") + f:close() + + local pkey_enc, err = ngx_ssl.parse_pem_priv_key(pkey_data) + if not pkey_enc then + ngx.log(ngx.ERR, "failed to parse pem key: ", err) + return + end + + local f = assert(io.open("t/certs/server_sign.key")) + local pkey_data = f:read("*a") + f:close() + + local pkey_sign, err = ngx_ssl.parse_pem_priv_key(pkey_data) + if not pkey_sign then + ngx.log(ngx.ERR, "failed to parse pem key: ", err) + return + end + + local ok, err = ssl.set_gm_priv_key(pkey_enc, pkey_sign) + if not ok then + ngx.log(ngx.ERR, "failed to set private key: ", err) + return + end + ngx.log(ngx.WARN, "====set_gm_cert_and_key done==") + end + } +_EOC_ + + $block->set_value("stream_config", $stream_config); + + my $http_config = $block->http_config // ''; + $http_config .= <<_EOC_; + init_by_lua_block { + function handshake() + local req = "'ping\\r\\n'" + local cmd = "/export/servers/tongsuo_jfe/bin/openssl s_client -connect 127.0.0.1:1986 " .. + "-enable_ntls -ntls -verifyCAfile t/certs/gm_ca.crt" + return io.popen("echo -n " .. req .. " | timeout 3s " .. cmd) + end + } +_EOC_ + + $block->set_value("http_config", $http_config); +}); + +run_tests(); + +__DATA__ + +=== TEST 1: gm handshake +--- stream_config + init_worker_by_lua_block { + local ssl = require "resty.apisix.ssl" + ssl.enable_ntls() + } + + server { + listen 1986 ssl; + + ssl_certificate_by_lua_block { + set_gm_cert_and_key() + } + ssl_certificate ../../certs/apisix.crt; + ssl_certificate_key ../../certs/apisix.key; + + content_by_lua_block { + ngx.say("PONG") + } + } + +--- stream_server_config + content_by_lua_block { + ngx.say("OK") + } + +--- config + server_tokens off; + + location /test { + content_by_lua_block { + local f, err = handshake() + ngx.log(ngx.INFO, "===call handshake==") + if not f then + ngx.say(err) + return + end + ngx.sleep(5) + ngx.log(ngx.INFO, "===read handshake==") + local out = f:read('*a') + ngx.log(ngx.INFO, "===out==", out) + ngx.say("ok") + f:close() + return + } + } + +--- request +GET /test +--- timeout: 10s +--- error_log_like +New, NTLSv1.1, Cipher is *-SM2-SM4-*-SM3 +--- no_error_log +[error] +[alert] +[emerg] \ No newline at end of file