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(),
            ];
        }
    }

laravel+ Liunx 修改ssh连接终端的展示代码记录

laravel+ Liunx 修改ssh连接终端的展示代码记录

本作品采用《CC 协议》,转载必须注明作者和本文链接
chowjiawei
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!
测开 @ 新大陆数字技术股份有限公司
文章
76
粉丝
42
喜欢
238
收藏
410
排名:238
访问:4.0 万
私信
所有博文
博客标签
社区赞助商