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鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: add client SSL certificiate support #957

Closed
wants to merge 5 commits into from

Conversation

detailyang
Copy link
Contributor

@detailyang detailyang commented Jan 16, 2017

I hereby granted the copyright of the changes in this pull request
to the authors of this lua-nginx-module project.

@agentzh This feature is talked at openresty/issues/222 and be listed in TODO list

Now I just add the api and tests but lack document. When we think it can be merged to master, i will supplement the api usage later.

Please help me review this PR, many thanks 馃榿




=== TEST 33: setsslcert, too many arguments
Copy link
Member

Choose a reason for hiding this comment

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

These new tests should go to a separate test file. This 129-ssl-socket.t file is already quite large.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

well, how about 151-ssl-client-setcert.t?

Copy link
Contributor

Choose a reason for hiding this comment

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

Should be fine.

local cert = read_file("$TEST_NGINX_HTML_DIR/client.crt")
local key = read_file("$TEST_NGINX_HTML_DIR/client.unsecure.key")

local ok, err = sock:setsslcert(cert, key)
Copy link
Member

Choose a reason for hiding this comment

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

I'm thinking about a different Lua API than this. Loading the raw certificates upon every SSL cosocket is quite expensive and the client-side certificates tend to be reused over and over again. I think we can introduce an opaque SSL ctx object that loads the certificates only once and for each new SSL cosocket, we just feed the SSL ctx object to it directly. Other custom SSL configurations added in the future can be configured in a similar way. What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@agentzh I agree Loading the raw certificates upon every SSL cosocket is quite expensive and the client-side certificates tend to be reused over and over again.So how about the new directivelua_ssl_client_keyandlua_ssl_client_certificate` to feed SSL ctx object to it directly? Also can we provide this Lua API to allow user to set different certificates on the fly?

Just now i misunderstand what you said, now i do understand. I agree to add new Lua API to be easily configure custom SSL configurations, And do you have a good suggestion about the new Lua API?

Copy link
Contributor

Choose a reason for hiding this comment

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

I understand the loading part that @agentzh as actually parsing the certificates (which would be solved by re-using the existing API as I already commented).

However I also think that adding the certificate to the "global" SSL_CTX is wrong, since that would affect any other SSL objects created from it (past and present), so having an API to create a new SSL_CTX and apply configurations to it would be best.

@detailyang
Copy link
Contributor Author

detailyang commented Jan 28, 2017

@agentzh so sorry long time to reply.

I know you are thinking a different Lua API. Can you explain to me what's the different Lua API so that to change this codebase? Now I have enough time to follow your minds.

By the way, happy new year 2017:)

@agentzh
Copy link
Member

agentzh commented Jan 30, 2017

@detailyang I'm not exactly sure at this point. Maybe something like this?

local ssl = require "ngx.ssl"
local ctx = ssl.create_ctx{
    client_certificate = "pem-data",
}

-- we can cache the ctx object in a cache like lua-resty-lrucache

local sock = ngx.socket.tcp()
local ok, err = sock:setsslctx(ctx)
sock:sslhandshake(...)

@lziest @ghedo @doujiang24 What do you think?

@detailyang
Copy link
Contributor Author

@agentzh well, it looks like not bad. I will attempt to implement it in localhost

* Reading the PEM-formatted certificate from memory into an X509
*/

x509 = PEM_read_bio_X509(cbio, NULL, 0, NULL);
Copy link
Contributor

Choose a reason for hiding this comment

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

Given that we already have PEM (and DER) parsing functions, should users maybe use those and pass the already parsed chain to this function?

Copy link
Contributor Author

@detailyang detailyang Feb 6, 2017

Choose a reason for hiding this comment

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

@ghedo Yesterday I have try to reuse the already parsed chain. Finally I have found these data is type of Luajit's cdata and we cannot reference these data in Lua CFunction.:(

@ghedo
Copy link
Contributor

ghedo commented Feb 2, 2017

@agentzh yes, that looks better, since it would avoid setting the certificate for the global SSL_CTX (so other SSLs wouldn't get the same configuration). An alternative API would be having a ngx_http_lua_socket_tcp_sslhandshake_with_ctx() which would be passed an ctx explicitly, and then reimplement ngx_http_lua_socket_tcp_sslhandshake() so that it would only call that. Would still need something to create the SSL_CTX in the first place.

goto done;
}

