Welcome! Log In Create A New Profile

Advanced

Contrib: http2 per server (was re: [nginx] support http2 per server)

David Freedman
September 19, 2017 07:50PM
Following on from the previous thread on this ( http://mailman.nginx.org/pipermail/nginx-devel/2017-June/010079.html ) , and after discussion with the original author
< hongzhidao at gmail.com > , I'd like to present the patches we are using.

Please note the following improvements / modifications:

1. Default state is set to off during conf merge, so that existing behaviour of enabling http2 directive on the listener and having it apply to all servers is preserved.

2. Attempts to re-use a connection via the listener to access a server without ssl_h2 enabled (i.e, explicitly set to off) are met with a 421 (Misdirected Request), enforcing the intended switch behavior on the server block, (in response to the comment made in http://mailman.nginx.org/pipermail/nginx-devel/2017-June/010091.html )
This works in much the same way as the ssl verify in that after SNI and in ngx_http_set_virtual_server(), we check for the switch state on the target server.

3. Tests added to the test suite.

Patches follow below (decided not to patchbomb as this is across two repositories) , feedback welcome - we'd like to see this feature adopted as it is clearly needed.

Patch against main project
-------------------------------------

# HG changeset patch
# User David Freedman <david.freedman@uk.clara.net>
# Date 1504832866 0
# Fri Sep 08 01:07:46 2017 +0000
# Node ID 2806e0ba8e91978ad6ce18ff1605b89bdd7806f4
# Parent 6b6e15bbda9269d03d66130efd51921dfedd93cb
Supports selective enabling of http2 for SSL/TLS sites
using new ssl_h2 option.

diff -r 6b6e15bbda92 -r 2806e0ba8e91 src/http/modules/ngx_http_ssl_module.c
--- a/src/http/modules/ngx_http_ssl_module.c Tue Sep 05 17:59:31 2017 +0300
+++ b/src/http/modules/ngx_http_ssl_module.c Fri Sep 08 01:07:46 2017 +0000
@@ -234,6 +234,13 @@
offsetof(ngx_http_ssl_srv_conf_t, stapling_verify),
NULL },

+ { ngx_string("ssl_h2"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
+ ngx_http_ssl_enable,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_ssl_srv_conf_t, h2),
+ NULL },
+
ngx_null_command
};

@@ -354,6 +361,7 @@
#endif
#if (NGX_HTTP_V2)
ngx_http_connection_t *hc;
+ ngx_http_ssl_srv_conf_t *sscf;
#endif
#if (NGX_HTTP_V2 || NGX_DEBUG)
ngx_connection_t *c;
@@ -372,7 +380,9 @@
#if (NGX_HTTP_V2)
hc = c->data;

- if (hc->addr_conf->http2) {
+ sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_ssl_module);
+
+ if (hc->addr_conf->http2 && sscf->h2) {
srv =
(unsigned char *) NGX_HTTP_V2_ALPN_ADVERTISE NGX_HTTP_NPN_ADVERTISE;
srvlen = sizeof(NGX_HTTP_V2_ALPN_ADVERTISE NGX_HTTP_NPN_ADVERTISE) - 1;
@@ -416,10 +426,13 @@
#if (NGX_HTTP_V2)
{
ngx_http_connection_t *hc;
+ ngx_http_ssl_srv_conf_t *sscf;

hc = c->data;

- if (hc->addr_conf->http2) {
+ sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_ssl_module);
+
+ if (hc->addr_conf->http2 && sscf->h2) {
*out =
(unsigned char *) NGX_HTTP_V2_NPN_ADVERTISE NGX_HTTP_NPN_ADVERTISE;
*outlen = sizeof(NGX_HTTP_V2_NPN_ADVERTISE NGX_HTTP_NPN_ADVERTISE) - 1;
@@ -559,6 +572,7 @@
sscf->session_ticket_keys = NGX_CONF_UNSET_PTR;
sscf->stapling = NGX_CONF_UNSET;
sscf->stapling_verify = NGX_CONF_UNSET;
+ sscf->h2 = NGX_CONF_UNSET;

return sscf;
}
@@ -624,6 +638,8 @@
ngx_conf_merge_str_value(conf->stapling_responder,
prev->stapling_responder, "");

+ ngx_conf_merge_value(conf->h2, prev->h2, 1);
+
conf->ssl.log = cf->log;

if (conf->enable) {
diff -r 6b6e15bbda92 -r 2806e0ba8e91 src/http/modules/ngx_http_ssl_module.h
--- a/src/http/modules/ngx_http_ssl_module.h Tue Sep 05 17:59:31 2017 +0300
+++ b/src/http/modules/ngx_http_ssl_module.h Fri Sep 08 01:07:46 2017 +0000
@@ -57,6 +57,9 @@

u_char *file;
ngx_uint_t line;
+
+ ngx_flag_t h2;
+
} ngx_http_ssl_srv_conf_t;


diff -r 6b6e15bbda92 -r 2806e0ba8e91 src/http/ngx_http_request.c
--- a/src/http/ngx_http_request.c Tue Sep 05 17:59:31 2017 +0300
+++ b/src/http/ngx_http_request.c Fri Sep 08 01:07:46 2017 +0000
@@ -795,6 +795,7 @@
unsigned int len;
const unsigned char *data;
ngx_http_connection_t *hc;
+ ngx_http_ssl_srv_conf_t *sscf;

hc = c->data;

@@ -813,9 +814,15 @@
SSL_get0_next_proto_negotiated(c->ssl->connection, &data, &len);
#endif

- if (len == 2 && data[0] == 'h' && data[1] == '2') {
- ngx_http_v2_init(c->read);
- return;
+ sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_ssl_module);
+
+ if (sscf->h2) {
+
+ if (len == 2 && data[0] == 'h' && data[1] == '2') {
+ ngx_http_v2_init(c->read);
+ return;
+ }
+
}
}
}
@@ -2106,6 +2113,15 @@
ngx_http_finalize_request(r, NGX_HTTP_MISDIRECTED_REQUEST);
return NGX_ERROR;
}
+#if (NGX_HTTP_V2)
+ if (hc->addr_conf->http2 && !sscf->h2) {
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "client attempted to request a server name "
+ "that does not have http2 enabled");
+ ngx_http_finalize_request(r, NGX_HTTP_MISDIRECTED_REQUEST);
+ return NGX_ERROR;
+ }
+#endif
}

