SSL: save sessions for upstream peers using a callback function.
In TLSv1.3, NewSessionTicket messages arrive after the handshake and
can come at any time. Therefore we use a callback to save the session
when we know about it. This approach works for < TLSv1.3 as well.
The callback function is set once per location on merge phase.
Since SSL_get_session() in BoringSSL returns an unresumable session for
TLSv1.3, peer save_session() methods have been updated as well to use a
session supplied within the callback. To preserve API, the session is
cached in c->ssl->session. It is preferably accessed in save_session()
methods by ngx_ssl_get_session() and ngx_ssl_get0_session() wrappers.
Sergey Kandaurov
3 years ago
23 | 23 | static void ngx_ssl_info_callback(const ngx_ssl_conn_t *ssl_conn, int where, |
24 | 24 | int ret); |
25 | 25 | static void ngx_ssl_passwords_cleanup(void *data); |
26 | static int ngx_ssl_new_client_session(ngx_ssl_conn_t *ssl_conn, | |
27 | ngx_ssl_session_t *sess); | |
26 | 28 | static void ngx_ssl_handshake_handler(ngx_event_t *ev); |
27 | 29 | static ngx_int_t ngx_ssl_handle_recv(ngx_connection_t *c, int n); |
28 | 30 | static void ngx_ssl_write_handler(ngx_event_t *wev); |
1161 | 1163 | |
1162 | 1164 | |
1163 | 1165 | ngx_int_t |
1166 | ngx_ssl_client_session_cache(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_uint_t enable) | |
1167 | { | |
1168 | if (!enable) { | |
1169 | return NGX_OK; | |
1170 | } | |
1171 | ||
1172 | SSL_CTX_set_session_cache_mode(ssl->ctx, | |
1173 | SSL_SESS_CACHE_CLIENT | |
1174 | |SSL_SESS_CACHE_NO_INTERNAL); | |
1175 | ||
1176 | SSL_CTX_sess_set_new_cb(ssl->ctx, ngx_ssl_new_client_session); | |
1177 | ||
1178 | return NGX_OK; | |
1179 | } | |
1180 | ||
1181 | ||
1182 | static int | |
1183 | ngx_ssl_new_client_session(ngx_ssl_conn_t *ssl_conn, ngx_ssl_session_t *sess) | |
1184 | { | |
1185 | ngx_connection_t *c; | |
1186 | ||
1187 | c = ngx_ssl_get_connection(ssl_conn); | |
1188 | ||
1189 | if (c->ssl->save_session) { | |
1190 | c->ssl->session = sess; | |
1191 | ||
1192 | c->ssl->save_session(c); | |
1193 | ||
1194 | c->ssl->session = NULL; | |
1195 | } | |
1196 | ||
1197 | return 0; | |
1198 | } | |
1199 | ||
1200 | ||
1201 | ngx_int_t | |
1164 | 1202 | ngx_ssl_create_connection(ngx_ssl_t *ssl, ngx_connection_t *c, ngx_uint_t flags) |
1165 | 1203 | { |
1166 | 1204 | ngx_ssl_connection_t *sc; |
1206 | 1244 | c->ssl = sc; |
1207 | 1245 | |
1208 | 1246 | return NGX_OK; |
1247 | } | |
1248 | ||
1249 | ||
1250 | ngx_ssl_session_t * | |
1251 | ngx_ssl_get_session(ngx_connection_t *c) | |
1252 | { | |
1253 | #ifdef TLS1_3_VERSION | |
1254 | if (c->ssl->session) { | |
1255 | SSL_SESSION_up_ref(c->ssl->session); | |
1256 | return c->ssl->session; | |
1257 | } | |
1258 | #endif | |
1259 | ||
1260 | return SSL_get1_session(c->ssl->connection); | |
1261 | } | |
1262 | ||
1263 | ||
1264 | ngx_ssl_session_t * | |
1265 | ngx_ssl_get0_session(ngx_connection_t *c) | |
1266 | { | |
1267 | if (c->ssl->session) { | |
1268 | return c->ssl->session; | |
1269 | } | |
1270 | ||
1271 | return SSL_get0_session(c->ssl->connection); | |
1209 | 1272 | } |
1210 | 1273 | |
1211 | 1274 |
75 | 75 | size_t buffer_size; |
76 | 76 | |
77 | 77 | ngx_connection_handler_pt handler; |
78 | ||
79 | ngx_ssl_session_t *session; | |
80 | ngx_connection_handler_pt save_session; | |
78 | 81 | |
79 | 82 | ngx_event_handler_pt saved_read_handler; |
80 | 83 | ngx_event_handler_pt saved_write_handler; |
167 | 170 | ngx_array_t *ngx_ssl_read_password_file(ngx_conf_t *cf, ngx_str_t *file); |
168 | 171 | ngx_int_t ngx_ssl_dhparam(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file); |
169 | 172 | ngx_int_t ngx_ssl_ecdh_curve(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *name); |
173 | ngx_int_t ngx_ssl_client_session_cache(ngx_conf_t *cf, ngx_ssl_t *ssl, | |
174 | ngx_uint_t enable); | |
170 | 175 | ngx_int_t ngx_ssl_session_cache(ngx_ssl_t *ssl, ngx_str_t *sess_ctx, |
171 | 176 | ssize_t builtin_session_cache, ngx_shm_zone_t *shm_zone, time_t timeout); |
172 | 177 | ngx_int_t ngx_ssl_session_ticket_keys(ngx_conf_t *cf, ngx_ssl_t *ssl, |
177 | 182 | |
178 | 183 | void ngx_ssl_remove_cached_session(SSL_CTX *ssl, ngx_ssl_session_t *sess); |
179 | 184 | ngx_int_t ngx_ssl_set_session(ngx_connection_t *c, ngx_ssl_session_t *session); |
180 | #define ngx_ssl_get_session(c) SSL_get1_session(c->ssl->connection) | |
185 | ngx_ssl_session_t *ngx_ssl_get_session(ngx_connection_t *c); | |
186 | ngx_ssl_session_t *ngx_ssl_get0_session(ngx_connection_t *c); | |
181 | 187 | #define ngx_ssl_free_session SSL_SESSION_free |
182 | 188 | #define ngx_ssl_get_connection(ssl_conn) \ |
183 | 189 | SSL_get_ex_data(ssl_conn, ngx_ssl_connection_index) |
4626 | 4626 | } |
4627 | 4627 | } |
4628 | 4628 | |
4629 | if (ngx_ssl_client_session_cache(cf, glcf->upstream.ssl, | |
4630 | glcf->upstream.ssl_session_reuse) | |
4631 | != NGX_OK) | |
4632 | { | |
4633 | return NGX_ERROR; | |
4634 | } | |
4635 | ||
4629 | 4636 | #ifdef TLSEXT_TYPE_application_layer_protocol_negotiation |
4630 | 4637 | |
4631 | 4638 | if (SSL_CTX_set_alpn_protos(glcf->upstream.ssl->ctx, |
4307 | 4307 | } |
4308 | 4308 | } |
4309 | 4309 | |
4310 | if (ngx_ssl_client_session_cache(cf, plcf->upstream.ssl, | |
4311 | plcf->upstream.ssl_session_reuse) | |
4312 | != NGX_OK) | |
4313 | { | |
4314 | return NGX_ERROR; | |
4315 | } | |
4316 | ||
4310 | 4317 | return NGX_OK; |
4311 | 4318 | } |
4312 | 4319 |
2390 | 2390 | } |
2391 | 2391 | } |
2392 | 2392 | |
2393 | if (ngx_ssl_client_session_cache(cf, uwcf->upstream.ssl, | |
2394 | uwcf->upstream.ssl_session_reuse) | |
2395 | != NGX_OK) | |
2396 | { | |
2397 | return NGX_ERROR; | |
2398 | } | |
2399 | ||
2393 | 2400 | return NGX_OK; |
2394 | 2401 | } |
2395 | 2402 |
186 | 186 | static void ngx_http_upstream_ssl_handshake_handler(ngx_connection_t *c); |
187 | 187 | static void ngx_http_upstream_ssl_handshake(ngx_http_request_t *, |
188 | 188 | ngx_http_upstream_t *u, ngx_connection_t *c); |
189 | static void ngx_http_upstream_ssl_save_session(ngx_connection_t *c); | |
189 | 190 | static ngx_int_t ngx_http_upstream_ssl_name(ngx_http_request_t *r, |
190 | 191 | ngx_http_upstream_t *u, ngx_connection_t *c); |
191 | 192 | #endif |
1674 | 1675 | } |
1675 | 1676 | |
1676 | 1677 | if (u->conf->ssl_session_reuse) { |
1678 | c->ssl->save_session = ngx_http_upstream_ssl_save_session; | |
1679 | ||
1677 | 1680 | if (u->peer.set_session(&u->peer, u->peer.data) != NGX_OK) { |
1678 | 1681 | ngx_http_upstream_finalize_request(r, u, |
1679 | 1682 | NGX_HTTP_INTERNAL_SERVER_ERROR); |
1758 | 1761 | } |
1759 | 1762 | } |
1760 | 1763 | |
1761 | if (u->conf->ssl_session_reuse) { | |
1762 | u->peer.save_session(&u->peer, u->peer.data); | |
1763 | } | |
1764 | ||
1765 | 1764 | c->write->handler = ngx_http_upstream_handler; |
1766 | 1765 | c->read->handler = ngx_http_upstream_handler; |
1767 | 1766 | |
1778 | 1777 | failed: |
1779 | 1778 | |
1780 | 1779 | ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR); |
1780 | } | |
1781 | ||
1782 | ||
1783 | static void | |
1784 | ngx_http_upstream_ssl_save_session(ngx_connection_t *c) | |
1785 | { | |
1786 | ngx_http_request_t *r; | |
1787 | ngx_http_upstream_t *u; | |
1788 | ||
1789 | if (c->idle) { | |
1790 | return; | |
1791 | } | |
1792 | ||
1793 | r = c->data; | |
1794 | ||
1795 | u = r->upstream; | |
1796 | c = r->connection; | |
1797 | ||
1798 | ngx_http_set_log_request(c->log, r); | |
1799 | ||
1800 | u->peer.save_session(&u->peer, u->peer.data); | |
1781 | 1801 | } |
1782 | 1802 | |
1783 | 1803 |
743 | 743 | |
744 | 744 | if (peers->shpool) { |
745 | 745 | |
746 | ssl_session = SSL_get0_session(pc->connection->ssl->connection); | |
746 | ssl_session = ngx_ssl_get0_session(pc->connection); | |
747 | 747 | |
748 | 748 | if (ssl_session == NULL) { |
749 | 749 | return; |
91 | 91 | ngx_command_t *cmd, void *conf); |
92 | 92 | static void ngx_stream_proxy_ssl_init_connection(ngx_stream_session_t *s); |
93 | 93 | static void ngx_stream_proxy_ssl_handshake(ngx_connection_t *pc); |
94 | static void ngx_stream_proxy_ssl_save_session(ngx_connection_t *c); | |
94 | 95 | static ngx_int_t ngx_stream_proxy_ssl_name(ngx_stream_session_t *s); |
95 | 96 | static ngx_int_t ngx_stream_proxy_set_ssl(ngx_conf_t *cf, |
96 | 97 | ngx_stream_proxy_srv_conf_t *pscf); |
1007 | 1008 | } |
1008 | 1009 | |
1009 | 1010 | if (pscf->ssl_session_reuse) { |
1011 | pc->ssl->save_session = ngx_stream_proxy_ssl_save_session; | |
1012 | ||
1010 | 1013 | if (u->peer.set_session(&u->peer, u->peer.data) != NGX_OK) { |
1011 | 1014 | ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); |
1012 | 1015 | return; |
1065 | 1068 | } |
1066 | 1069 | } |
1067 | 1070 | |
1068 | if (pscf->ssl_session_reuse) { | |
1069 | u = s->upstream; | |
1070 | u->peer.save_session(&u->peer, u->peer.data); | |
1071 | } | |
1072 | ||
1073 | 1071 | if (pc->write->timer_set) { |
1074 | 1072 | ngx_del_timer(pc->write); |
1075 | 1073 | } |
1082 | 1080 | failed: |
1083 | 1081 | |
1084 | 1082 | ngx_stream_proxy_next_upstream(s); |
1083 | } | |
1084 | ||
1085 | ||
1086 | static void | |
1087 | ngx_stream_proxy_ssl_save_session(ngx_connection_t *c) | |
1088 | { | |
1089 | ngx_stream_session_t *s; | |
1090 | ngx_stream_upstream_t *u; | |
1091 | ||
1092 | s = c->data; | |
1093 | u = s->upstream; | |
1094 | ||
1095 | u->peer.save_session(&u->peer, u->peer.data); | |
1085 | 1096 | } |
1086 | 1097 | |
1087 | 1098 | |
2050 | 2061 | } |
2051 | 2062 | } |
2052 | 2063 | |
2064 | if (ngx_ssl_client_session_cache(cf, pscf->ssl, pscf->ssl_session_reuse) | |
2065 | != NGX_OK) | |
2066 | { | |
2067 | return NGX_ERROR; | |
2068 | } | |
2069 | ||
2053 | 2070 | return NGX_OK; |
2054 | 2071 | } |
2055 | 2072 |