• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Http fallback and raw proxying
2
3Lws has several interesting options and features that can be applied to get
4some special behaviours... this article discusses them and how they work.
5
6## Overview of normal vhost selection
7
8Lws supports multiple http or https vhosts sharing a listening socket on the
9same port.
10
11For unencrypted http, the Host: header is used to select which vhost the
12connection should bind to, by comparing what is given there against the
13names the server was configured with for the various vhosts.  If no match, it
14selects the first configured vhost.
15
16For TLS, it has an extension called SNI (Server Name Indication) which tells
17the server early in the TLS handshake the host name the connection is aimed at.
18That allows lws to select the vhost early, and use vhost-specific TLS certs
19so everything is happy.  Again, if there is no match the connection proceeds
20using the first configured vhost and its certs.
21
22## Http(s) fallback options
23
24What happens if you try to connect, eg, an ssh client to the http server port
25(this is not an idle question...)?  Obviously the http server part or the tls
26part of lws will fail the connection and close it.  (We will look at that flow
27in a moment in detail for both unencrypted and tls listeners.)
28
29However if the first configured vhost for the port was created with the
30vhost creation info struct `.options` flag `LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG`,
31then instead of the error, the connection transitions to whatever role was
32given in the vhost creation info struct `.listen_accept_role` and `.listen_accept_protocol`.
33
34With lejp-conf / lwsws, the options can be applied to the first vhost using:
35
36```
37   "listen-accept-role": "the-role-name",
38   "listen-accept-protocol": "the-protocol-name",
39   "fallback-listen-accept": "1"
40```
41
42See `./minimal-examples/raw/minimal-raw-fallback-http-server` for examples of
43all the options in use via commandline flags.
44
45So long as the first packet for the protocol doesn't look like GET, POST, or
46a valid tls packet if connection to an https vhost, this allows the one listen
47socket to handle both http(s) and a second protocol, as we will see, like ssh.
48
49Notice there is a restriction that no vhost selection processing is possible,
50neither for tls listeners nor plain http ones... the packet belonging to a
51different protocol will not send any Host: header nor tls SNI.
52
53Therefore although the flags and settings are applied to the first configured
54vhost, actually their effect is global for a given listen port.  If enabled,
55all vhosts on the same listen port will do the fallback action.
56
57### Plain http flow
58
59![plain http flow](/doc-assets/accept-flow-1.svg)
60
61Normally, if the first received packet does not contain a valid HTTP method,
62then the connection is dropped.  Which is what you want from an http server.
63
64However if enabled, the connection can transition to the defined secondary
65role / protocol.
66
67|Flag|lejp-conf / lwsws|Function|
68|---|---|---|
69|`LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG`|`"fallback-listen-accept": "1"`|Enable fallback processing|
70
71### TLS https flow
72
73![tls https flow](/doc-assets/accept-flow-2.svg)
74
75If the port is listening with tls, the point that a packet from a different
76protocol will fail is earlier, when the tls tunnel is being set up.
77
78|Flag|lejp-conf / lwsws|Function|
79|---|---|---|
80|`LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG`|`"fallback-listen-accept": "1"`|Enable fallback processing|
81|`LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS`|`"redirect-http": "1"`|Treat invalid tls packet as http, issue http redirect to https://|
82|`LWS_SERVER_OPTION_ALLOW_HTTP_ON_HTTPS_LISTENER`|`"allow-http-on-https": "1"`|Accept unencrypted http connections on this tls port (dangerous)|
83
84The latter two options are higher priority than, and defeat, the first one.
85
86### Non-http listener
87
88![non-http flow](/doc-assets/accept-flow-3.svg)
89
90It's also possible to skip the fallback processing and just force the first
91vhost on the port to use the specified role and protocol in the first place.
92
93|Flag|lejp-conf / lwsws|Function|
94|---|---|---|
95|LWS_SERVER_OPTION_ADOPT_APPLY_LISTEN_ACCEPT_CONFIG|`"apply-listen-accept": "1"`|Force vhost to use listen-accept-role / listen-accept-protocol|
96
97## Using http(s) fallback with raw-proxy
98
99If enabled for build with `cmake .. -DLWS_ROLE_RAW_PROXY=1 -DLWS_WITH_PLUGINS=1`
100then lws includes ready-to-use support for raw tcp proxying.
101
102This can be used standalone on the first vhost on a port, but most intriguingly
103it can be specified as the fallback for http(s)...
104
105See `./minimal-examples/raw/minimal-raw-proxy-fallback.c` for a working example.
106
107### fallback with raw-proxy in code
108
109On the first vhost for the port, specify the required "onward" pvo to configure
110the raw-proxy protocol...you can adjust the "ipv4:127.0.0.1:22" to whatever you
111want...
112
113```
114	static struct lws_protocol_vhost_options pvo1 = {
115	        NULL,
116	        NULL,
117	        "onward",		/* pvo name */
118	        "ipv4:127.0.0.1:22"	/* pvo value */
119	};
120
121	static const struct lws_protocol_vhost_options pvo = {
122	        NULL,           	/* "next" pvo linked-list */
123	        &pvo1,			/* "child" pvo linked-list */
124	        "raw-proxy",		/* protocol name we belong to on this vhost */
125	        ""              	/* ignored */
126	};
127```
128
129... and set up the fallback enable and bindings...
130
131```
132	info.options |= LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG;
133	info.listen_accept_role = "raw_proxy";
134	info.listen_accept_proxy = "raw_proxy";
135	info.pvo = &pvo;
136```
137
138### fallback with raw-proxy in JSON conf
139
140On the first vhost for the port, enable the raw-proxy protocol on the vhost and
141set the pvo config
142
143```
144                "ws-protocols": [{
145                        "raw-proxy": {
146                         "status": "ok",
147                         "onward": "ipv4:127.0.0.1:22"
148                        }
149                 }],
150```
151
152Enable the fallback behaviour on the vhost and the role / protocol binding
153
154```
155	"listen-accept-role": "raw-proxy",
156	"listen-accept-protocol": "raw-proxy",
157	"fallback-listen-accept": "1"
158```
159
160### Testing
161
162With this configured, the listen port will function normally for http or https
163depending on how it was set up.
164
165But if you try to connect to it with an ssh client, that will also work fine.
166
167The libwebsockets.org server is set up in this way, you can confirm it by
168visiting `https://libwebsockets.org` on port 443 as usual, but also trying
169`ssh -p 443 invalid@libwebsockets.org`... you will get permission denied from
170your ssh client.  With valid credentials in fact that works perfectly for
171ssh, scp, git-over-ssh etc all on port 443...
172
173