#endif


Patch against test suite
---------------------------------

# HG changeset patch
# User David Freedman <david.freedman@uk.clara.net>
# Date 1505862083 0
# Tue Sep 19 23:01:23 2017 +0000
# Node ID b0bcd8bc295f8bf4dc735ea8d3ab44061eb88bdb
# Parent f373a718f6463a949e2f0fe88a078425b7ba3b73
Tests: add testing for ssl_h2 (selective http2 over ssl)

diff -r f373a718f646 -r b0bcd8bc295f h2_ssl.t
--- a/h2_ssl.t Mon Sep 11 20:06:15 2017 +0300
+++ b/h2_ssl.t Tue Sep 19 23:01:23 2017 +0000
@@ -50,6 +50,18 @@

location / { }
}
+
+ server {
+ listen 127.0.0.1:8081 http2 ssl;
+ server_name localhost;
+
+ ssl_h2 off;
+
+ ssl_certificate_key localhost.key;
+ ssl_certificate localhost.crt;
+
+ location / { }
+ }
}

EOF
@@ -80,6 +92,7 @@
open STDERR, ">&", \*OLDERR;

plan(skip_all => 'no ALPN/NPN negotiation') unless defined getconn(port(8080));
+plan(skip_all => 'no ALPN/NPN negotiation') if defined getconn(port(8081));
$t->plan(1);

###############################################################################
@@ -113,7 +126,7 @@
my $sock = Test::Nginx::HTTP2::new_socket($port, SSL => 1,
alpn => 'h2');
$s = Test::Nginx::HTTP2->new($port, socket => $sock)
- if $sock->alpn_selected();
+ if ($sock->alpn_selected() && $sock->alpn_selected()=~m/h2/);
};

return $s if defined $s;
@@ -122,7 +135,7 @@
my $sock = Test::Nginx::HTTP2::new_socket($port, SSL => 1,
npn => 'h2');
$s = Test::Nginx::HTTP2->new($port, socket => $sock)
- if $sock->next_proto_negotiated();
+ if ($sock->next_proto_negotiated() && $sock->next_proto_negotiated()=~m/h2/);
};