pkey = PEM_read_bio_PrivateKey(pbio, NULL,
Copy link
Contributor

@ghedo ghedo Feb 2, 2017

Choose a reason for hiding this comment

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

As above. We don't yet support parsing keys with passwords, but that should be added to the existing API instead IMO, so it would be more generally useful. Or better yet, add an additional function (e.g. ngx_http_lua_ffi_parse_pem_priv_key_with_password()) to do just that.

Copy link
Contributor

Choose a reason for hiding this comment

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

Also, I think this should be a separate PR, so this one would get smaller and easier to review.

@agentzh
Copy link
Member

agentzh commented Feb 2, 2017

@ghedo Agreed :)

@detailyang
Copy link
Contributor Author

@ghedo @agentzh Thanks for yours eager suggestion. I guess I need more time to finish this feature (so sorry for my level). So just want to finish these easy parts pointed by @ghedo (parsing the private key with password), then do other parts.

I wil create another PR to add the existing API (ngx_http_lua_ffi_parse_pem_priv_key) or creating additional api (ngx_http_lua_ffi_parse_pem_priv_key_with_password) for parsing the private key with password.

What do you thinks of this?

@detailyang
Copy link
Contributor Author

@agentzh @lziest @ghedo

Hello, guys. I have successfully run a demo for Lua API to feed SSL Secure Context Object. For reduce waste yours time to review, I wish we can agree on the Lua API design.

Recently I I have try to implement two versions Lua API to feed SSL_CTX* object as the following:

  1. userdata version
local ctx = ngx.ssl.create_ctx{
    client_certificate = "pem-data",
}

-- we can cache the ctx object in a cache like lua-resty-lrucache

local sock = ngx.socket.tcp()
local ok, err = sock:setsslctx(ctx)
sock:sslhandshake(...)

This version ssl.create_ctx will return Userdata from Lua stack, it is enough. But if we want to feed more arguments to SSL_CTX * in future, we will have to add more option for options table and the create_ctx CFunction will become more and more large.

  1. table version like ngx.socket.tcp
local ctx = ngx.ssl.ctx()
local ok, err = ctx:init({
    method = 'xxx', -- the openssl protocol methods
    cert = 'xxx', -- cert chains in PEM format 
    key = 'xxx', --  private keys in PEM format
    cacert = 'xxx'  -- the trusted CA certificates
})

if not ok then
    return error("init ssl ctx error: " .. err)
end

-- we can cache the ctx table in a cache like lua-resty-lrucache

local sock = ngx.socket.tcp()
local ok, err = sock:setsslctx(ctx)
sock:sslhandshake(...)

This version ssl.create_ctx will return Table from Lua stack, it wraps SSL_CTX * to table. This is more extended. If we want to feed more arguments to SSL_CTX *, we just add new CFunction to ctx table metatable like the following:

local ctx = ngx.ssl.ctx()
local ok, err = ctx:init({
    method = 'xxx', -- the openssl protocol methods default SSLv23_method
    cert = 'xxx', -- cert chains in PEM format 
    key = 'xxx', --  private keys in PEM format
    cacert = 'xxx' -- the trusted CA certificates in PEM format
})

if not ok then
    return error("init ssl ctx error: " .. err)
end

--we can add more api in future as the following

ctx:set_key()
ctx:set_cert()
ctx:add_cacert()
ctx:add_crl()
ctx:set_ciphres()
ctx:set_ecdhcurve()
ctx:set_dhparam()

Also I refer to the Node.js JS API Design and Node.js C++ API Design as a guide to our Lua API.

Which one is better? Need yours feedback and let us make a choice :D

@detailyang
Copy link
Contributor Author

detailyang commented Feb 14, 2017

Ping @agentzh @doujiang24 @thibaultcha @lziest @ghedo. Can you have a look at the above, If you are free now. Thanks :D

@agentzh
Copy link
Member

agentzh commented Feb 14, 2017

@detailyang Sorry for the delay on my side. Been massively distracted. Regarding the API design, I don't quite like the ctx.init() syntax. It should be in the ngx.ssl module of lua-resty-core. As I've demonstrated before:

local ssl = require "ngx.ssl"
local ctx = ssl.create_ctx{
    client_certificate = "pem-data",
}

-- we can cache the ctx object in a cache like lua-resty-lrucache

local sock = ngx.socket.tcp()
local ok, err = sock:setsslctx(ctx)
sock:sslhandshake(...)

But yeah, we'll have to implement the setsslctx function atop FFI instead of as a CFunction.

The ctx data returned by ssl.create_ctx() is an opaque pointer as an FFI cdata object.

@detailyang
Copy link
Contributor Author

detailyang commented Feb 14, 2017

@agentzh Thanks for reply me. I have try to implement the setsslctx function atop FFI. But I'm failed because the tcp:sslhandshake is the CFunction. I'm confused that how can we pass the ctx cdata to tcp:sslhandshake CFunction. Can you give me a little hint ? After carefully rereading what you have said, I have understand. Thanks you :D

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants