@@ -39,9 +39,12 @@ existing higher-level protocol implementation.
39
39
** Table of contents**
40
40
41
41
* [ Quickstart example] ( #quickstart-example )
42
+ * [ API] ( #api )
43
+ * [ SshProcessConnector] ( #sshprocessconnector )
44
+ * [ SshSocksConnector] ( #sshsocksconnector )
42
45
* [ Usage] ( #usage )
43
- * [ SshProcessConnector] ( #sshprocessconnector )
44
46
* [ Plain TCP connections] ( #plain-tcp-connections )
47
+ * [ Secure TLS connections] ( #secure-tls-connections )
45
48
* [ HTTP requests] ( #http-requests )
46
49
* [ Connection timeout] ( #connection-timeout )
47
50
* [ DNS resolution] ( #dns-resolution )
@@ -80,7 +83,7 @@ $loop->run();
80
83
81
84
See also the [ examples] ( examples ) .
82
85
83
- ## Usage
86
+ ## API
84
87
85
88
### SshProcessConnector
86
89
@@ -92,8 +95,12 @@ any destination by using an intermediary SSH server as a proxy server.
92
95
```
93
96
94
97
This class is implemented as a lightweight process wrapper around the ` ssh `
95
- client binary, so you'll have to make sure that you have a suitable SSH client
96
- installed. On Debian/Ubuntu-based systems, you may simply install it like this:
98
+ client binary, so it will spawn one ` ssh ` process for each connection. For
99
+ example, if you [ open a connection] ( #plain-tcp-connections ) to
100
+ ` tcp://reactphp.org:80 ` , it will run the equivalent of ` ssh -W reactphp.org:80 user@example.com `
101
+ and forward data from its standard I/O streams. For this to work, you'll have to
102
+ make sure that you have a suitable SSH client installed. On Debian/Ubuntu-based
103
+ systems, you may simply install it like this:
97
104
98
105
``` bash
99
106
$ sudo apt install openssh-client
@@ -110,7 +117,18 @@ The proxy URL may or may not contain a scheme and port definition. The default
110
117
port will be ` 22 ` for SSH, but you may have to use a custom port depending on
111
118
your SSH server setup.
112
119
113
- This is the main class in this package.
120
+ Keep in mind that this class is implemented as a lightweight process wrapper
121
+ around the ` ssh ` client binary and that it will spawn one ` ssh ` process for each
122
+ connection. If you open more connections, it will spawn one ` ssh ` process for
123
+ each connection. Each process will take some time to create a new SSH connection
124
+ and then keep running until the connection is closed, so you're recommended to
125
+ limit the total number of concurrent connections. If you plan to only use a
126
+ single or few connections (such as a single database connection), using this
127
+ class is the recommended approach. If you plan to create multiple connections or
128
+ have a larger number of connections (such as an HTTP client), you're recommended
129
+ to use the [ ` SshSocksConnector ` ] ( #sshsocksconnector ) instead.
130
+
131
+ This is one of the two main classes in this package.
114
132
Because it implements ReactPHP's standard
115
133
[ ` ConnectorInterface ` ] ( https://github.com/reactphp/socket#connectorinterface ) ,
116
134
it can simply be used in place of a normal connector.
@@ -131,16 +149,95 @@ higher-level component:
131
149
+ $client = new SomeClient($proxy);
132
150
```
133
151
134
- #### Plain TCP connections
152
+ ### SshSocksConnector
153
+
154
+ The ` SshSocksConnector ` is responsible for creating plain TCP/IP connections to
155
+ any destination by using an intermediary SSH server as a proxy server.
156
+
157
+ ```
158
+ [you] -> [proxy] -> [destination]
159
+ ```
160
+
161
+ This class is implemented as a lightweight process wrapper around the ` ssh `
162
+ client binary and it will spawn one ` ssh ` process on demand for multiple
163
+ connections. For example, once you [ open a connection] ( #plain-tcp-connections )
164
+ to ` tcp://reactphp.org:80 ` for the first time, it will run the equivalent of
165
+ ` ssh -D 1080 user@example.com ` to run the SSH client in local SOCKS proxy server
166
+ mode and will then create a SOCKS client connection to this server process. You
167
+ can create any number of connections over this one process and it will keep this
168
+ process running while there are any open connections and will automatically
169
+ close if when it is idle. For this to work, you'll have to make sure that you
170
+ have a suitable SSH client installed. On Debian/Ubuntu-based systems, you may
171
+ simply install it like this:
172
+
173
+ ``` bash
174
+ $ sudo apt install openssh-client
175
+ ```
176
+
177
+ Its constructor simply accepts an SSH proxy server URL and a loop to bind to:
178
+
179
+ ``` php
180
+ $loop = React\EventLoop\Factory::create();
181
+ $proxy = new Clue\React\SshProxy\SshSocksConnector('user@example.com', $loop);
182
+ ```
183
+
184
+ The proxy URL may or may not contain a scheme and port definition. The default
185
+ port will be ` 22 ` for SSH, but you may have to use a custom port depending on
186
+ your SSH server setup.
187
+
188
+ Keep in mind that this class is implemented as a lightweight process wrapper
189
+ around the ` ssh ` client binary and that it will spawn one ` ssh ` process for
190
+ multiple connections. This process will take some time to create a new SSH
191
+ connection and then keep running until the last connection is closed. If you
192
+ plan to create multiple connections or have a larger number of concurrent
193
+ connections (such as an HTTP client), using this class is the recommended
194
+ approach. If you plan to only use a single or few connections (such as a single
195
+ database connection), you're recommended to use the [ ` SshProcessConnector ` ] ( #sshprocessconnector )
196
+ instead.
197
+
198
+ > * Security note for multi-user systems* : This class will spawn the SSH client
199
+ process in local SOCKS server mode and will accept connections on the
200
+ localhost interface only. If you're running on a multi-user system, other
201
+ users on the same system may be able to connect to this proxy server and
202
+ create connections over it. If this applies to your deployment, you're
203
+ recommended to use the [ `SshProcessConnector] ( #sshprocessconnector ) instead.
204
+
205
+ This is one of the two main classes in this package.
206
+ Because it implements ReactPHP's standard
207
+ [ ` ConnectorInterface ` ] ( https://github.com/reactphp/socket#connectorinterface ) ,
208
+ it can simply be used in place of a normal connector.
209
+ Accordingly, it provides only a single public method, the
210
+ [ ` connect() ` ] ( https://github.com/reactphp/socket#connect ) method.
211
+ The ` connect(string $uri): PromiseInterface<ConnectionInterface, Exception> `
212
+ method can be used to establish a streaming connection.
213
+ It returns a [ Promise] ( https://github.com/reactphp/promise ) which either
214
+ fulfills with a [ ConnectionInterface] ( https://github.com/reactphp/socket#connectioninterface )
215
+ on success or rejects with an ` Exception ` on error.
216
+
217
+ This makes it fairly simple to add SSH proxy support to pretty much any
218
+ higher-level component:
219
+
220
+ ``` diff
221
+ - $client = new SomeClient($connector);
222
+ + $proxy = new SshSocksConnector('user@example.com', $loop);
223
+ + $client = new SomeClient($proxy);
224
+ ```
225
+
226
+ ## Usage
227
+
228
+ ### Plain TCP connections
135
229
136
230
SSH proxy servers are commonly used to issue HTTPS requests to your destination.
137
231
However, this is actually performed on a higher protocol layer and this
138
- connector is actually inherently a general-purpose plain TCP/IP connector.
139
- As documented above, you can simply invoke its ` connect() ` method to establish
140
- a streaming plain TCP/IP connection and use any higher level protocol like so:
232
+ project is actually inherently a general-purpose plain TCP/IP connector.
233
+ As documented above, you can simply invoke the ` connect() ` method to establish
234
+ a streaming plain TCP/IP connection on the ` SshProcessConnector ` or ` SshSocksConnector `
235
+ and use any higher level protocol like so:
141
236
142
237
``` php
143
- $proxy = new SshProcessConnector('user@example.com', $connector);
238
+ $proxy = new SshProcessConnector('user@example.com', $loop);
239
+ // or
240
+ $proxy = new SshSocksConnector('user@example.com', $loop);
144
241
145
242
$proxy->connect('tcp://smtp.googlemail.com:587')->then(function (ConnectionInterface $stream) {
146
243
$stream->write("EHLO local\r\n");
@@ -150,8 +247,8 @@ $proxy->connect('tcp://smtp.googlemail.com:587')->then(function (ConnectionInter
150
247
});
151
248
```
152
249
153
- You can either use the ` SshProcessConnector ` directly or you may want to wrap this connector
154
- in ReactPHP's [ ` Connector ` ] ( https://github.com/reactphp/socket#connector ) :
250
+ You can either use the ` SshProcessConnector ` or ` SshSocksConnector ` directly or you
251
+ may want to wrap this connector in ReactPHP's [ ` Connector ` ] ( https://github.com/reactphp/socket#connector ) :
155
252
156
253
``` php
157
254
$connector = new Connector($loop, array(
@@ -167,25 +264,60 @@ $connector->connect('tcp://smtp.googlemail.com:587')->then(function (ConnectionI
167
264
});
168
265
```
169
266
170
- Keep in mind that this class is implemented as a lightweight process wrapper
171
- around the ` ssh ` client binary, so it will spawn one ` ssh ` process for each
172
- connection. Each process will keep running until the connection is closed, so
173
- you're recommended to limit the total number of concurrent connections.
267
+ For this example, you can use either the ` SshProcessConnector ` or ` SshSocksConnector ` .
268
+ Keep in mind that this project is implemented as a lightweight process wrapper
269
+ around the ` ssh ` client binary. While the ` SshProcessConnector ` will spawn one
270
+ ` ssh ` process for each connection, the ` SshSocksConnector ` will spawn one ` ssh `
271
+ process that will be shared for multiple connections, see also above for more
272
+ details.
273
+
274
+ ### Secure TLS connections
275
+
276
+ The ` SshSocksConnector ` can also be used if you want to establish a secure TLS connection
277
+ (formerly known as SSL) between you and your destination, such as when using
278
+ secure HTTPS to your destination site. You can simply wrap this connector in
279
+ ReactPHP's [ ` Connector ` ] ( https://github.com/reactphp/socket#connector ) or the
280
+ low-level [ ` SecureConnector ` ] ( https://github.com/reactphp/socket#secureconnector ) :
281
+
282
+ ``` php
283
+ $proxy = new SshSocksConnector('user@example.com', $loop);
284
+
285
+ $connector = new Connector($loop, array(
286
+ 'tcp' => $proxy,
287
+ 'dns' => false
288
+ ));
289
+
290
+ $connector->connect('tls://smtp.googlemail.com:465')->then(function (ConnectionInterface $stream) {
291
+ $stream->write("EHLO local\r\n");
292
+ $stream->on('data', function ($chunk) use ($stream) {
293
+ echo $chunk;
294
+ });
295
+ });
296
+ ```
297
+
298
+ > Note how secure TLS connections are in fact entirely handled outside of
299
+ this SSH proxy client implementation.
300
+ The ` SshProcessConnector ` does not currently support secure TLS connections
301
+ because PHP's underlying crypto functions require a socket resource and do not
302
+ work for virtual connections. As an alternative, you're recommended to use the
303
+ ` SshSocksConnector ` as given in the above example.
174
304
175
- #### HTTP requests
305
+ ### HTTP requests
176
306
177
307
HTTP operates on a higher layer than this low-level SSH proxy implementation.
178
308
If you want to issue HTTP requests, you can add a dependency for
179
309
[ clue/reactphp-buzz] ( https://github.com/clue/reactphp-buzz ) .
180
310
It can interact with this library by issuing all HTTP requests through your SSH
181
311
proxy server, similar to how it can issue
182
312
[ HTTP requests through an HTTP CONNECT proxy server] ( https://github.com/clue/reactphp-buzz#http-proxy ) .
183
- At the moment, this only works for plaintext HTTP requests.
313
+ When using the ` SshSocksConnector ` (recommended), this works for both plain HTTP
314
+ and TLS-encrypted HTTPS requests. When using the ` SshProcessConnector ` , this only
315
+ works for plaintext HTTP requests.
184
316
185
- #### Connection timeout
317
+ ### Connection timeout
186
318
187
- By default, the ` SshProcessConnector ` does not implement any timeouts for establishing remote
188
- connections.
319
+ By default, neither the ` SshProcessConnector ` nor the ` SshSocksConnector ` implement
320
+ any timeouts for establishing remote connections.
189
321
Your underlying operating system may impose limits on pending and/or idle TCP/IP
190
322
connections, anywhere in a range of a few minutes to several hours.
191
323
@@ -216,26 +348,26 @@ See also any of the [examples](examples).
216
348
> Note how the connection timeout is in fact entirely handled outside of this
217
349
SSH proxy client implementation.
218
350
219
- #### DNS resolution
351
+ ### DNS resolution
220
352
221
- By default, the ` SshProcessConnector ` does not perform any DNS resolution at all and simply
222
- forwards any hostname you're trying to connect to the remote proxy server.
223
- The remote proxy server is thus responsible for looking up any hostnames via DNS
224
- (this default mode is thus called * remote DNS resolution* ).
353
+ By default, neither the ` SshProcessConnector ` nor the ` SshSocksConnector ` perform
354
+ any DNS resolution at all and simply forwards any hostname you're trying to
355
+ connect to the remote proxy server. The remote proxy server is thus responsible
356
+ for looking up any hostnames via DNS (this default mode is thus called * remote DNS resolution* ).
225
357
226
358
As an alternative, you can also send the destination IP to the remote proxy
227
359
server.
228
360
In this mode you either have to stick to using IPs only (which is ofen unfeasable)
229
361
or perform any DNS lookups locally and only transmit the resolved destination IPs
230
362
(this mode is thus called * local DNS resolution* ).
231
363
232
- The default * remote DNS resolution* is useful if your local ` SshProcessConnector ` either can
233
- not resolve target hostnames because it has no direct access to the internet or
234
- if it should not resolve target hostnames because its outgoing DNS traffic might
235
- be intercepted.
364
+ The default * remote DNS resolution* is useful if your local ` SshProcessConnector `
365
+ or ` SshSocksConnector ` either can not resolve target hostnames because it has no
366
+ direct access to the internet or if it should not resolve target hostnames
367
+ because its outgoing DNS traffic might be intercepted.
236
368
237
- As noted above, the ` SshProcessConnector ` defaults to using remote DNS resolution.
238
- However, wrapping the ` SshProcessConnector ` in ReactPHP's
369
+ As noted above, the ` SshProcessConnector ` and ` SshSocksConnector ` default to using
370
+ remote DNS resolution. However, wrapping them in ReactPHP's
239
371
[ ` Connector ` ] ( https://github.com/reactphp/socket#connector ) actually
240
372
performs local DNS resolution unless explicitly defined otherwise.
241
373
Given that remote DNS resolution is assumed to be the preferred mode, all
@@ -261,7 +393,7 @@ $connector = new Connector($loop, array(
261
393
> Note how local DNS resolution is in fact entirely handled outside of this
262
394
SSH proxy client implementation.
263
395
264
- #### Password authentication
396
+ ### Password authentication
265
397
266
398
Note that this class is implemented as a lightweight process wrapper around the
267
399
` ssh ` client binary. It works under the assumption that you have verified you
@@ -286,7 +418,9 @@ If your SSH proxy server requires password authentication, you may pass the
286
418
username and password as part of the SSH proxy server URL like this:
287
419
288
420
``` php
289
- $proxy = new SshProcessConnector('user:pass@example.com', $connector);
421
+ $proxy = new SshProcessConnector('user:pass@example.com', $loop);
422
+ // or
423
+ $proxy = new SshSocksConnector('user:pass@example.com', $loop);
290
424
```
291
425
292
426
For this to work, you will have to have the ` sshpass ` binary installed. On
@@ -305,7 +439,7 @@ $pass = 'p@ss';
305
439
306
440
$proxy = new SshProcessConnector(
307
441
rawurlencode($user) . ':' . rawurlencode($pass) . '@example.com:2222',
308
- $connector
442
+ $loop
309
443
);
310
444
```
311
445
0 commit comments