Linux Web终端连接

文章目录

    • [Linux Web终端连接](#Linux Web终端连接)
      • [使用Python Flask + Paramiko](#使用Python Flask + Paramiko)
        • [1. 在CentOS8/TencentOS3.x上安装所需软件](#1. 在CentOS8/TencentOS3.x上安装所需软件)
        • [2. 创建Web应用](#2. 创建Web应用)
        • [3. 安装Python依赖并运行](#3. 安装Python依赖并运行)
        • [4. 访问Web界面](#4. 访问Web界面)

Linux Web终端连接

使用Python Flask + Paramiko

1. 在CentOS8/TencentOS3.x上安装所需软件
bash 复制代码
# 更新系统
sudo dnf update -y

# 安装Python和必要组件
sudo dnf install python3 python3-pip openssh-server -y

# 启动SSH服务
sudo systemctl enable --now sshd

# 确保防火墙允许HTTP/SSH
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=ssh
sudo firewall-cmd --reload
2. 创建Web应用

创建文件 web_shell.py

python 复制代码
#!/usr/bin/env python3

from flask import Flask, request, jsonify, render_template_string
import paramiko
import logging
import json

app = Flask(__name__)
logging.basicConfig(level=logging.WARNING)

class SSHSession:
    def __init__(self, hostname, username, password):
        self.ssh = paramiko.SSHClient()
        self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        self.ssh.connect(hostname, username=username, password=password, timeout=10)

    def execute(self, command):
        stdin, stdout, stderr = self.ssh.exec_command(command)
        output = stdout.read().decode('utf-8', errors='ignore')
        error = stderr.read().decode('utf-8', errors='ignore')
        return output + (error if error else "")

    def close(self):
        self.ssh.close()

sessions = {}

HTML = '''
<!DOCTYPE html>
<html>
<head>
    <title>Linux Web终端</title>
    <meta charset="utf-8">
    <style>
        body { font-family: Arial; margin: 20px; background: #f0f0f0; }
        .container { max-width: 800px; margin: auto; background: white; padding: 20px; border-radius: 5px; box-shadow: 0 0 10px rgba(0,0,0,0.1); }
        h2 { color: #333; border-bottom: 1px solid #ddd; padding-bottom: 10px; }
        .form-group { margin: 15px 0; }
        label { display: block; margin-bottom: 5px; font-weight: bold; }
        input[type="text"], input[type="password"], textarea {
            width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 3px; box-sizing: border-box;
        }
        button {
            background: #4CAF50; color: white; border: none; padding: 10px 15px;
            border-radius: 3px; cursor: pointer; margin-right: 10px;
        }
        button:hover { background: #45a049; }
        .danger { background: #f44336; }
        .danger:hover { background: #d32f2f; }
        .output {
            background: #000; color: #0f0; padding: 10px; border-radius: 3px;
            font-family: monospace; white-space: pre-wrap; margin-top: 10px;
            max-height: 400px; overflow-y: auto;
        }
        .terminal { display: none; }
        .session-info { background: #e7f7e7; padding: 10px; border-radius: 3px; margin-bottom: 15px; }
    </style>
</head>
<body>
    <div class="container">
        <h2>Linux Web终端连接</h2>

        <div id="login-form">
            <div class="form-group">
                <label>目标服务器IP:</label>
                <input type="text" id="host" value="192.168.100.140">
            </div>
            <div class="form-group">
                <label>用户名:</label>
                <input type="text" id="username" value="root">
            </div>
            <div class="form-group">
                <label>密码:</label>
                <input type="password" id="password">
            </div>
            <button onclick="connectSSH()">连接到服务器</button>
            <p style="color: #666; font-size: 12px; margin-top: 20px;">
                <strong>注意:</strong> 仅用于测试环境
            </p>
        </div>

        <div id="terminal" class="terminal">
            <div class="session-info">
                已连接到: <span id="connected-host"></span> |
                用户: <span id="connected-user"></span>
                <button onclick="logout()" style="float:right;">断开连接</button>
            </div>

            <div class="form-group">
                <label>输入命令:</label>
                <textarea id="command" rows="2" placeholder="输入Linux命令..."></textarea>
            </div>

            <button onclick="executeCommand()">执行命令</button>
            <button onclick="sendCommand('pwd')">pwd</button>
            <button onclick="sendCommand('ls -la')">ls -la</button>
            <button onclick="sendCommand('whoami')">whoami</button>
            <button onclick="sendCommand('df -h')">df -h</button>
            <button onclick="clearOutput()" class="danger">清屏</button>

            <h3>输出:</h3>
            <div id="output" class="output"></div>
        </div>
    </div>

    <script>
        let sessionId = null;

        function connectSSH() {
            const host = document.getElementById('host').value;
            const username = document.getElementById('username').value;
            const password = document.getElementById('password').value;

            if (!host || !username || !password) {
                alert('请填写所有字段');
                return;
            }

            fetch('/connect', {
                method: 'POST',
                headers: {'Content-Type': 'application/json'},
                body: JSON.stringify({
                    host: host,
                    username: username,
                    password: password
                })
            })
            .then(response => response.json())
            .then(data => {
                if (data.success) {
                    sessionId = data.session_id;
                    document.getElementById('login-form').style.display = 'none';
                    document.getElementById('terminal').style.display = 'block';
                    document.getElementById('connected-host').textContent = host;
                    document.getElementById('connected-user').textContent = username;
                    document.getElementById('output').textContent = '连接成功!\\n输入命令开始...';
                } else {
                    alert('连接失败: ' + data.error);
                }
            })
            .catch(error => {
                console.error('Error:', error);
                alert('连接出错: ' + error);
            });
        }

        function executeCommand() {
            if (!sessionId) {
                alert('请先连接到服务器');
                return;
            }

            const command = document.getElementById('command').value.trim();
            if (!command) {
                alert('请输入命令');
                return;
            }

            sendCommand(command);
        }

        function sendCommand(cmd) {
            if (!sessionId) return;

            document.getElementById('command').value = cmd;

            fetch('/execute', {
                method: 'POST',
                headers: {'Content-Type': 'application/json'},
                body: JSON.stringify({
                    session_id: sessionId,
                    command: cmd
                })
            })
            .then(response => response.json())
            .then(data => {
                const outputDiv = document.getElementById('output');
                outputDiv.textContent += '\\n$ ' + cmd + '\\n' + data.output + '\\n';
                outputDiv.scrollTop = outputDiv.scrollHeight;
                document.getElementById('command').value = '';
            })
            .catch(error => {
                console.error('Error:', error);
                document.getElementById('output').textContent += '\\n错误: ' + error;
            });
        }

        function clearOutput() {
            document.getElementById('output').textContent = '';
        }

        function logout() {
            if (sessionId) {
                fetch('/logout', {
                    method: 'POST',
                    headers: {'Content-Type': 'application/json'},
                    body: JSON.stringify({session_id: sessionId})
                });
            }
            sessionId = null;
            document.getElementById('terminal').style.display = 'none';
            document.getElementById('login-form').style.display = 'block';
            document.getElementById('password').value = '';
        }

        // 按Enter键执行命令
        document.getElementById('command').addEventListener('keypress', function(e) {
            if (e.key === 'Enter' && !e.shiftKey) {
                e.preventDefault();
                executeCommand();
            }
        });

        // 按Ctrl+L清屏
        document.addEventListener('keydown', function(e) {
            if (e.ctrlKey && e.key === 'l') {
                e.preventDefault();
                clearOutput();
            }
        });
    </script>
</body>
</html>
'''

@app.route('/')
def index():
    return HTML

@app.route('/connect', methods=['POST'])
def connect():
    try:
        data = request.json
        print(f"连接请求: {data['host']} 用户: {data['username']}")

        session = SSHSession(data['host'], data['username'], data['password'])
        session_id = str(id(session))
        sessions[session_id] = session

        return jsonify({'success': True, 'session_id': session_id})
    except Exception as e:
        print(f"连接错误: {str(e)}")
        return jsonify({'success': False, 'error': str(e)})

@app.route('/execute', methods=['POST'])
def execute():
    try:
        data = request.json
        session_id = data['session_id']

        if session_id not in sessions:
            return jsonify({'output': '会话已过期,请重新连接'})

        command = data['command']

        # 简单的危险命令过滤
        dangerous = ['rm -rf', 'mkfs', 'dd', ':(){:|:&};:', 'shutdown', 'halt', 'reboot']
        for d in dangerous:
            if d in command:
                return jsonify({'output': f'阻止危险命令: {d}'})

        output = sessions[session_id].execute(command)
        return jsonify({'output': output})
    except Exception as e:
        return jsonify({'output': f'执行错误: {str(e)}'})

@app.route('/logout', methods=['POST'])
def logout():
    data = request.json
    session_id = data.get('session_id')

    if session_id in sessions:
        sessions[session_id].close()
        del sessions[session_id]

    return jsonify({'success': True})

if __name__ == '__main__':
    print("=" * 60)
    print("Web终端服务启动中...")
    print("访问地址: http://192.168.100.140:5000")
    print("=" * 60)
    app.run(host='0.0.0.0', port=5000, debug=True, use_reloader=False)
3. 安装Python依赖并运行
bash 复制代码
# 安装Python依赖
pip3 install flask paramiko

# 执行 pip3 install flask paramiko 报错后,解决方法:(没有报错不需要执行)
	# 1. 升级 pip 到最新版(解决绝大多数 Rust 扩展编译问题)
	python3 -m pip install --upgrade pip

	# 2. 安装系统级编译工具(CentOS/RHEL/Fedora)
	sudo yum install gcc libffi-devel python3-devel openssl-devel -y

	# 3. 重新安装 paramiko(会自动安装 bcrypt)
	pip3 install --user flask paramiko


# 运行Web应用(在后台运行)
nohup python3 web_shell.py > web_shell.log 2>&1 &

# 查看运行日志
tail -f web_shell.log

# 停止应用
pkill -f web_shell.py
4. 访问Web界面

在浏览器中访问:http://192.168.100.140:5000


相关推荐
oMcLin2 小时前
如何通过 TCP BBR 与 FQ_codel 优化 Linux 网络带宽:提升跨境电商与视频流业务的响应速度
linux·网络·tcp/ip
Object~2 小时前
2.变量声明
开发语言·前端·javascript
IT_陈寒2 小时前
Vite 3实战:我用这5个优化技巧让HMR构建速度提升了40%
前端·人工智能·后端
2501_927773072 小时前
Linux系统编程——TCP并发模型
linux·网络·tcp/ip
_OP_CHEN2 小时前
【测试理论与实践】(四)测试用例篇(上):从概念到万能思路,解锁测试设计核心密码
运维·测试开发·测试用例·bug·压力测试·测试·网络测试
大爱编程♡2 小时前
JAVAEE-前端三剑客
java·前端·java-ee
下雨打伞干嘛2 小时前
前端学习官网文档
前端·学习
维李设论2 小时前
从2025看2026前端发展趋势
前端·架构·aigc·ai编程·大前端·趋势·前端工程师
胡萝卜3.02 小时前
程序构建核心解析:从预处理到链接的完整指南
运维·服务器·c++·人工智能·操作系统·编译原理·系统编成