Can I implements http/2 server without ALPN support? - http2

In this way, the client can connect the server without ALPN and use http/2 default.
Is that possible?

Yes it is possible, but the conditions are strict.
Browsers, as of now, do not implement clear-text communication for HTTP/2, so if you use a browser the answer to your question is no: you have to deploy your server with ALPN support if you want browsers to be able to connect.
On the other hand, other clients such as Java clients may be able to connect to a server using clear-text HTTP/2, so for those type of clients the answer to your question is yes: you can deploy a HTTP/2 server without ALPN support.
The Jetty Project [disclaimer, I am a committer] implements a web server and servlet container that support both scenarios: you can enable ALPN for TLS (SSL) HTTP/2 communication so that browsers will be able to connect, and you can also enable clear-text HTTP/2 communication as explained here. See also the Jetty HTTP/2 documentation.

Related

Is ALPN mandatory for server to establish TLSv1.3 connection via http/2 ("h2") protocol?

I know, that if we talk about non secured connection, it is possible to establish connection via http/2 protocol without ALPN.
But what about TLS connection? In RFC said:
A client MUST send the connection preface (Section 3.5) and then MAY
immediately send HTTP/2 frames to such a server; servers can identify
these connections by the presence of the connection preface. This
only affects the establishment of HTTP/2 connections over cleartext
TCP; **implementations that support HTTP/2 over TLS MUST use protocol
negotiation in TLS [TLS-ALPN]**.
Does it mean that both server and client must use ALPN to establish connection via TLS and http2? Or there are workarounds and other options?
A compliant HTTP/2 client must send the ALPN extension for HTTP/2 over TLS.
However, what would a server do if the ALPN extension is not present?
This may happen with old clients, or non-compliant clients, or attackers.
The server could be legitimately configured to speak only HTTP/2 (for example, https://h2.domain.com), so it may assume that the protocol being spoken is h2 without the need of negotiating it via ALPN. This is an implementation/configuration choice. (Another valid choice could be to just close the connection if ALPN is absent).
The RFC also discusses the role of ALPN for cross-protocol attacks, see this section.
I think the intent of the RFC is to mandate the use of ALPN; however, a server should be prepared to receive connection attempts without ALPN, and at that point it can be configured to either close the connection or assume a default protocol, which is typically http/1.1, but could as well be h2.
Keep in mind that you may always use TLS without ALPN and perform an HTTP/1.1 to HTTP/2 upgrade request (where, like in ALPN, you declare what protocol you want to upgrade to), which would typically succeed as servers should support HTTP/1.1 to HTTP/2 upgrade.
So you would be able to speak HTTP/2 to such servers, after the upgrade, even without ALPN.
A server may assume that if ALPN is missing, the client wants to try with an HTTP/1.1 to HTTP/2 upgrade. If the server does not see the upgrade (but directly the HTTP/2 client preface), it may reply with 426 Upgrade Required (see here).

Implementing websocket support for HTTP/2 proxies

I am developing an https http/2 proxy server as mentioned here:
https://chromium.googlesource.com/chromium/src/+/HEAD/net/docs/proxy.md#HTTPS-proxy-scheme
It is mentioned there that
HTTPS proxies using HTTP/2 can offer better performance in Chrome than a regular HTTP proxy due to higher connection limits (HTTP/1.1 proxies in Chrome are limited to 32 simultaneous connections across all domains).
But when users try to surf a website which is using websocket over raw http connection, the response contains 'Upgrade' http header which is forbidden to be used in http/2 as there is no websockets for HTTP/2.
https://daniel.haxx.se/blog/2016/06/15/no-websockets-over-http2/#:~:text=There%20is%20no%20websockets%20for%20HTTP%2F2.&text=That%20spec%20details%20how%20a,connection%20into%20a%20websockets%20connection.
The problem comes from the fact that when http1.1 traffic passes through an https proxy which implements http/2, headers must be transferred from http1.1 to http/2. Of course when the webpage is using TLS, there is no such a problem as all traffic passes through a connection made by http 'CONNECT' method. The problem occurs when the website does not use TLS.
If there is no solution to this problem, it means that HTTPS proxies should not implement HTTP/2 protocol.
As there is no websockets for HTTP/2
The link you posted is old, and since then WebSocket over HTTP/2 has been specified by RFC 8441.
The behavior of a HTTP/2 proxy is specified in RFC 7540, section 8.3.
When the client communicates with the proxy using secure HTTP/2, each HTTP/2 stream is a "tunnel" to the server.
Client and proxy communicate using secure HTTP/2, and the "tunnelling" happens because each HTTP/2 stream becomes an "infinite" request with an "infinite" response, i.e. an "infinite" series of HTTP/2 DATA frames in both directions that carry an opaque byte array payload.
The job of the proxy is to unwrap the DATA frames received by the client, and forward the byte array payload to the server, perhaps re-wrapping it into an HTTP/2 DATA frame if the communication between proxy and server is also HTTP/2 (it may be possible to optimize away the unwrapping and re-wrapping but it may be tricky -- for example the stream numbering could be different).
When a client attempts to perform WebSocket over HTTP/2, the browser will do as specified by RFC 8441, and the proxy will receive a CONNECT request with a :protocol pseudo-header, and the proxy will have to know what to do in that case, depending on what protocol it uses to communicate with the server.
What above describes what your proxy need to support when the communication with the client is HTTP/2.
If your proxy needs to support clients that speak HTTP/1.1, then you need to implement what is required for a proxy to support HTTP/1.1 and WebSocket proxying, and it's typically not difficult: the first is just HTTP/1.1 forwarding or HTTP CONNECT "tunnelling", the second is WebSocket HTTP Upgrade request forwarding followed by a "tunnel" connection or even a fully fledged WebSocket proxy.
Disclaimer, I have implemented all of the above in the Jetty Project.
You can use Jetty 10 or later to implement an HTTP/1.1, HTTP/2, or WebSocket, both client and server (and therefore a proxy). WebSocket over HTTP/2 is supported too.
Finally, to answer your last question, it is perfectly possible to have secure proxies support HTTP/2, even in presence of WebSocket.
For example, a clear-text WebSocket connection to a server starts with an HTTP upgrade request; this request will be sent, encrypted, to the proxy which will decrypt it and forward it to the server (using any protocol the server supports -- could even be WebSocket over secure HTTP/2); the server will reply with a 101 and upgrade the connection to WebSocket; the proxy will receive the 101 from the server and upgrade to a "tunnel" or to a WebSocket proxy; the proxy encrypts the 101 response and forward it to the client; the client decrypts it and upgrades the connection to WebSocket.
At this point, the client is speaking clear-text WebSocket to the server through a secure connection with the proxy (the proxy sees the clear-text WebSocket communication).
Viceversa, a secure WebSocket connection to a server starts with establishing a HTTP CONNECT tunnel through the proxy to the server; then the client will establish the secure communication with the server; then it will send the HTTP upgrade to WebSocket to the server (through the proxy tunnel).
At this point, the client is speaking secure WebSocket to the server through a secure connection with the proxy (the proxy cannot see the WebSocket communication).
The HTTP/2 variants are similar, just taking into account the HTTP/2 specific ways of "tunnelling" and transporting WebSocket.

Can I have my cloudrun server receive http/2 requests?

Can I have Google send http/2 requests to my server in cloud run?
I am not sure how google would know my server supports it since google terminates the SSL on the loadbalancer and sends http to the stateless servers in cloud run.
If possible, I am thinking of grabbing a few pieces from webpieces and creating a pure http/2 server with no http1.1 for microservices that I 'know' will only be doing http/2.
Also, if I have a pure http/2 server, is there a way that google translates from http1 requests to http/2 when needed so I could host websites as well?
The only info I could find was a great FAQ that seems to be missing the does it support http/2 on the server side(rather than client)...
https://github.com/ahmetb/cloud-run-faq
thanks,
Dean
Cloud Run container contract requires your application to serve on an unencrypted HTTP endpoint. However, this can be either HTTP/1 or HTTP/2.
Today, gRPC apps work on Cloud Run and gRPC actually uses HTTP/2 as its transport. This works because the gRPC servers (unless configured with TLS certificates) use the H2C (HTTP/2 unencrypted cleartext) protocol.
So, if your application can actually serve traffic unencrypted using h2c protocol, the traffic between Cloud Run load balancer <=> your application can be HTTP/2, without ever being downgraded to HTTP/1.
For example, in Go, you can use https://godoc.org/golang.org/x/net/http2/h2c package to automatically detect and upgrade http2 connections.
To test if your application implements h2c correctly, you need to locally run:
curl -v --http2-prior-knowledge http://localhost:8080
and see < HTTP/2 200 response.
(I'll make sure to add this to the FAQ repo.)

How does a browser know if a site supports HTTP/2?

If I type out https://http2.golang.org/ the chrome browser will automatically send the HTTP/2 request. How is this done?
Take stackoverflow for example, when the browser sends a request to stackoverflow.com, it has to do the following steps:
DNS lookup. find the ip address of stackoverflow.
TCP/IP handshake
TLS handshake.
HTTP request/response (Application Protocol).
....
TLS handshake
Regarding step3 TLS handshake, there is an nice explanation by #Oleg.
In order to inspect the detail of TCP/IP packet, You may need use some tools to capture packets. e.g. Wireshark.
Client sends ClientHello to server, which carries several things
supported cipher suite. which cipher suites do you like?
supported TLS version.
a random number.
the supported Application Protocols. e.g. HTTP/2, HTTP 1.1/ Spdy/..
...
Server responds SeverHello, which carries
chosen cipher suite.
chosen TLS version.
a random generated number
And, chosen application protocols in TLS application-layer protocol negotiation (ALPN), e.g. HTTP/2
Conclusion
HTTP2 request/response happens in step4. Before that, browser has already know whether sever support HTTP/2 through TLS handshake.
The chrome browser will only send a HTTP/1.1 Request to the website. As the website is HTTP/2 Enabled, it will send a message to the browser that it supports HTTP/2. The server upgrades the communication protocol between it and the server to HTTP/2 if it finds the browser capable of recognizing HTTP/2.
So, it is generally the server which converts a request to the HTTP/2 Connection. The browser just complies with the upgrade policy of the server.
The chrome browser displays that you have a HTTP/2 connection with the server or website, only after the server upgrades the communication protocol.
The string "h2" identifies the protocol where HTTP/2 uses Transport Layer >Security (TLS) [TLS12].
This identifier is used in the TLS application-layer protocol negotiation (ALPN) >extension [TLS-ALPN] field and in any place where HTTP/2 over TLS is identified.
If server support http2.0 browser will find that server is support http2.0 in TLS application-layer protocol negotiation.
refer link!

Why don't current websocket client implementations support proxies?

A Web Socket detects the presence of a proxy server and automatically sets up a tunnel to pass through the proxy. The tunnel is established by issuing an HTTP CONNECT statement to the proxy server, which requests for the proxy server to open a TCP/IP connection to a specific host and port. Once the tunnel is set up, communication can flow unimpeded through the proxy. Since HTTP/S works in a similar fashion, secure Web Sockets over SSL can leverage the same HTTP CONNECT technique. [1]
OK, sounds useful! But, in the client implementations I've seen thus far (Go [2], Java [3]) I do not see anything related to proxy detection.
Am I missing something or are these implementations just young? I know WebSockets is extremely new and client implementations may be equally young and immature. I just want to know if I'm missing something about proxy detection and handling.
[1] http://www.kaazing.org/confluence/display/KAAZING/What+is+an+HTML+5+WebSocket
[2] http://golang.org/src/pkg/websocket/client.go
[3] http://github.com/adamac/Java-WebSocket-client/raw/master/src/com/sixfire/websocket/WebSocket.java
Let me try to explain the different success rates you may have encountered. While the HTML5 Web Socket protocol itself is unaware of proxy servers and firewalls, it features an HTTP-compatible handshake so that HTTP servers can share their default HTTP and HTTPS ports (80 and 443) with a Web Sockets gateway or server.
The Web Socket protocol defines a ws:// and wss:// prefix to indicate a WebSocket and a WebSocket Secure connection, respectively. Both schemes use an HTTP upgrade mechanism to upgrade to the Web Socket protocol. Some proxy servers are harmless and work fine with Web Sockets; others will prevent Web Sockets from working correctly, causing the connection to fail. In some cases additional proxy server configuration may be required, and certain proxy servers may need to be upgraded to support Web Sockets.
If unencrypted WebSocket traffic flows through an explicit or a transparent proxy server on its way the WebSocket server, then, whether or not the proxy server behaves as it should, the connection is almost certainly bound to fail today (in the future, proxy servers may become Web Socket aware). Therefore, unencrypted WebSocket connections should be used only in the simplest topologies.
If encrypted WebSocket connection is used, then the use of Transport Layer Security (TLS) in the Web Sockets Secure connection ensures that an HTTP CONNECT command is issued when the browser is configured to use an explicit proxy server. This sets up a tunnel, which provides low-level end-to-end TCP communication through the HTTP proxy, between the Web Sockets Secure client and the WebSocket server. In the case of transparent proxy servers, the browser is unaware of the proxy server, so no HTTP CONNECT is sent. However, since the wire traffic is encrypted, intermediate transparent proxy servers may simply allow the encrypted traffic through, so there is a much better chance that the WebSocket connection will succeed if Web Sockets Secure is used. Using encryption, of course, is not free, but often provides the highest success rate.
One way to see it in action is to download and install the Kaazing WebSocket Gateway--a highly optimized, proxy-aware WebSocket gateway, which provides native WebSocket support as well as a full emulation of the standard for older browsers.
The answer is that these clients simply do not support proxies.
-Occam
The communication channel is already established by the time the WebSocket protocol enters the scene. The WebSocket is built on top of TCP and HTTP so you don't have to care about the things already done by these protocols, including proxies.
When a WebSocket connection is established it always starts with a HTTP/TCP connection which is later "upgraded" during the "handshake" phase of WebSocket. At this time the tunnel is established so the proxies are transparent, there's no need to care about them.
Regarding websocket clients and transparent proxies,
I think websocket client connections will fail most of the time for the following reasons (not tested):
If the connection is in clear, since the client does not know it is communicating with a http proxy server, it won't send the "CONNECT TO" instruction that turns the http proxy into a tcp proxy (needed for the client after the websocket handshake). It could work if the proxy supports natively websocket and handles the URL with the ws scheme differently than http.
If the connection is in SSL, the transparent proxy cannot know to which server it should connect to since it has decrypt the host name in the https request. It could by either generating a self-signed certificate on the fly (like for SSLStrip) or providing its own static certificate and decrypt the communication but if the client validates the server certificate it will fail (see https://serverfault.com/questions/369829/setting-up-a-transparent-ssl-proxy).
You mentioned Java proxies, and to respond to that I wanted to mention that Java-Websocket now supports proxies.
You can see the information about that here: http://github.com/TooTallNate/Java-WebSocket/issues/88
websocket-client, a Python package, supports proxies, at the very least over secure scheme wss:// as in that case proxy need no be aware of the traffic it forwards.
https://github.com/liris/websocket-client/commit/9f4cdb9ec982bfedb9270e883adab2e028bbd8e9

Resources