laravel+ Linux 修改ssh连接终端的展示信息代码示例记录
使用场景
对Linux设备进行集中管理的时候,标明平台信息,防止多个部署的管理平台操作到同一个设备,用此信息表明设备的所属
思路
ssh到服务器,判断设备是否有含有本平台的信息,从而判断此设备能否使用,如果含有信息或者文件为空,均为无人认领机器,可以对其操作
如果不含本平台信息则不允许对其操作.
实现原理
追加修改linux设备的 /etc/profile 文件,来展示需要显示的内容
拓展
可以搭配 chattr 等指令 来保护文件不被修改、删除
环境
- laravel8.5
- php8.0
- 拓展包 :查看代码引用
实操服务代码
autotest_supervise_example:
*************************************************************
* 请您立即退出本服务器 *
* *
* 本服务器受自动化测试程序监管,所有操作将被监控和记录。 *
* 未经授权的访问和更改是被禁止的,自动化结果可能变化。 *
* 请遵守相关规定,感谢您的合作。 *
* 如有疑问,请联系系统管理员:test@test.com *
*************************************************************
服务代码:
use Exception;
use File;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Storage;
use phpseclib3\Crypt\PublicKeyLoader;
use phpseclib3\Net\SFTP;
use phpseclib3\Net\SSH2;
public function addSupervise($box)
{
if(env('DEBUG')){
return [
'status' => TRUE,
'message' => "监管已成功移除。"
];
}
$userName = $box->ssh_name;
$appUrl = env('APP_URL'); // 获取当前平台的 APP_URL try {
$this->insertTextArrayToFile( [
" ",
"自动化测试程序: " . env('APP_NAME') ,
"自动化测试程序地址: " . env('APP_URL') ,
"自动化测试程序版本: " . env('VERSION') ,
]);
// 创建 SSH 客户端实例
$ssh = new SSH2($box['ssh_ip'], $box['ssh_port']);
$key = Storage::get('id_rsa');
// 使用 PublicKeyLoader 加载私钥
$rsa = PublicKeyLoader::load($key);
if (!$ssh->login($userName, $rsa)) {
return [
'message' => "SSH 登录失败",
'code' => "-1",
'status' => FALSE
];
}
// 检查远程文件是否存在并读取内容
$remoteFilePath = '/usr/local/disk/autotest_supervise';
$checkCommand = "if [ -f {$remoteFilePath} ]; then cat {$remoteFilePath}; else echo 'FILE_NOT_EXIST'; fi";
$remoteContent = $ssh->exec($checkCommand);
if (trim($remoteContent) === 'FILE_NOT_EXIST') {
// 文件不存在,可以继续添加监管
} else {
// 文件存在,检查是否包含当前平台的 APP_URL if (strpos($remoteContent, $appUrl) === FALSE) {
return [
'message' => "此设备已经被其他平台添加至监管,请前往其他平台解除监管后添加",
'code' => "-4",
'status' => FALSE
];
}
// 如果包含当前 APP_URL,可以选择覆盖或追加,根据需求调整
}
// 创建 SFTP 实例进行文件上传
$sftp = new SFTP($box['ssh_ip'], $box['ssh_port']);
if (!$sftp->login($userName, $rsa)) {
return [
'message' => "SFTP 登录失败",
'code' => "-1",
'status' => FALSE
];
}
// 本地文件路径
$localFilePath = Storage::path('public/autotest_supervise');
// 远程服务器上的文件路径
$remoteFilePath = '/usr/local/disk/autotest_supervise';
// 将文件上传到远程服务器
if (!$sftp->put($remoteFilePath, $localFilePath, SFTP::SOURCE_LOCAL_FILE)) {
return [
'message' => "SFTP 传输失败, 需要确认权限及 /usr/local/disk 存在",
'code' => "-2",
'status' => FALSE
];
}
$sftp->disconnect();
// 追加内容到 /etc/profile if ($userName == 'root') {
$command = 'sudo sh -c \'cat >> /etc/profile << "EOF"
if [ -f /usr/local/disk/autotest_supervise ]; then
cat /usr/local/disk/autotest_supervisefi
EOF\'';
$ssh->exec(str_replace("\r\n", "\n", $command));
} else {
// 切换到 sudo 用户并执行命令
$ssh->write("sudo su\n");
$ssh->read('密码:');
$ssh->write("nle\n"); // 这里假设密码为 'nle', 请根据实际情况修改
$ssh->setTimeout(1);
$ssh->read();
$ssh->write('[ "$(stat -c "%U:%G" /usr/local/disk)" != "nle:nle" ] && sudo chown -R nle:nle /usr/local/disk' . "\n");
$ssh->setTimeout(2);
$ssh->read();
$ssh->setTimeout(1);
$command = 'sudo sh -c \'cat >> /etc/profile << "EOF"
if [ -f /usr/local/disk/autotest_supervise ]; then
cat /usr/local/disk/autotest_supervisefi
EOF\'' . "\n";
$ssh->write(str_replace("\r\n", "\n", $command));
$ssh->read();
}
// 关闭 SSH 连接
$ssh->disconnect();
return [
'status' => TRUE
];
} catch (Exception $e) {
return [
'status' => FALSE,
'line' => $e->getLine(),
'message' => $e->getMessage(),
];
}
}
public function insertTextArrayToFile(array $textArray)
{
// 1. 读取文件内容
$content = Storage::get('public/autotest_supervise_example');
// 2. 统一换行符为 \n,避免因换行符不同导致的问题
$content = str_replace(["\r\n", "\r"], "\n", $content);
// 3. 拆分为行数组
$lines = explode("\n", $content);
// 4. 找到底部边框行的位置
$borderLine = str_repeat('*', 61);
$bottomBorderIndex = null;
foreach (array_reverse($lines, true) as $index => $lineContent) {
if (trim($lineContent) === $borderLine) {
$bottomBorderIndex = $index;
break;
}
}
if ($bottomBorderIndex === null) {
// 未找到底部边框,抛出异常或根据需要处理
throw new Exception('底部边框行未找到,无法插入文本。');
}
// 5. 定义颜色代码
$colors = [
'red' => "\033[31m", // 红色
'reset' => "\033[0m", // 重置颜色
];
// 6. 准备要插入的行数组,并添加颜色
$insertLines = [];
foreach ($textArray as $key => $text) {
// 检查是否是需要着色的行,这里假设第一行需要红色
if ($key === 0|| $key === 1||$key === 2||$key === 3) {
$colorStart = $colors['red'];
$colorEnd = $colors['reset'];
} else {
$colorStart = '';
$colorEnd = '';
}
// 生成新行内容,确保总可见宽度为59个字符(61 - 2个 '*')
$lineContent = " {$text}"; // 两个空格开头
$visibleWidth = mb_strwidth($lineContent, 'UTF-8');
// 计算需要填充的空格数量
$paddingLength = 59 - $visibleWidth;
$padding = str_repeat(' ', max(0, $paddingLength));
// 填充空格后,确保总可见宽度一致
$lineContentPadded = $lineContent . $padding;
// 应用颜色转义码,仅包裹内容部分,不包括填充
$coloredLineContent = $colorStart . $lineContent . $colorEnd . $padding;
// 构建完整的行,以 '*' 开始和结束
$line = "*" . $coloredLineContent . "*";
$insertLines[] = $line;
}
// 7. 在底部边框行之前插入新行数组
array_splice($lines, $bottomBorderIndex, 0, $insertLines);
// 8. 重组内容并写回文件
$newContent = implode("\n", $lines);
Storage::put('public/autotest_supervise', $newContent);
}
public function destroySupervise($box)
{
if(env('DEBUG')){
return [
'status' => TRUE,
'message' => "监管已成功移除。"
];
}
$userName = $box->ssh_name;
$appUrl = env('APP_URL'); // 获取当前平台的 APP_URL
try {
// 创建 SSH 客户端实例
$ssh = new SSH2($box['ssh_ip'], $box['ssh_port']);
$key = Storage::get('id_rsa');
// 使用 PublicKeyLoader 加载私钥
$rsa = PublicKeyLoader::load($key);
if (!$ssh->login($userName, $rsa)) {
return [
'message' => "SSH 登录失败",
'code' => "-1",
'status' => FALSE
];
}
// 检查远程文件是否存在并读取内容
$remoteFilePath = '/usr/local/disk/autotest_supervise';
$checkCommand = "if [ -f {$remoteFilePath} ]; then cat {$remoteFilePath}; else echo 'FILE_NOT_EXIST'; fi";
$remoteContent = $ssh->exec($checkCommand);
$remoteContent = trim($remoteContent);
if ($remoteContent === 'FILE_NOT_EXIST') {
// 文件不存在,说明没有添加过监管,可以返回成功或提示无监管
return [
'message' => "该设备未被监管或已被移除。",
'code' => "0",
'status' => TRUE
];
} else {
// 文件存在,检查内容是否为空或包含当前平台的 APP_URL if ($remoteContent !== '' && strpos($remoteContent, $appUrl) === FALSE) {
return [
'message' => "此设备已经被其他平台添加至监管,请前往其他平台解除监管后再进行操作。",
'code' => "-4",
'status' => FALSE
];
}
// 如果内容为空或包含当前 APP_URL,可以继续执行移除操作
}
// 创建 SFTP 实例进行文件删除
$sftp = new SFTP($box['ssh_ip'], $box['ssh_port']);
if (!$sftp->login($userName, $rsa)) {
return [
'message' => "SFTP 登录失败",
'code' => "-1",
'status' => FALSE
];
}
// 删除远程 supervise 文件
if (!$sftp->delete($remoteFilePath)) {
return [
'message' => "SFTP 删除文件失败,需要确认权限及文件存在",
'code' => "-2",
'status' => FALSE
];
}
$sftp->disconnect();
// 移除 /etc/profile 中的相关内容
if ($userName == 'root') {
$command = 'sudo sed -i \'/^if \[ -f \/usr\/local\/disk\/autotest_supervise \]; then$/,/^fi$/d\' /etc/profile';
$ssh->exec($command);
} else {
// 切换到 sudo 用户并执行命令
$ssh->write("sudo su\n");
$ssh->read('密码:');
$ssh->write("nle\n"); // 请根据实际情况修改密码
$ssh->setTimeout(2);
$ssh->read();
$command = 'sudo sed -i \'/^if \[ -f \/usr\/local\/disk\/autotest_supervise \]; then$/,/^fi$/d\' /etc/profile' . "\n";
$ssh->write($command);
$ssh->read();
}
// 关闭 SSH 连接
$ssh->disconnect();
return [
'status' => TRUE,
'message' => "监管已成功移除。"
];
} catch (Exception $e) {
return [
'status' => FALSE,
'line' => $e->getLine(),
'message' => $e->getMessage(),
];
}
}
本作品采用《CC 协议》,转载必须注明作者和本文链接