Skip to content

Commit db74819

Browse files
committed
Add documentation and example for MySQL database SSH tunnel
1 parent 3d0cda6 commit db74819

File tree

3 files changed

+85
-1
lines changed

3 files changed

+85
-1
lines changed

README.md

+46
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ existing higher-level protocol implementation.
4646
* [Plain TCP connections](#plain-tcp-connections)
4747
* [Secure TLS connections](#secure-tls-connections)
4848
* [HTTP requests](#http-requests)
49+
* [Database tunnel](#database-tunnel)
4950
* [Connection timeout](#connection-timeout)
5051
* [DNS resolution](#dns-resolution)
5152
* [Password authentication](#password-authentication)
@@ -323,6 +324,51 @@ When using the `SshSocksConnector` (recommended), this works for both plain HTTP
323324
and TLS-encrypted HTTPS requests. When using the `SshProcessConnector`, this only
324325
works for plaintext HTTP requests.
325326

327+
### Database tunnel
328+
329+
We should now have a basic understanding of how we can tunnel any TCP/IP-based
330+
protocol over an SSH proxy server. Besides using this to access "external"
331+
services, this is also particularly useful because it allows you to access
332+
network services otherwise only local to this SSH server from the outside, such
333+
as a firewalled database server.
334+
335+
For example, this allows us to combine an
336+
[async MySQL database client](https://github.com/friends-of-reactphp/mysql) and
337+
the above SSH proxy server setup, so we can access a firewalled MySQL database
338+
server through an SSH tunnel. Here's the gist:
339+
340+
```php
341+
$loop = React\EventLoop\Factory::create();
342+
$proxy = new Clue\React\SshProxy\SshProcessConnector('[email protected]', $loop);
343+
344+
$uri = 'test:test@localhost/test';
345+
$factory = new React\MySQL\Factory($loop, $proxy);
346+
$connection = $factory->createLazyConnection($uri);
347+
348+
$connection->query('SELECT * FROM book')->then(
349+
function (QueryResult $command) {
350+
echo count($command->resultRows) . ' row(s) in set' . PHP_EOL;
351+
},
352+
function (Exception $error) {
353+
echo 'Error: ' . $error->getMessage() . PHP_EOL;
354+
}
355+
);
356+
357+
$connection->quit();
358+
359+
$loop->run();
360+
```
361+
362+
See also [example #21](examples) for more details.
363+
364+
This example will automatically launch the `ssh` client binary to create the
365+
connection to a database server that can not otherwise be accessed from the
366+
outside. From the perspective of the database server, this looks just like a
367+
regular, local connection. From this code's perspective, this will create a
368+
regular, local connection which just happens to use a secure SSH tunnel to
369+
transport this to a remote server, so you can send any query like you would to a
370+
local database server.
371+
326372
### Connection timeout
327373

328374
By default, neither the `SshProcessConnector` nor the `SshSocksConnector` implement

composer.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
},
2626
"require-dev": {
2727
"clue/block-react": "^1.3",
28-
"phpunit/phpunit": "^7.4 || ^6.4 || ^5.0 || ^4.8.36"
28+
"phpunit/phpunit": "^7.4 || ^6.4 || ^5.0 || ^4.8.36",
29+
"react/mysql": "^0.5.3"
2930
}
3031
}

examples/21-mysql-ssh-tunnel.php

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
// A more advanced example to show how a MySQL server can be accessed through an SSH tunnel.
4+
// The SSH proxy can be given through the SSH_PROXY env and defaults to localhost otherwise.
5+
// The MySQL server can be given through the MYSQL_LOGIN env and default to localhost otherwise.
6+
//
7+
// You can assign the SSH_PROXY and MYSQL_LOGIN environment and prefix this with
8+
// a space to make sure your login credentials are not stored in your bash
9+
// history like this:
10+
//
11+
// $ export SSH_PROXY=user:[email protected]
12+
// $ export MYSQL_LOGIN=user:password@localhost
13+
// $ php examples/21-mysql-ssh-tunnel.php
14+
//
15+
// See also https://github.com/friends-of-reactphp/mysql
16+
17+
use Clue\React\SshProxy\SshProcessConnector;
18+
use React\MySQL\Factory;
19+
use React\MySQL\QueryResult;
20+
21+
require __DIR__ . '/../vendor/autoload.php';
22+
23+
$loop = React\EventLoop\Factory::create();
24+
25+
$url = getenv('SSH_PROXY') !== false ? getenv('SSH_PROXY') : 'ssh://localhost:22';
26+
$proxy = new SshProcessConnector($url, $loop);
27+
28+
$url = getenv('MYSQL_LOGIN') !== false ? getenv('MYSQL_LOGIN') : 'user:pass@localhost';
29+
$factory = new Factory($loop, $proxy);
30+
$client = $factory->createLazyConnection($url);
31+
32+
$client->query('SELECT * FROM (SELECT "foo" UNION SELECT "bar") data')->then(function (QueryResult $query) {
33+
var_dump($query->resultRows);
34+
}, 'printf');
35+
$client->quit();
36+
37+
$loop->run();

0 commit comments

Comments
 (0)