I originally wanted to interact with the process through PHP's proc_open, but there were too many pitfalls in the middle, so I had to change my thinking, and then I remembered that Baota had a web version of shell client, and then I studied it, hehe, I found that it could be done.
1. Preparation
PHP connection ssh is based on a third-party expansion library, PECL/ssh2 (a php extension of libssh2, allowing php programs to call functions in libssh2)
Then there is a ready-made library that encapsulates most common operationsphpseclib
SSH is read and write concurrently through swoole coroutines, as well as websockets and browsers for communication.
1. Install the ssh2 expansion library
1.1. Linux installation
First installlibssh2(libssh2 is a C function library used to implement the SSH2 protocol.)
yum install libssh2 libssh2-devel
Then install it through pcelssh2Expand, find the right version
pecl install ssh2-1.1.2
Of course, you can also manually install it through phpize.
1.2. Windows installation
It seems that libssh2 is usually available. If not, download it and throw it into the system. It mainly installs ssh2. Download according to your PHP version, you can check out your php version, as well as whether it is 32-bit or 64-bit, 32-bit download x86, 64-bit download x64
Download address
Add extension=php_ssh2.dll to it and it's done.
2. Swoole installation
Reference official website:/#/environment
3、phpseclib
Official website:, you can install composer:
composer require phpseclib/phpseclib:~3.0
2. Write code
Test Demo::5707/
Create a websocket through swoole. When the connection is successful, a coroutine is created to read the content returned by ssh and send it to the websocket. The client forwards the message to the shell when sending it.
The following is a simple function implementation, which cannot be applied to production. After testing, the output of certain commands needs to be processed in actual use.
1、
<?php include_once 'include/'; include_once 'vendor/'; use Swoole\Http\Request; use Swoole\Http\Response; use Swoole\WebSocket\CloseFrame; use Swoole\Coroutine\Http\Server; use Swoole\Coroutine; use function Swoole\Coroutine\go; use function Swoole\Coroutine\run; use function Swoole\Coroutine\defer; use phpseclib3\Net\SSH2; /* * Set coroutine operation related parameters * */ Co::set([ 'socket_timeout'=>-1, //tcp timeout 'hook_flags' => SWOOLE_HOOK_ALL //HOOK function range]); /* * Create coroutine container * */ run(function () { /* * The third parameter represents whether to enable ssl * */ $server = new Server('0.0.0.0', 5707, false); $server->handle('/ws', function (Request $request, Response $ws) { /*websocket protocol*/ $ws->upgrade(); /*Connect ssh*/ $ssh = new SSH2('localhost',22); /*If login fails*/ if (!$ssh->login('root', 'Qq461625091@')) { $ws->close(); return; } /*Read time of the output content of the command*/ $ssh->setTimeout(0.1); /* * Create coroutines to output command line content specifically * */ $subscribe=function () use($ws,$ssh){ /* * Save id, used to cancel coroutine * */ $ws->Gid = go(function () use ($ws,$ssh){ /* * Clean up when coroutine exits * */ defer(function () use ($ssh,$ws) { /* * quit * */ logs($ws->qq.', the link has been broken! '); $ssh->disconnect(); }); try { while (true){ $msg=$ssh->read('username@username:~$'); if(!empty($msg)){ $ws->push($msg); } } } catch (\Throwable $e) { logs('Read exception'); } }); }; /* * Clean up * */ $quit=function ($log) use ($ws){ logs($log);//Record the reason for exit /* * If the coroutine is already running * */ if(isset($ws->Gid)){ Coroutine::cancel($ws->Gid); //Close the coroutine } $ws->close(); //Disconnect ws }; /* * Normal processing logic * */ $subscribe(); //Start Subscribe $cmd=[ 'ps -ef', 'ping 127.0.0.1', 'ifconfig', "\x03" ]; while (true) { $frame = $ws->recv(); //Blocking reception of messages if ($frame === '') { $quit("Disconnected, empty data was received!"); break; } else if ($frame === false) { $quit(swoole_last_error()); break; } else { if ($frame->data == 'close' || get_class($frame) === CloseFrame::class) { $quit("User actively closes\n"); break; } /* * If the test command is not present, terminate * */ if(!in_array($frame->data,$cmd)){ continue; } $ssh->write($frame->data."\n"); // note the "\n" } } }); /* * Output default test template * */ $server->handle('/', function (Request $request, Response $response) { $response->end(getTest()); }); $server->start(); });
2、
<?php /* * Print the html template for testing * */ function getTest(): string { $test = <<<HTML <!DOCTYPE html> <html lang="zh-cn" xmlns="http:///1999/html"> <head> <meta charset="UTF-8"/> <meta charset="UTF-8"/> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> <title>Web SSHClient</title> <link href="/wp-content/themes/document/" rel="external nofollow" rel="shortcut icon" type="image/x-icon"/> <script src="/cdn/expire-1-M/jquery/3.6.0/" type="application/javascript"></script> <script src="/cdn/expire-1-M/keyboardjs/2.6.2/" type="application/javascript"></script> <style> body{ background-color: #000000; color: #e2e2e2; padding: 15px; } input{ background-color: black; border: none; color: white; outline: none; font-size: 17px; } </style> </head> <body> <h1>Web SSHtest</h1> <div>Note:test环境只支持:ps -ef、ping 127.0.0.1、ifconfig,Three commands。</div> <div>hint:Enter to submit、ctrl+cInterrupt(The terminal is now connected to the website host)</div> <br /> <main> <span ></span> <input type="text"> </main> </body> <script> =function (){ let content=$("#content"); let input= $('input'); let wsServer = 'ws://:5707/ws'; let websocket = new WebSocket(wsServer); = function (evt) { ("Connected to WebSocket server.<br />"); }; = function (evt) { ("Disconnected.<br />"); }; = function (evt) { (("\\n",'<br />')); (""); $(window).scrollTop() }; = function (evt, e) { ("Error occured: " + +"<br />"); }; (); /* * Automatic focus * */ $(window).on("click",function (){ (); }) /* * Enter to submit * */ ('enter', (e) => { (()); }); /* * ctrl+c * */ ('ctrl > c', (e) => { ("\x03"); }); } </script> HTML; return $test; } /* * Logging * */ function logs(string $log, bool $flag = true): void { $time = date("Y-m-d H:i:s", time()); if ($flag) { echo $time . ',' . $log . "\n"; } else { file_put_contents('', $time . ',' . $log . "\n", FILE_APPEND); } }
3. Run
php
The above is the detailed explanation of the shell client for PHP+Swoole implementation of the web version. For more information about the PHP Swoole shell client, please follow my other related articles!