return $s;
diff -r f373a718f646 -r b0bcd8bc295f h2_ssl_verify_authority.t
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/h2_ssl_verify_authority.t Tue Sep 19 23:01:23 2017 +0000
@@ -0,0 +1,153 @@
+#!/usr/bin/perl
+
+# Tests for HTTP/2 protocol with ssl, ssl_verify_authority
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+use Test::Nginx::HTTP2;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+eval { require IO::Socket::SSL; };
+plan(skip_all => 'IO::Socket::SSL not installed') if $@;
+eval { IO::Socket::SSL->can_client_sni() or die; };
+plan(skip_all => 'IO::Socket::SSL with OpenSSL SNI support required') if $@;
+eval { IO::Socket::SSL->can_alpn() or die; };
+plan(skip_all => 'OpenSSL ALPN support required') if $@;
+
+my $t = Test::Nginx->new()->has(qw/http http_ssl sni http_v2/)
+ ->has_daemon('openssl');
+
+$t->write_file_expand('nginx.conf', <<'EOF');
+
+%%TEST_GLOBALS%%
+
+daemon off;
+
+events {
+}
+
+http {
+ %%TEST_GLOBALS_HTTP%%
+
+ ssl_certificate_key localhost.key;
+ ssl_certificate localhost.crt;
+
+ server {
+ listen 127.0.0.1:8080 ssl http2;
+ server_name localhost;
+
+ location / { }
+ }
+
+ server {
+ listen 127.0.0.1:8080 ssl;
+ server_name example.com;
+
+ ssl_h2 off;
+
+ location / { }
+ }
+}
+
+EOF
+
+$t->write_file('openssl.conf', <<EOF);
+[ req ]
+default_bits = 1024
+encrypt_key = no
+distinguished_name = req_distinguished_name
+[ req_distinguished_name ]
+EOF
+
+my $d = $t->testdir();
+
+foreach my $name ('localhost') {
+ system('openssl req -x509 -new '
+ . "-config '$d/openssl.conf' -subj '/CN=$name/' "
+ . "-out '$d/$name.crt' -keyout '$d/$name.key' "
+ . ">>$d/openssl.out 2>&1") == 0
+ or die "Can't create certificate for $name: $!\n";
+}
+
+$t->write_file('t', 'SEE-THIS');
+
+open OLDERR, ">&", \*STDERR; close STDERR;
+$t->run();
+open STDERR, ">&", \*OLDERR;
+
+my $s = get_ssl_socket();
+plan(skip_all => 'no alpn') unless $s->alpn_selected();
+$t->plan(2);
+
+###############################################################################
+
+my $sess = get_sess('localhost');
+is(get($sess, 'localhost')->{':status'}, '200', 'ok');
+is(get($sess, 'example.com')->{':status'}, '421', 'misdirected');
+###############################################################################
+
+sub get_ssl_socket {
+ my ($sni) = @_;
+ my $s;
+
+ eval {
+ local $SIG{ALRM} = sub { die "timeout\n" };
+ local $SIG{PIPE} = sub { die "sigpipe\n" };
+ alarm(2);
+ $s = IO::Socket::SSL->new(
+ Proto => 'tcp',
+ PeerAddr => '127.0.0.1',
+ PeerPort => port(8080),
+ SSL_verify_mode => IO::Socket::SSL::SSL_VERIFY_NONE(),
+ SSL_alpn_protocols => [ 'h2' ],
+ SSL_hostname => $sni,
+ SSL_error_trap => sub { die $_[1] }
+ );
+ alarm(0);
+ };
+ alarm(0);
+
+ if ($@) {
+ log_in("died: $@");
+ return undef;
+ }
+
+ return $s;
+}
+
+sub get_sess {
+ my $sni = shift;
+ my $s = get_ssl_socket($sni);
+ my $sess = Test::Nginx::HTTP2->new(port(8080), socket => $s);
+
+ return $sess
+}
+
+sub get {
+ my ($sess, $host) = @_;
+
+ my $sid = $sess->new_stream({ headers => [
+ { name => ':method', value => 'GET', mode => 0 },
+ { name => ':scheme', value => 'http', mode => 0 },
+ { name => ':path', value => '/t', mode => 1 },
+ { name => ':authority', value => $host, mode => 1 }]});
+ my $frames = $sess->read(all => [{ sid => $sid, fin => 1 }]);
+
+ my ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
+ return $frame->{'headers'};
+}
+
+###############################################################################




_______________________________________________
nginx-devel mailing list
nginx-devel@nginx.org
http://mailman.nginx.org/mailman/listinfo/nginx-devel
Subject Author Views Posted

Contrib: http2 per server (was re: [nginx] support http2 per server)

David Freedman 1017 September 19, 2017 07:50PM

Re: Contrib: http2 per server (was re: [nginx] support http2 per server)

David Freedman 225 October 02, 2017 11:32AM



Sorry, you do not have permission to post/reply in this forum.

Online Users

Guests: 171
Record Number of Users: 8 on April 13, 2023
Record Number of Guests: 421 on December 02, 2018
Powered by nginx      Powered by FreeBSD      PHP Powered      Powered by MariaDB      ipv6 ready