Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement mruby_ssl_handshake_handler() #205

Merged
merged 7 commits into from Aug 24, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions mruby_test/build_config.rb
Expand Up @@ -29,6 +29,7 @@
conf.gem :github => 'iij/mruby-io'
conf.gem :github => 'iij/mruby-socket'
conf.gem :github => 'iij/mruby-pack'
conf.gem :github => 'iij/mruby-env'
# include the default GEMs
conf.gembox 'full-core'

Expand Down
71 changes: 66 additions & 5 deletions src/http/ngx_http_mruby_module.c
Expand Up @@ -96,6 +96,7 @@ static char *ngx_http_mruby_init_worker_inline(ngx_conf_t *cf, ngx_command_t *cm
static char *ngx_http_mruby_exit_worker_phase(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static char *ngx_http_mruby_exit_worker_inline(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
#if (NGX_HTTP_SSL)
static char *ngx_http_mruby_ssl_handshake_phase(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static char *ngx_http_mruby_ssl_handshake_inline(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
#endif /* NGX_HTTP_SSL */

Expand Down Expand Up @@ -170,6 +171,9 @@ static ngx_command_t ngx_http_mruby_commands[] = {
#if (NGX_HTTP_SSL)

/* server config */
{ngx_string("mruby_ssl_handshake_handler"), NGX_HTTP_SRV_CONF | NGX_CONF_TAKE12, ngx_http_mruby_ssl_handshake_phase,
NGX_HTTP_SRV_CONF_OFFSET, 0, NULL},

{ngx_string("mruby_ssl_handshake_handler_code"), NGX_HTTP_SRV_CONF | NGX_CONF_TAKE1,
ngx_http_mruby_ssl_handshake_inline, NGX_HTTP_SRV_CONF_OFFSET, 0, NULL},

Expand Down Expand Up @@ -356,6 +360,7 @@ static void ngx_http_mruby_srv_conf_cleanup(void *data)
ngx_http_mruby_srv_conf_t *mscf = data;

NGX_MRUBY_CODE_MRBC_CONTEXT_FREE(mscf->state->mrb, mscf->ssl_handshake_code);
NGX_MRUBY_CODE_MRBC_CONTEXT_FREE(mscf->state->mrb, mscf->ssl_handshake_inline_code);
}

static void *ngx_http_mruby_create_srv_conf(ngx_conf_t *cf)
Expand Down Expand Up @@ -383,6 +388,7 @@ static void *ngx_http_mruby_create_srv_conf(ngx_conf_t *cf)
mscf->cert_data.len = 0;
mscf->cert_key_data.len = 0;
mscf->ssl_handshake_code = NGX_CONF_UNSET_PTR;
mscf->ssl_handshake_inline_code = NGX_CONF_UNSET_PTR;

cln->handler = ngx_http_mruby_srv_conf_cleanup;
cln->data = mscf;
Expand All @@ -399,8 +405,9 @@ static char *ngx_http_mruby_merge_srv_conf(ngx_conf_t *cf, void *parent, void *c
ngx_http_ssl_srv_conf_t *sscf;

NGX_MRUBY_MERGE_CODE(conf->ssl_handshake_code, prev->ssl_handshake_code);
NGX_MRUBY_MERGE_CODE(conf->ssl_handshake_inline_code, prev->ssl_handshake_inline_code);

if (conf->ssl_handshake_code != NGX_CONF_UNSET_PTR) {
if (conf->ssl_handshake_code != NGX_CONF_UNSET_PTR || conf->ssl_handshake_inline_code != NGX_CONF_UNSET_PTR) {
sscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_ssl_module);
if (sscf == NULL || sscf->ssl.ctx == NULL) {
ngx_log_error(NGX_LOG_EMERG, cf->log, 0, MODULE_NAME " : no ssl configured for the server");
Expand Down Expand Up @@ -1033,7 +1040,7 @@ static ngx_int_t ngx_http_mruby_shared_state_compile(ngx_conf_t *cf, ngx_mrb_sta

#if (NGX_HTTP_SSL)

static char *ngx_http_mruby_ssl_handshake_inline(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
static char *ngx_http_mruby_ssl_handshake_phase(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_mruby_srv_conf_t *mscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_mruby_module);
ngx_http_mruby_main_conf_t *mmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_mruby_module);
Expand All @@ -1050,12 +1057,55 @@ static char *ngx_http_mruby_ssl_handshake_inline(ngx_conf_t *cf, ngx_command_t *

value = cf->args->elts;

code = ngx_http_mruby_mrb_code_from_string(cf->pool, &value[1]);
code = ngx_http_mruby_mrb_code_from_file(cf->pool, &value[1]);
if (code == NGX_CONF_UNSET_PTR) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, MODULE_NAME " : mruby_ssl_handshake_phase mrb_file(%s) open failed",
value[1].data);
return NGX_CONF_ERROR;
}
if (cf->args->nelts == 3) {
if (ngx_strcmp(value[2].data, "cache") == 0) {
code->cache = ON;
} else {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%V\", vaild parameter is only \"cache\"",
&value[2]);
return NGX_CONF_ERROR;
}
}
mscf->ssl_handshake_code = code;
rc = ngx_http_mruby_shared_state_compile(cf, mscf->state, code);
if (rc != NGX_OK) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, MODULE_NAME " : mruby_ssl_handshake_phase mrb_file(%s) open failed",
value[1].data);
return NGX_CONF_ERROR;
}

return NGX_CONF_OK;
}

static char *ngx_http_mruby_ssl_handshake_inline(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_mruby_srv_conf_t *mscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_mruby_module);
ngx_http_mruby_main_conf_t *mmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_mruby_module);
ngx_str_t *value;
ngx_mrb_code_t *code;
ngx_int_t rc;

if (mscf->ssl_handshake_inline_code != NGX_CONF_UNSET_PTR) {
return "is duplicated";
}

/* share mrb_state of preinit */
mscf->state = mmcf->state;

value = cf->args->elts;

code = ngx_http_mruby_mrb_code_from_string(cf->pool, &value[1]);
if (code == NGX_CONF_UNSET_PTR) {
return NGX_CONF_ERROR;
}
mscf->ssl_handshake_inline_code = code;
rc = ngx_http_mruby_shared_state_compile(cf, mscf->state, code);
if (rc != NGX_OK) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, MODULE_NAME " : mruby_ssl_handshake_inline mrb_string(%s) load failed",
value[1].data);
Expand Down Expand Up @@ -2465,7 +2515,7 @@ static int ngx_http_mruby_ssl_cert_handler(ngx_ssl_conn_t *ssl_conn, void *data)
}
mscf->connection = c;

if (mscf->ssl_handshake_code == NGX_CONF_UNSET_PTR) {
if (mscf->ssl_handshake_code == NGX_CONF_UNSET_PTR && mscf->ssl_handshake_inline_code == NGX_CONF_UNSET_PTR) {
ngx_log_error(NGX_LOG_ERR, c->log, 0, MODULE_NAME " : mruby ssl handler: unexpected error, mruby code not found");
return 1;
}
Expand All @@ -2474,7 +2524,16 @@ static int ngx_http_mruby_ssl_cert_handler(ngx_ssl_conn_t *ssl_conn, void *data)
mrb = mscf->state->mrb;
mrb->ud = mscf;
ai = mrb_gc_arena_save(mrb);
mrb_run(mrb, mscf->ssl_handshake_code->proc, mrb_top_self(mrb));
if (mscf->ssl_handshake_code != NGX_CONF_UNSET_PTR) {
if (!mscf->ssl_handshake_code->cache) {
NGX_MRUBY_STATE_REINIT_IF_NOT_CACHED(0, mscf->state, mscf->ssl_handshake_code,
ngx_http_mruby_state_reinit_from_file);
}
mrb_run(mrb, mscf->ssl_handshake_code->proc, mrb_top_self(mrb));
}
if (mscf->ssl_handshake_inline_code != NGX_CONF_UNSET_PTR) {
mrb_run(mrb, mscf->ssl_handshake_inline_code->proc, mrb_top_self(mrb));
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When both ssl_handshake_code and ssl_handshake_inline_code have byte-code, these both byte-codes should run at this phase.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed. I'll fix it.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When don't use code cache (arg 3 = "cache"), we should recompile the ruby code, like the following:

ref: src/http/ngx_http_mruby_module.c

1879     if (!code->cache) {                                                                                                \
1880       NGX_MRUBY_STATE_REINIT_IF_NOT_CACHED(mlcf->cached, mmcf->state, code, ngx_http_mruby_state_reinit_from_file);    \
1881     }                                                                                                                  \
                                                                                                            \                                                                                                      


if (mrb->exc) {
struct RString *str;
Expand All @@ -2487,11 +2546,13 @@ static int ngx_http_mruby_ssl_cert_handler(ngx_ssl_conn_t *ssl_conn, void *data)
MODULE_NAME " : mrb_run failed: return 500 HTTP status code to client: error: %s", err_out);
}
NGX_MRUBY_CODE_MRBC_CONTEXT_FREE(mrb, mscf->ssl_handshake_code);
NGX_MRUBY_CODE_MRBC_CONTEXT_FREE(mrb, mscf->ssl_handshake_inline_code);
ngx_mrb_state_clean(NULL, mscf->state);
mrb_gc_arena_restore(mrb, ai);
return 0;
}
NGX_MRUBY_CODE_MRBC_CONTEXT_FREE(mrb, mscf->ssl_handshake_code);
NGX_MRUBY_CODE_MRBC_CONTEXT_FREE(mrb, mscf->ssl_handshake_inline_code);
ngx_mrb_state_clean(NULL, mscf->state);
mrb_gc_arena_restore(mrb, ai);

Expand Down
1 change: 1 addition & 0 deletions src/http/ngx_http_mruby_module.h
Expand Up @@ -56,6 +56,7 @@ extern ngx_module_t ngx_http_mruby_module;
typedef struct {
ngx_mrb_state_t *state;
ngx_mrb_code_t *ssl_handshake_code;
ngx_mrb_code_t *ssl_handshake_inline_code;
ngx_str_t *servername;
ngx_str_t cert_path;
ngx_str_t cert_key_path;
Expand Down
1 change: 1 addition & 0 deletions test.sh
Expand Up @@ -114,6 +114,7 @@ if [ -n "$BUILD_DYNAMIC_MODULE" ]; then
fi

cp -pr test/html/* ${NGINX_INSTALL_DIR}/html/.
sed -e "s|__NGXDOCROOT__|${NGINX_INSTALL_DIR}/html/|g" test/html/set_ssl_cert_and_key.rb > ${NGINX_INSTALL_DIR}/html/set_ssl_cert_and_key.rb

echo "====================================="
echo ""
Expand Down
1 change: 1 addition & 0 deletions test/build_config.rb
Expand Up @@ -29,6 +29,7 @@
conf.gem :github => 'iij/mruby-io'
conf.gem :github => 'iij/mruby-socket'
conf.gem :github => 'iij/mruby-pack'
conf.gem :github => 'iij/mruby-env'
# include the default GEMs
conf.gembox 'full-core'

Expand Down
30 changes: 30 additions & 0 deletions test/conf/nginx.conf
Expand Up @@ -63,6 +63,36 @@ http {
}
}

server {
listen 58085 ssl;
server_name _;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_certificate __NGXDOCROOT__/dummy.crt;
ssl_certificate_key __NGXDOCROOT__/dummy.key;

mruby_ssl_handshake_handler build/nginx/html/set_ssl_cert_and_key.rb;

location / {
mruby_content_handler_code "Nginx.rputs 'ssl test ok'";
}
}

server {
listen 58086 ssl;
server_name _;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_certificate __NGXDOCROOT__/dummy.crt;
ssl_certificate_key __NGXDOCROOT__/dummy.key;

mruby_ssl_handshake_handler build/nginx/html/set_ssl_cert_and_key.rb cache;

location / {
mruby_content_handler_code "Nginx.rputs 'ssl test ok'";
}
}

server {
listen 58081;
server_name localhost;
Expand Down
3 changes: 3 additions & 0 deletions test/html/set_ssl_cert_and_key.rb
@@ -0,0 +1,3 @@
ssl = Nginx::SSL.new
ssl.certificate = "__NGXDOCROOT__/#{ssl.servername}.crt"
ssl.certificate_key = "__NGXDOCROOT__/#{ssl.servername}.key"
41 changes: 41 additions & 0 deletions test/t/ngx_mruby.rb
Expand Up @@ -412,6 +412,47 @@ def base_ssl(port)
t.assert_equal "", res
end

dir = ENV['BUILD_DYNAMIC_MODULE'] == 'TRUE' ? 'build_dynamic' : 'build'
fname = File.expand_path("../../../#{dir}/nginx/html/set_ssl_cert_and_key.rb", __FILE__)

t.assert('ngx_mruby - ssl certificate changing - reading handler from file without caching') do
res = `curl -k #{base_ssl(58085) + '/'}`
t.assert_equal 'ssl test ok', res

content = File.read(fname).gsub('#{ssl.servername}', 'localhost')
File.open(fname, 'w') { |f| f.puts content }

cmd_l = "openssl s_client -servername localhost -connect localhost:58085 < /dev/null 2> /dev/null | openssl x509 -text | grep Not | sed -e 's/://' | awk '{print (res = $6 - res)}' | tail -n 1"
cmd_h = "openssl s_client -servername hogehoge -connect 127.0.0.1:58085 < /dev/null 2> /dev/null | openssl x509 -text | grep Not | sed -e 's/://' | awk '{print (res = $6 - res)}' | tail -n 1"
t.assert_equal "1", `#{cmd_l}`.chomp
t.assert_equal "1", `#{cmd_h}`.chomp

content = File.read(fname).gsub('localhost', '#{ssl.servername}')
File.open(fname, 'w') { |f| f.puts content }

t.assert_equal "1", `#{cmd_l}`.chomp
t.assert_equal "", `#{cmd_h}`.chomp
end

t.assert('ngx_mruby - ssl certificate changing - reading handler from file with caching') do
res = `curl -k #{base_ssl(58086) + '/'}`
t.assert_equal 'ssl test ok', res

content = File.read(fname).gsub('#{ssl.servername}', 'localhost')
File.open(fname, 'w') { |f| f.puts content }

cmd_l = "openssl s_client -servername localhost -connect localhost:58086 < /dev/null 2> /dev/null | openssl x509 -text | grep Not | sed -e 's/://' | awk '{print (res = $6 - res)}' | tail -n 1"
cmd_h = "openssl s_client -servername hogehoge -connect 127.0.0.1:58086 < /dev/null 2> /dev/null | openssl x509 -text | grep Not"
t.assert_equal "1", `#{cmd_l}`.chomp
t.assert_equal "", `#{cmd_h}`.chomp

content = File.read(fname).gsub('localhost', '#{ssl.servername}')
File.open(fname, 'w') { |f| f.puts content }

t.assert_equal "1", `#{cmd_l}`.chomp
t.assert_equal "", `#{cmd_h}`.chomp
end

t.assert('ngx_mruby - issue_172', 'location /issue_172') do
res = HttpRequest.new.get base + '/issue_172/index.html'
expect_content = 'hello world'.upcase
Expand Down