Opened 3 years ago

Closed 13 months ago

#120 closed enhancement (fixed)

RFC5077 stateless tls session tickets

Reported by: Daniel Black Owned by: somebody
Priority: minor Milestone:
Component: nginx-module Version: 1.0.x
Keywords: ssl tls tickets session Cc:
Sensitive: no
uname -a:
nginx -V: not applicable

Description

As nginx's design wants to use constant memory allocating a large block of shared memory for session tickets isn't in keeping with that. In RFC5077 it describes how a web server needs to only maintain a small number of aes encryption keys (for allowing tls sessions always available as aes keys expire ) that are shared between all ssl session. The clients will maintain an initialisation vector.

OpenSSL has a callback SSL_CTX_set_tlsext_ticket_key_cb that came out in release 0.9.8h that assists with this function. Can't find its documentation? I wrote some for this: http://rt.openssl.org/Ticket/Display.html?id=2697

If client certificates are used then an amount of memory will need to map a client state to the client certificate (which won't be sent when ssl session tickets are used).

Attachments (3)

nginx-rfc5077-testplan.txt (32.4 KB) - added by launchpad.net/~daniel-black 2 years ago.
test plan
nginx.conf (1.1 KB) - added by launchpad.net/~daniel-black 2 years ago.
nginx configuration file for testing
nginx-rfc5077.patch (30.5 KB) - added by launchpad.net/~daniel-black 2 years ago.
the patch implementing rfc5077

Download all attachments as: .zip

Change History (13)

comment:1 follow-up: Changed 3 years ago by Maxim Dounin

Session tickets should work fine as OpenSSL initializes secrets by itself on process startup. Do you see any problems with them?

The SSL_CTX_set_tlsext_ticket_key_cb() (or SSL_CTX_set_tlsext_ticket_keys()) will be needed if/when we'll work on sharing tickets between multiple servers, but that's another story.

comment:2 in reply to: ↑ 1 Changed 3 years ago by Daniel Black

Replying to Maxim Dounin:

Session tickets should work fine as OpenSSL initializes secrets by itself on process startup. Do you see any problems with them?

I agree the builtin is fine if you have one worker process. I was looking at how the shared memory scales per number of web clients and a constant memory implemention using RFC5077 tickets seems ideal. There is an inbuilt "stateless" session ticket mechanism which looks fine (I did look at the code here but I haven't looked at what activates this).

The SSL_CTX_set_tlsext_ticket_key_cb()
(or SSL_CTX_set_tlsext_ticket_keys())

Another undocumented openssl function. Sigh.

The reason I like the callback is it allows the changing of session keys as they expire without requiring the clients go through a full tls renegotiation.

will be needed if/when we'll work on sharing tickets between multiple servers, but that's another story.

That's where I was heading as a goal. I'd come across Matt Palmer's implementation that did this https://github.com/mpalmer/nginx which is currently running as github's server. I thought this could be greatly improved by using a number of crypto variables shared via memcache. I've barely had time to crudely develop this so a first step of session tickets in shared memory is probably a first step.

I've done a crude bash at an implementation but I haven't got far due to time constraints in the last few months so I'd just thought I'd document the possibility in this ticket. I'm happy to share what I have but its only 1/2 way there.

comment:3 Changed 2 years ago by launchpad.net/~daniel-black

I came across http://vincent.bernat.im/en/blog/2011-ssl-session-reuse-rfc5077.html which gives a good foundation and a reasonable client side testing framework.

Looks like we need to support both session ids and session tickets and respond to session tickets if presented, with session id empty, and use session ids as a second preference.

The current session id implementation just seems to drop the session id if a client cert is presented. Session tickets get ugly with client side certificates as the server doesn't keep track of individual ids so this crude mechanism to ensure client certificates are available to the webserver and app isn't possible. I don't see a problem here as the app should be able to manage only seeing a certificate once and depend on cookies thereafter, it just needs to be documented. As a second iteration we could probably make this configurable.

As a third iteration we could cache the client cert by session id or/session ticket name/iv provided but this will get quite memory hungry compared to session ids.

Before I get ahead of myself, I've made some progress. Still working through the under-documented openssl APIs. I'll attach it once I get a base implementation working. Questions, guidance and work-in-progress code requests welcome.

comment:4 Changed 2 years ago by launchpad.net/~daniel-black

I'm pleased to attach a patch that implements rfc5077, session tickets for nginx.

Brief features of rfc5077:

  • Like session IDs, this allows client to resume tls sessions with a quicker startup latency by a full round trip. All of these clients can connect with the quicker session resumption.
  • This feature is TLS only and not in SSLv3.
  • A single AES key and HMAC on the server are used to protect session information of an unlimited number of clients that support the TLS extension session tickets.
  • The state, including the cryptographic variables, any client certificates or other stored authentication, or any element of client state, is stored encrypted on in the client browser.
  • The client stores the crypto state as an opaque blob encrypted by the AES key and validated by the HMAC in the server.

For full details, and perhaps more accurate information, read RFC5077.

Security considerations is described in rfc5077.

Client support:

Seems all Firefox and Chrome support it. IE doesn't yet according to:
http://vincent.bernat.im/en/blog/2011-ssl-session-reuse-rfc5077.html

IMPLEMENTATION:

This follows the recommended reference implementation of RFC5077 which are
largely part of the openssl libraries.

This patch uses a fixed 120 bytes of the ssl_session_cache for two session tickets.

The timeout from ssl_ticket_timeout is also used for session tickets defaulting to
one week (Based on the security considerations in 5.6 of the RFC5077).

The current full session ID is still available after applying this patch.

If session ticket fails due to any reason the session ID of the client is used
to store the session resumption.

The full ngx_ssl_tlsext_ticket_key_cb was implemented following a similar form
to the internal openssl. This was done so that a future extension could support
a shared cache between servers.

Consequential changes:

  • renamed the following to better represent their content:

ngx_ssl_session_cache_init -> ngx_ssl_cache_init
ngx_ssl_session_cache_t -> ngx_ssl_cache_t

ssl_ticket_timeout added to http and mail modules

  • data structures

changes to where ssl session id information is stored as both tickets and
sessions are now in the same structure.

  • logging/printing

string formatting was extended to allow printing of string buffers in hex.

  • removed ngx_ssl_server_conf as it wasn't being used

TICKET RENEWAL

The design was that two tickets have overlapping expire times so even if a
session resumption occurs within the expiry time the server will issue the next
session ticket anyway to allow the next connection of the client use a new ticket.

The tickets were designed to renew after 1/2 the timeout period has passed.

Unfortunately after testing clients the following clients it seems that all
abort with an unexpected tls message.

The following libraries were tested:

  • nss 3.13.5
  • gnutls 2.12.17
  • openssl 1.0.0j

Google Chrome Version 23.0.1271.10 dev also aborted connection though because of
its many connection policy it appears to have dropped its session ticket after
the connection was aborted and the new (concurrent possibly) connection picked
up the new ticket.

Looking at the Apache implementation of the callback
(httpd-trunk/modules/ssl/ssl_engine_kernel.c:ssl_callback_SessionTicket) the
resume option isn't used.

A #define NGX_RENEW_SSL_TICKET is included to enable the feature but commenting it
out will disable the renewal process and leave the tickets to expire.

SESSION ID AND SESSION TICKET BEHAVIOUR:

Clients presenting both session id and session tickets will be processed down the
session ticket path as per 3.4 of RFC5077. This is all handled internally within
openssl. As a result of this the session id callbacks will not be called if the
session ticket callback is successful.

TEST PLAN

The instructions to validate this implementation are attached.

QUESTIONS BASED ON IMPLEMENTATION

Session ticket resumption:

Since this isn't supported client side do you want it to be temporarily disabled
by:

  • manipulating ngx_ssl_ticket_valid to never indicate resumption? or
  • simplifying everything relevant to it?

Log detail:

I've included the AES, HMAC and iv key in the debug log. Acceptable?

Any other instances of too much/too little logging?

Changed 2 years ago by launchpad.net/~daniel-black

test plan

Changed 2 years ago by launchpad.net/~daniel-black

nginx configuration file for testing

Changed 2 years ago by launchpad.net/~daniel-black

the patch implementing rfc5077

comment:5 Changed 2 years ago by launchpad.net/~daniel-black

for convenience I've setup https://nginxtest.openquery.com with these test settings

comment:6 follow-up: Changed 2 years ago by Maxim Dounin

The question is: what's the real value added?

As far as I see, the only difference compared to what we have now is introduction of ssl_ticket_timeout directive, which allows to rotate session ticket keys, nothing more. This is certainly a valuable addition, but it's something you should be able to achieve with much less code.

It also looks like session tickets are now broken if there are multiple worker processes used and no shared ssl_session_cache configured.

And I don't see any attempt to address the problem of sharing session tickets secrets among multiple servers. As I already indicated, it's the only real problem with session tickets in nginx now.

Some quick notes about patch in general:

  1. Posting patches here is a wrong way to go. Recommended way to submit patches is to post them for review in nginx-devel@ mailing list.
  2. There are lots of unrelated changes and style problems in you patch. It's good idea to get rid of them. If you want some unrelated changes to happen - submit them as separate patches.
  3. Forget about threads, you don't need to care about them here.

comment:7 in reply to: ↑ 6 ; follow-up: Changed 2 years ago by launchpad.net/~daniel-black

Replying to Maxim Dounin:

The question is: what's the real value added?

That the server can now handle more session resumption clients using less shared memory. It also handles resumption for client side certificate and other bits abreviated in a session id based resumption. All session ticket capable client connections will not incur any extra shared memory usage. Because there's no memory cost for these they have a larger time out value allowing those clients to have a quicker resumption (one round trip quicker) when they connect within the ssl_ticket_timeout.

.... but it's something you should be able to achieve with much less code.

Without the resumption bit I guess it would be about the same size as httpd's callback:
http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/ssl/ssl_engine_kernel.c?view=markup line 2101.

I'm happy to start there.

It also looks like session tickets are now broken
if there are multiple worker processes used and no shared ssl_session_cache configured.

If you mean now before this patch is applied then you are probably right. The openssl library handles tickets internally if a callback isn't registered so the following is what I saw in Matt Palmers memcache patches:

/* TLS Session Tickets use a random encryption key which is different

  • per server. Clients will attempt to use this instead of server-side
  • sessions, defeating the purpose of caching sessions in the first place. */

SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_TICKET)

I'll include this in the next set of patches.

And I don't see any attempt to address the problem of sharing session tickets secrets among multiple servers. As I already indicated, it's the only real problem with session tickets in nginx now.

Haven't got to that bit. Thought it would make the patch too big. That and making an async process in the middle of a callback is a bit tricky. My two thoughts so far are a) setjmp/longjmp using the async callback to finish it, or b) some introspection before the ticket callback occurs.

Some quick notes about patch in general:

  1. Posting patches here is a wrong way to go. Recommended way to submit patches is to post them for review in nginx-devel@ mailing list.

Ok. I'll take your comments here on board and then send the patches there.

  1. There are lots of unrelated changes and style problems in you patch. It's good idea to get rid of them. If you want some unrelated changes to happen - submit them as separate patches.

Ok. I'll take this into account and also http://wiki.nginx.org/CodingStyle . If there's other style bits I'll look back there when cleaning this up.

  1. Forget about threads, you don't need to care about them here.

Great.

On session ticket resumption client side library errors:

comment:8 in reply to: ↑ 7 ; follow-up: Changed 2 years ago by Maxim Dounin

Replying to launchpad.net/~daniel-black:

Replying to Maxim Dounin:

The question is: what's the real value added?

That the server can now handle more session resumption clients using less shared memory. It also handles resumption for client side certificate and other bits abreviated in a session id based resumption. All session ticket capable client connections will not incur any extra shared memory usage. Because there's no memory cost for these they have a larger time out value allowing those clients to have a quicker resumption (one round trip quicker) when they connect within the ssl_ticket_timeout.

Compared to what we have now (i.e. session ticket support as provided by OpenSSL by default)? Sorry, I don't see how.

[...]

It also looks like session tickets are now broken
if there are multiple worker processes used and no shared ssl_session_cache configured.

If you mean now before this patch is applied then you are probably right.

I mean *with* the patch applied.

The openssl library handles tickets internally if a callback isn't registered so the following is what I saw in Matt Palmers memcache patches:

(Just a side note: Matt Palmer's patches are classic example of what shouldn't be done in nginx. He uses blocking remote memcached calls, and this is something one shouldn't even think about as this will block the whole nginx worker and all clients.)

As for session tickets, he disables them as keys aren't shared between multiple servers. This is wrong way though: instead of disabling session tickets, there should be a way provided to configure secret shared between multiple servers.

But again, the problem he tried to address only affects setups with multiple servers, not with a single nginx instance running (even if there are multiple worker processes configured).

And I don't see any attempt to address the problem of sharing session tickets secrets among multiple servers. As I already indicated, it's the only real problem with session tickets in nginx now.

Haven't got to that bit. Thought it would make the patch too big. That and making an async process in the middle of a callback is a bit tricky. My two thoughts so far are a) setjmp/longjmp using the async callback to finish it, or b) some introspection before the ticket callback occurs.

It would be easy enough to provide a way for *external* code to sync ticket keys, e.g. provide a configurable ticket key file much like Apache does.

As for patch size - it's always a good idea to code a patch series instead of a single patch if the problem appears to be big.

comment:9 in reply to: ↑ 8 Changed 2 years ago by launchpad.net/~daniel-black

I posted to the list before I saw this.

Replying to Maxim Dounin:

Compared to what we have now (i.e. session ticket support as provided by OpenSSL by default)? Sorry, I don't see how.

With multiple workers using shared memory this actually works? I'll need to check scenarios here.

It also looks like session tickets are now broken
if there are multiple worker processes used and no shared ssl_session_cache configured.

If you mean now before this patch is applied then you are probably right.

I mean *with* the patch applied.

Ok will do more testing.

It would be easy enough to provide a way for *external* code to sync ticket keys, e.g. provide a configurable ticket key file much like Apache does.

Interesting.

As for patch size - it's always a good idea to code a patch series instead of a single patch if the problem appears to be big.

Hopefully the list of patches to the dev list is better. Thanks.

comment:10 Changed 13 months ago by Maxim Dounin

  • Resolution set to fixed
  • Sensitive unset
  • Status changed from new to closed

The patch from Piotr Sikora was committed to allow sharing Session Ticket keys between multiple servers, see 1356a3b96924.

Note: See TracTickets for help on using tickets.