11. Python环境准备
11.1 Python版本检查
11.1.1 检查系统Python版本
查看Python版本:
# 检查Python 3版本
python3 --version
# 检查pip版本
pip3 --version
# 查看Python安装路径
which python3
# 查看详细信息
python3 -c "import sys; print(sys.version)"
预期输出:
Python 3.10.12
pip 22.0.2 from /usr/lib/python3/dist-packages/pip (python 3.10)
/usr/bin/python3
3.10.12 (main, Nov 20 2023, 15:14:05) [GCC 11.4.0]
11.1.2 版本要求
最低要求:
Python: 3.8+
pip: 20.0+
setuptools: 45.0+
推荐版本:
Python: 3.10.x (本项目使用)
pip: 22.0+
setuptools: 59.0+
⚠️ 重要:Python 3.7及以下版本不支持某些现代语法特性,可能导致兼容性问题。
11.1.3 升级Python(如需要)
Ubuntu系统升级Python:
# 添加deadsnakes PPA(提供最新Python版本)
sudo add-apt-repository ppa:deadsnakes/ppa
sudo apt update
# 安装Python 3.10
sudo apt install python3.10 python3.10-venv python3.10-dev
# 更新alternatives(切换默认Python版本)
sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.10 1
# 验证版本
python3 --version
升级pip:
# 升级pip到最新版本
python3 -m pip install --upgrade pip
# 验证pip版本
pip3 --version
11.2 pip安装
11.2.1 安装pip包管理器
Ubuntu/Debian系统:
# 更新软件源
sudo apt update
# 安装pip
sudo apt install python3-pip
# 验证安装
pip3 --version
CentOS/RHEL系统:
# 安装EPEL仓库
sudo yum install epel-release
# 安装pip
sudo yum install python3-pip
# 验证安装
pip3 --version
11.2.2 pip配置优化
配置国内镜像源(加速下载):
# 创建pip配置目录
mkdir -p ~/.pip
# 创建配置文件
nano ~/.pip/pip.conf
配置内容:
[global]
index-url = https://pypi.tuna.tsinghua.edu.cn/simple
trusted-host = pypi.tuna.tsinghua.edu.cn
[install]
timeout = 60
常用国内镜像源:
清华大学:https://pypi.tuna.tsinghua.edu.cn/simple
阿里云:https://mirrors.aliyun.com/pypi/simple/
中科大:https://pypi.mirrors.ustc.edu.cn/simple/
豆瓣:https://pypi.douban.com/simple/
临时使用镜像源:
pip3 install Flask -i https://pypi.tuna.tsinghua.edu.cn/simple
11.2.3 pip常用命令
安装软件包:
# 安装指定版本
pip3 install Flask==2.3.3
# 安装最新版本
pip3 install Flask
# 从requirements.txt安装
pip3 install -r requirements.txt
# 安装本地包
pip3 install /path/to/package.tar.gz
查询软件包:
# 列出已安装的包
pip3 list
# 查看包详细信息
pip3 show Flask
# 搜索包
pip3 search flask # 注:该功能已被PyPI禁用
# 检查可更新的包
pip3 list --outdated
卸载软件包:
# 卸载单个包
pip3 uninstall Flask
# 卸载多个包
pip3 uninstall Flask requests -y
# 卸载所有包(危险操作)
pip3 freeze | xargs pip3 uninstall -y
导出依赖列表:
# 导出当前环境所有包
pip3 freeze > requirements.txt
# 仅导出项目使用的包(推荐使用pipreqs)
pip3 install pipreqs
pipreqs /path/to/project
11.3 依赖包安装命令
11.3.1 创建requirements.txt
项目依赖清单:
# 创建依赖文件
cat > /home/vpn-demo/requirements.txt << 'EOF'
Flask==2.3.3
Werkzeug==2.3.7
click==8.1.7
itsdangerous==2.1.2
Jinja2==3.1.2
MarkupSafe==2.1.3
EOF
📝 说明:以上是Flask及其依赖的精确版本,确保环境一致性。
11.3.2 安装依赖包
方法1:使用requirements.txt(推荐)
# 进入项目目录
cd /home/vpn-demo
# 安装所有依赖
sudo pip3 install -r requirements.txt
# 验证安装
pip3 list | grep Flask
方法2:直接安装Flask
# 安装指定版本
sudo pip3 install Flask==2.3.3
# 或安装最新版本
sudo pip3 install Flask
11.3.3 验证依赖安装
检查Flask安装:
# 方法1:使用pip
pip3 show Flask
# 方法2:使用Python
python3 -c "import flask; print(flask.__version__)"
# 方法3:查看安装路径
python3 -c "import flask; print(flask.__file__)"
预期输出:
Name: Flask
Version: 2.3.3
Summary: A simple framework for building complex web applications.
Home-page: https://palletsprojects.com/p/flask
Author: Armin Ronacher
License: BSD-3-Clause
Location: /usr/local/lib/python3.10/dist-packages
Requires: Werkzeug, Jinja2, itsdangerous, click
Required-by:
检查所有依赖:
# 检查Flask及其依赖树
pip3 show Flask Werkzeug Jinja2 click itsdangerous MarkupSafe
# 或使用pipdeptree(需要先安装)
pip3 install pipdeptree
pipdeptree -p Flask
11.3.4 处理依赖冲突
常见问题:版本冲突
# 问题示例
ERROR: pip's dependency resolver does not currently take into account
all the packages that are installed. This behaviour is the source of
the following dependency conflicts.
解决方案:
# 方法1:强制安装指定版本
pip3 install --force-reinstall Flask==2.3.3
# 方法2:忽略依赖检查(不推荐)
pip3 install --no-deps Flask==2.3.3
# 方法3:使用虚拟环境(推荐)
python3 -m venv /home/vpn-demo/venv
source /home/vpn-demo/venv/bin/activate
pip3 install -r requirements.txt
11.4 虚拟环境配置(可选)
11.4.1 为什么使用虚拟环境
优点:
- ✅ 隔离项目依赖,避免版本冲突
- ✅ 便于项目迁移和复制
- ✅ 不影响系统Python环境
- ✅ 易于管理多个项目
缺点:
- ❌ 需要激活虚拟环境
- ❌ 占用额外磁盘空间
- ❌ 对初学者略复杂
💡 建议:生产环境推荐使用虚拟环境;简单测试可以直接安装到系统。
11.4.2 创建虚拟环境
使用venv(Python内置):
# 创建虚拟环境
python3 -m venv /home/vpn-demo/venv
# 激活虚拟环境
source /home/vpn-demo/venv/bin/activate
# 验证环境(提示符会变化)
which python3
# 输出:/home/vpn-demo/venv/bin/python3
# 升级pip
pip3 install --upgrade pip
使用virtualenv(第三方工具):
# 安装virtualenv
sudo pip3 install virtualenv
# 创建虚拟环境
virtualenv /home/vpn-demo/venv
# 激活虚拟环境
source /home/vpn-demo/venv/bin/activate
11.4.3 在虚拟环境中安装依赖
激活环境后安装:
# 确保虚拟环境已激活(提示符显示(venv))
(venv) user@host:~$
# 安装依赖
pip3 install -r /home/vpn-demo/requirements.txt
# 验证安装
pip3 list
11.4.4 退出和删除虚拟环境
退出虚拟环境:
# 退出命令
deactivate
# 提示符恢复正常
user@host:~$
删除虚拟环境:
# 直接删除目录
rm -rf /home/vpn-demo/venv
# 重新创建(如需要)
python3 -m venv /home/vpn-demo/venv
11.4.5 systemd服务中使用虚拟环境
修改启动脚本:
#!/bin/bash
# 激活虚拟环境
source /home/vpn-demo/venv/bin/activate
# 启动Flask应用
python3 /home/vpn-demo/vpn_api.py
systemd服务文件:
[Unit]
Description=VPN Management API
After=network.target
[Service]
Type=simple
User=root
WorkingDirectory=/home/vpn-demo
ExecStart=/home/vpn-demo/venv/bin/python3 /home/vpn-demo/vpn_api.py
Restart=on-failure
[Install]
WantedBy=multi-user.target
12. Flask后端部署
12.1 vpn_api.py代码
12.1.1 完整代码清单
创建Flask应用文件:
# 创建项目目录
sudo mkdir -p /home/vpn-demo
cd /home/vpn-demo
# 创建API文件
sudo nano vpn_api.py
完整代码(vpn_api.py):
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
WireGuard VPN 管理 API
提供VPN服务的启动、停止、状态查询和性能测试功能
"""
from flask import Flask, jsonify, request
import subprocess
import json
import re
import time
app = Flask(__name__)
# ==================== 工具函数 ====================
def run_command(cmd, shell=False):
"""
执行系统命令并返回结果
Args:
cmd: 命令字符串或列表
shell: 是否使用shell模式
Returns:
dict: {
'success': bool,
'output': str,
'error': str,
'returncode': int
}
"""
try:
if shell:
result = subprocess.run(
cmd,
shell=True,
capture_output=True,
text=True,
timeout=30
)
else:
result = subprocess.run(
cmd,
capture_output=True,
text=True,
timeout=30
)
return {
'success': result.returncode == 0,
'output': result.stdout.strip(),
'error': result.stderr.strip(),
'returncode': result.returncode
}
except subprocess.TimeoutExpired:
return {
'success': False,
'output': '',
'error': 'Command timeout',
'returncode': -1
}
except Exception as e:
return {
'success': False,
'output': '',
'error': str(e),
'returncode': -1
}
# ==================== API路由 ====================
@app.route('/api/vpn/status', methods=['GET'])
def get_vpn_status():
"""
查询VPN运行状态
Returns:
JSON: {
'status': 'running' | 'stopped',
'interface': 'wg0',
'peers': int,
'details': str
}
"""
result = run_command(['sudo', 'wg', 'show'])
if result['success'] and result['output']:
# 解析peer数量
peers = len([line for line in result['output'].split('\n')
if line.startswith('peer:')])
return jsonify({
'status': 'running',
'interface': 'wg0',
'peers': peers,
'details': result['output']
})
else:
return jsonify({
'status': 'stopped',
'interface': 'wg0',
'peers': 0,
'details': 'WireGuard is not running'
})
@app.route('/api/vpn/start', methods=['POST'])
def start_vpn():
"""
启动VPN服务
Returns:
JSON: {
'success': bool,
'message': str,
'details': str
}
"""
# 检查是否已经运行
check = run_command(['sudo', 'wg', 'show'])
if check['success'] and check['output']:
return jsonify({
'success': False,
'message': 'VPN is already running',
'details': check['output']
}), 400
# 启动WireGuard
result = run_command(['sudo', 'wg-quick', 'up', 'wg0'])
if result['success']:
# 等待接口完全启动
time.sleep(2)
# 验证启动状态
verify = run_command(['sudo', 'wg', 'show'])
return jsonify({
'success': True,
'message': 'VPN started successfully',
'details': verify['output']
})
else:
return jsonify({
'success': False,
'message': 'Failed to start VPN',
'details': result['error']
}), 500
@app.route('/api/vpn/stop', methods=['POST'])
def stop_vpn():
"""
停止VPN服务
Returns:
JSON: {
'success': bool,
'message': str,
'details': str
}
"""
# 检查是否在运行
check = run_command(['sudo', 'wg', 'show'])
if not check['success'] or not check['output']:
return jsonify({
'success': False,
'message': 'VPN is not running',
'details': 'WireGuard interface not found'
}), 400
# 停止WireGuard
result = run_command(['sudo', 'wg-quick', 'down', 'wg0'])
if result['success']:
return jsonify({
'success': True,
'message': 'VPN stopped successfully',
'details': result['output']
})
else:
return jsonify({
'success': False,
'message': 'Failed to stop VPN',
'details': result['error']
}), 500
@app.route('/api/vpn/test/latency', methods=['GET'])
def test_latency():
"""
测试VPN延迟
使用ping命令测试10.8.0.2(客户端)的延迟
Returns:
JSON: {
'success': bool,
'target': str,
'packets_sent': int,
'packets_received': int,
'packet_loss': float,
'rtt_min': float,
'rtt_avg': float,
'rtt_max': float,
'rtt_mdev': float
}
"""
target = '10.8.0.2'
count = 10
# 执行ping测试
result = run_command(['ping', '-c', str(count), target])
if not result['success']:
return jsonify({
'success': False,
'message': 'Ping test failed',
'details': result['error']
}), 500
# 解析ping结果
output = result['output']
# 提取统计信息
# 示例:10 packets transmitted, 10 received, 0% packet loss
stats_match = re.search(
r'(\d+) packets transmitted, (\d+) received, ([\d.]+)% packet loss',
output
)
# 提取RTT信息
# 示例:rtt min/avg/max/mdev = 0.032/0.065/0.173/0.020 ms
rtt_match = re.search(
r'rtt min/avg/max/mdev = ([\d.]+)/([\d.]+)/([\d.]+)/([\d.]+) ms',
output
)
if stats_match and rtt_match:
return jsonify({
'success': True,
'target': target,
'packets_sent': int(stats_match.group(1)),
'packets_received': int(stats_match.group(2)),
'packet_loss': float(stats_match.group(3)),
'rtt_min': float(rtt_match.group(1)),
'rtt_avg': float(rtt_match.group(2)),
'rtt_max': float(rtt_match.group(3)),
'rtt_mdev': float(rtt_match.group(4)),
'unit': 'ms'
})
else:
return jsonify({
'success': False,
'message': 'Failed to parse ping results',
'details': output
}), 500
@app.route('/api/vpn/test/bandwidth', methods=['GET'])
def test_bandwidth():
"""
测试VPN带宽
通过WireGuard传输统计估算带宽
Returns:
JSON: {
'success': bool,
'rx_bytes': int,
'tx_bytes': int,
'rx_mbps': float,
'tx_mbps': float
}
"""
# 获取传输统计
result = run_command(['sudo', 'wg', 'show', 'wg0', 'transfer'])
if not result['success']:
return jsonify({
'success': False,
'message': 'Failed to get transfer statistics',
'details': result['error']
}), 500
# 解析输出
# 示例:peer_pubkey 12345678 87654321
lines = result['output'].strip().split('\n')
if len(lines) == 0:
return jsonify({
'success': False,
'message': 'No peer data available',
'details': 'No connected peers'
}), 404
# 取第一个peer的数据
parts = lines[0].split()
if len(parts) >= 3:
rx_bytes = int(parts[1])
tx_bytes = int(parts[2])
# 简单估算:假设数据在60秒内传输
time_window = 60
rx_mbps = (rx_bytes * 8) / (time_window * 1000000)
tx_mbps = (tx_bytes * 8) / (time_window * 1000000)
return jsonify({
'success': True,
'rx_bytes': rx_bytes,
'tx_bytes': tx_bytes,
'rx_mbps': round(rx_mbps, 2),
'tx_mbps': round(tx_mbps, 2),
'note': 'Bandwidth estimation based on transfer statistics'
})
else:
return jsonify({
'success': False,
'message': 'Failed to parse transfer data',
'details': result['output']
}), 500
@app.route('/api/system/info', methods=['GET'])
def get_system_info():
"""
获取系统信息
Returns:
JSON: {
'hostname': str,
'kernel': str,
'uptime': str,
'load_average': str
}
"""
# 获取主机名
hostname = run_command(['hostname'])['output']
# 获取内核版本
kernel = run_command(['uname', '-r'])['output']
# 获取系统运行时间
uptime = run_command(['uptime', '-p'])['output']
# 获取负载
load = run_command(['cat', '/proc/loadavg'])['output'].split()[:3]
return jsonify({
'hostname': hostname,
'kernel': kernel,
'uptime': uptime,
'load_average': ' '.join(load),
'timestamp': int(time.time())
})
# ==================== 健康检查 ====================
@app.route('/health', methods=['GET'])
def health_check():
"""API健康检查"""
return jsonify({
'status': 'healthy',
'service': 'VPN Management API',
'version': '1.0'
})
# ==================== 错误处理 ====================
@app.errorhandler(404)
def not_found(error):
"""404错误处理"""
return jsonify({
'error': 'Not Found',
'message': 'The requested endpoint does not exist'
}), 404
@app.errorhandler(500)
def internal_error(error):
"""500错误处理"""
return jsonify({
'error': 'Internal Server Error',
'message': 'An unexpected error occurred'
}), 500
# ==================== 主程序 ====================
if __name__ == '__main__':
# 生产环境配置
app.run(
host='0.0.0.0', # 监听所有接口
port=5000, # 端口
debug=False, # 关闭调试模式
threaded=True # 启用多线程
)
12.1.2 代码结构说明
模块组织:
vpn_api.py
├── 导入模块 (Flask, subprocess, etc.)
├── Flask应用初始化
├── 工具函数
│ └── run_command() # 执行系统命令
├── API路由
│ ├── GET /api/vpn/status # 查询状态
│ ├── POST /api/vpn/start # 启动VPN
│ ├── POST /api/vpn/stop # 停止VPN
│ ├── GET /api/vpn/test/latency # 延迟测试
│ ├── GET /api/vpn/test/bandwidth# 带宽测试
│ └── GET /api/system/info # 系统信息
├── 健康检查
│ └── GET /health # 健康状态
├── 错误处理
│ ├── 404处理
│ └── 500处理
└── 主程序入口
12.1.3 代码关键点解析
1. subprocess安全执行:
# 使用列表形式传递命令(防止shell注入)
result = subprocess.run(
['sudo', 'wg', 'show'], # 不使用shell=True
capture_output=True,
text=True,
timeout=30 # 设置超时防止hang
)
2. 错误处理:
try:
result = subprocess.run(...)
return {
'success': result.returncode == 0,
'output': result.stdout.strip(),
'error': result.stderr.strip()
}
except subprocess.TimeoutExpired:
# 超时处理
except Exception as e:
# 其他异常处理
3. 正则表达式解析:
# 解析ping输出
stats_match = re.search(
r'(\d+) packets transmitted, (\d+) received, ([\d.]+)% packet loss',
output
)
if stats_match:
packets_sent = int(stats_match.group(1))
packets_received = int(stats_match.group(2))
packet_loss = float(stats_match.group(3))
4. JSON响应格式:
return jsonify({
'success': True,
'message': 'Operation completed',
'details': data
})
12.2 API接口说明
12.2.1 接口列表
| 方法 | 路径 | 功能 | 权限 |
|---|---|---|---|
| GET | /api/vpn/status | 查询VPN状态 | 读取 |
| POST | /api/vpn/start | 启动VPN服务 | root |
| POST | /api/vpn/stop | 停止VPN服务 | root |
| GET | /api/vpn/test/latency | 延迟测试 | 读取 |
| GET | /api/vpn/test/bandwidth | 带宽测试 | 读取 |
| GET | /api/system/info | 系统信息 | 读取 |
| GET | /health | 健康检查 | 无 |
12.2.2 接口详细说明
1. GET /api/vpn/status
查询WireGuard VPN当前运行状态。
请求示例:
curl http://localhost:5000/api/vpn/status
响应示例(运行中):
{
"status": "running",
"interface": "wg0",
"peers": 1,
"details": "interface: wg0\n public key: rUEB9pR7DO7qgkbo6Ylerrh/Fvl1XA8RJuALBdlkFkk=\n private key: (hidden)\n listening port: 51820\n\npeer: QcDIGh0MyyLJoovsb7hMccZGwMha7aqfa/3cyFtfhE4=\n endpoint: 192.168.1.100:54231\n allowed ips: 10.8.0.2/32\n latest handshake: 45 seconds ago\n transfer: 1.23 MiB received, 856.78 KiB sent"
}
响应示例(已停止):
{
"status": "stopped",
"interface": "wg0",
"peers": 0,
"details": "WireGuard is not running"
}
2. POST /api/vpn/start
启动WireGuard VPN服务。
请求示例:
curl -X POST http://localhost:5000/api/vpn/start
成功响应:
{
"success": true,
"message": "VPN started successfully",
"details": "interface: wg0\n public key: rUEB9pR7DO7qgkbo6Ylerrh/Fvl1XA8RJuALBdlkFkk=\n listening port: 51820"
}
失败响应(已在运行):
{
"success": false,
"message": "VPN is already running",
"details": "..."
}
3. POST /api/vpn/stop
停止WireGuard VPN服务。
请求示例:
curl -X POST http://localhost:5000/api/vpn/stop
成功响应:
{
"success": true,
"message": "VPN stopped successfully",
"details": ""
}
失败响应(未在运行):
{
"success": false,
"message": "VPN is not running",
"details": "WireGuard interface not found"
}
4. GET /api/vpn/test/latency
测试VPN隧道延迟(ping 10.8.0.2)。
请求示例:
curl http://localhost:5000/api/vpn/test/latency
成功响应:
{
"success": true,
"target": "10.8.0.2",
"packets_sent": 10,
"packets_received": 10,
"packet_loss": 0.0,
"rtt_min": 0.032,
"rtt_avg": 0.065,
"rtt_max": 0.173,
"rtt_mdev": 0.020,
"unit": "ms"
}
失败响应:
{
"success": false,
"message": "Ping test failed",
"details": "Destination Host Unreachable"
}
5. GET /api/vpn/test/bandwidth
测试VPN带宽(基于传输统计估算)。
请求示例:
curl http://localhost:5000/api/vpn/test/bandwidth
成功响应:
{
"success": true,
"rx_bytes": 1290240,
"tx_bytes": 877568,
"rx_mbps": 0.17,
"tx_mbps": 0.12,
"note": "Bandwidth estimation based on transfer statistics"
}
6. GET /api/system/info
获取系统基本信息。
请求示例:
curl http://localhost:5000/api/system/info
响应示例:
{
"hostname": "eve-ng",
"kernel": "5.15.0-91-generic",
"uptime": "up 2 days, 5 hours, 32 minutes",
"load_average": "0.15 0.18 0.12",
"timestamp": 1711180800
}
7. GET /health
API健康检查。
请求示例:
curl http://localhost:5000/health
响应示例:
{
"status": "healthy",
"service": "VPN Management API",
"version": "1.0"
}
12.3 权限配置
12.3.1 sudo权限配置
Flask应用需要以root权限执行WireGuard命令,配置sudo免密执行。
方法1:配置sudoers(推荐)
# 编辑sudoers文件
sudo visudo
在文件末尾添加:
# 允许www-data用户免密执行WireGuard命令
www-data ALL=(ALL) NOPASSWD: /usr/bin/wg
www-data ALL=(ALL) NOPASSWD: /usr/bin/wg-quick
www-data ALL=(ALL) NOPASSWD: /bin/ping
# 或允许所有用户(不推荐)
ALL ALL=(ALL) NOPASSWD: /usr/bin/wg, /usr/bin/wg-quick, /bin/ping
保存并退出(Ctrl+X, Y, Enter)。
验证配置:
# 测试sudo免密
sudo -u www-data sudo wg show
# 应该能正常执行,不要求密码
⚠️ 安全提示:仅授权必要的命令,不要使用NOPASSWD: ALL。
方法2:使用root用户运行(简单但不推荐)
# 直接以root用户运行Flask应用
sudo python3 vpn_api.py
💡 说明:本项目使用此方法,简单直接,但生产环境不推荐。
12.3.2 文件权限设置
设置vpn_api.py权限:
# 设置所有者
sudo chown root:root /home/vpn-demo/vpn_api.py
# 设置可执行权限
sudo chmod 755 /home/vpn-demo/vpn_api.py
# 验证权限
ls -l /home/vpn-demo/vpn_api.py
预期输出:
-rwxr-xr-x 1 root root 12345 Mar 23 10:00 /home/vpn-demo/vpn_api.py
设置日志目录权限:
# 创建日志目录
sudo mkdir -p /var/log/vpn-demo
# 设置权限
sudo chown -R root:root /var/log/vpn-demo
sudo chmod 755 /var/log/vpn-demo
12.3.3 防火墙权限
限制API访问(推荐):
# 仅允许本机访问API
sudo ufw deny 5000/tcp
sudo ufw allow from 127.0.0.1 to any port 5000
# 或允许内网访问
sudo ufw allow from 192.168.1.0/24 to any port 5000
# 重新加载防火墙
sudo ufw reload
临时开放(测试用):
# 临时允许所有IP访问
sudo ufw allow 5000/tcp
# 测试完成后关闭
sudo ufw delete allow 5000/tcp
12.4 启动命令
12.4.1 前台启动(测试)
直接运行:
# 进入项目目录
cd /home/vpn-demo
# 以root权限启动
sudo python3 vpn_api.py
输出示例:
* Serving Flask app 'vpn_api'
* Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Running on all addresses (0.0.0.0)
* Running on http://127.0.0.1:5000
* Running on http://192.168.1.66:5000
Press CTRL+C to quit
💡 提示:按Ctrl+C停止服务。
测试API:
# 打开新终端,测试健康检查
curl http://localhost:5000/health
# 测试VPN状态
curl http://localhost:5000/api/vpn/status
12.4.2 后台启动
方法1:使用nohup
# 后台运行,输出到日志文件
sudo nohup python3 /home/vpn-demo/vpn_api.py > /var/log/vpn-demo/api.log 2>&1 &
# 查看进程
ps aux | grep vpn_api.py
# 查看日志
tail -f /var/log/vpn-demo/api.log
停止服务:
# 查找PID
ps aux | grep vpn_api.py | grep -v grep
# 杀死进程
sudo kill -9 <PID>
# 或使用pkill
sudo pkill -f vpn_api.py
方法2:使用screen(推荐)
# 安装screen
sudo apt install screen
# 创建新会话
sudo screen -S vpn-api
# 在screen中启动Flask
cd /home/vpn-demo
sudo python3 vpn_api.py
# 分离会话(Ctrl+A, D)
# 重新连接会话
sudo screen -r vpn-api
# 查看所有会话
screen -ls
方法3:使用systemd(最推荐)
参见12.5节”进程管理”。
12.4.3 开机自启动
使用systemd服务:
# 创建服务文件
sudo nano /etc/systemd/system/vpn-api.service
服务配置:
[Unit]
Description=VPN Management API
After=network.target [email protected]
Wants[email protected]
[Service]
Type=simple
User=root
WorkingDirectory=/home/vpn-demo
ExecStart=/usr/bin/python3 /home/vpn-demo/vpn_api.py
Restart=on-failure
RestartSec=10
StandardOutput=append:/var/log/vpn-demo/api.log
StandardError=append:/var/log/vpn-demo/api-error.log
[Install]
WantedBy=multi-user.target
启用服务:
# 重新加载systemd
sudo systemctl daemon-reload
# 启动服务
sudo systemctl start vpn-api
# 设置开机自启
sudo systemctl enable vpn-api
# 查看状态
sudo systemctl status vpn-api
12.5 进程管理
12.5.1 systemd服务管理
基本命令:
# 启动服务
sudo systemctl start vpn-api
# 停止服务
sudo systemctl stop vpn-api
# 重启服务
sudo systemctl restart vpn-api
# 查看状态
sudo systemctl status vpn-api
# 查看日志
sudo journalctl -u vpn-api -f
# 查看最近100行日志
sudo journalctl -u vpn-api -n 100
自启动管理:
# 启用开机自启
sudo systemctl enable vpn-api
# 禁用开机自启
sudo systemctl disable vpn-api
# 检查是否已启用
sudo systemctl is-enabled vpn-api
服务状态示例:
● vpn-api.service - VPN Management API
Loaded: loaded (/etc/systemd/system/vpn-api.service; enabled; vendor preset: enabled)
Active: active (running) since Wed 2025-03-23 10:30:15 UTC; 2h 15min ago
Main PID: 1234 (python3)
Tasks: 1 (limit: 4638)
Memory: 25.3M
CPU: 1.234s
CGroup: /system.slice/vpn-api.service
└─1234 /usr/bin/python3 /home/vpn-demo/vpn_api.py
12.5.2 进程监控
查看进程:
# 方法1:使用ps
ps aux | grep vpn_api
# 方法2:使用pgrep
pgrep -f vpn_api.py
# 方法3:使用top
top -p $(pgrep -f vpn_api.py)
# 方法4:使用htop(更友好)
sudo apt install htop
htop -p $(pgrep -f vpn_api.py)
资源使用监控:
# CPU和内存使用
ps -p $(pgrep -f vpn_api.py) -o %cpu,%mem,cmd
# 详细信息
pidstat -p $(pgrep -f vpn_api.py) 1
网络连接监控:
# 查看监听端口
sudo netstat -tulnp | grep :5000
# 查看连接数
sudo netstat -an | grep :5000 | wc -l
# 使用ss命令
sudo ss -tulnp | grep :5000
12.5.3 日志管理
实时查看日志:
# systemd日志
sudo journalctl -u vpn-api -f
# 自定义日志文件
tail -f /var/log/vpn-demo/api.log
日志过滤:
# 查看错误日志
sudo journalctl -u vpn-api -p err
# 查看今天的日志
sudo journalctl -u vpn-api --since today
# 查看最近1小时的日志
sudo journalctl -u vpn-api --since "1 hour ago"
# 查看指定时间范围的日志
sudo journalctl -u vpn-api --since "2025-03-23 10:00" --until "2025-03-23 12:00"
日志轮转配置:
# 创建logrotate配置
sudo nano /etc/logrotate.d/vpn-demo
配置内容:
/var/log/vpn-demo/*.log {
daily
rotate 7
compress
delaycompress
missingok
notifempty
create 0640 root root
sharedscripts
postrotate
systemctl reload vpn-api > /dev/null 2>&1 || true
endscript
}
测试配置:
# 测试logrotate配置
sudo logrotate -d /etc/logrotate.d/vpn-demo
# 强制轮转
sudo logrotate -f /etc/logrotate.d/vpn-demo
12.5.4 进程保护
配置服务自动重启:
在/etc/systemd/system/vpn-api.service中已配置:
[Service]
Restart=on-failure
RestartSec=10
配置说明:
Restart=on-failure: 仅在异常退出时重启RestartSec=10: 重启间隔10秒- 其他选项:
always: 总是重启on-success: 正常退出时重启on-abnormal: 异常退出时重启
限制重启次数:
[Service]
StartLimitInterval=600
StartLimitBurst=5
说明:600秒内最多重启5次,超过则停止重启。
测试自动重启:
# 杀死进程测试
sudo kill -9 $(pgrep -f vpn_api.py)
# 等待10秒后检查
sleep 10
sudo systemctl status vpn-api
# 应该显示进程已自动重启
13. 前端页面部署
13.1 index.html代码
13.1.1 完整HTML代码
创建前端文件:
# 确保在项目目录
cd /home/vpn-demo
# 创建HTML文件
sudo nano index.html
完整代码(index.html):
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>VPN 管理系统</title>
<!-- Tailwind CSS CDN -->
<script src="https://cdn.tailwindcss.com"></script>
<style>
/* 自定义样式 */
.status-indicator {
width: 12px;
height: 12px;
border-radius: 50%;
display: inline-block;
margin-right: 8px;
}
.status-running {
background-color: #10b981;
box-shadow: 0 0 8px #10b981;
}
.status-stopped {
background-color: #ef4444;
}
.loading {
border: 3px solid #f3f3f3;
border-top: 3px solid #3498db;
border-radius: 50%;
width: 24px;
height: 24px;
animation: spin 1s linear infinite;
display: inline-block;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>
</head>
<body class="bg-gray-100">
<!-- 导航栏 -->
<nav class="bg-blue-600 text-white shadow-lg">
<div class="container mx-auto px-4 py-4">
<div class="flex justify-between items-center">
<h1 class="text-2xl font-bold">WireGuard VPN 管理系统</h1>
<div class="flex items-center space-x-4">
<span id="clock" class="text-sm"></span>
<span class="text-sm">谢北然 | 42006606</span>
</div>
</div>
</div>
</nav>
<!-- 主内容区 -->
<div class="container mx-auto px-4 py-8">
<!-- 左侧按钮 -->
<div class="fixed left-4 top-1/2 transform -translate-y-1/2 z-10">
<a href="https://www.retourne-toi.org" target="_blank"
class="block bg-purple-600 hover:bg-purple-700 text-white px-4 py-3 rounded-lg shadow-lg transition-all">
📄 技术文档
</a>
</div>
<!-- 右侧按钮 -->
<div class="fixed right-4 top-1/2 transform -translate-y-1/2 z-10">
<a href="https://www.retourne-toi.org" target="_blank"
class="block bg-green-600 hover:bg-green-700 text-white px-4 py-3 rounded-lg shadow-lg transition-all">
🎓 答辩资料
</a>
</div>
<!-- 状态卡片区 -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
<!-- VPN状态卡片 -->
<div class="bg-white rounded-lg shadow-lg p-6">
<div class="flex items-center justify-between mb-4">
<h3 class="text-gray-600 text-sm font-medium">VPN 状态</h3>
<span class="status-indicator" id="status-indicator"></span>
</div>
<div class="text-3xl font-bold" id="vpn-status">加载中...</div>
</div>
<!-- 在线用户卡片 -->
<div class="bg-white rounded-lg shadow-lg p-6">
<div class="flex items-center justify-between mb-4">
<h3 class="text-gray-600 text-sm font-medium">在线用户</h3>
<svg class="w-6 h-6 text-blue-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z" />
</svg>
</div>
<div class="text-3xl font-bold" id="peer-count">0</div>
</div>
<!-- 延迟卡片 -->
<div class="bg-white rounded-lg shadow-lg p-6">
<div class="flex items-center justify-between mb-4">
<h3 class="text-gray-600 text-sm font-medium">平均延迟</h3>
<svg class="w-6 h-6 text-green-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M13 10V3L4 14h7v7l9-11h-7z" />
</svg>
</div>
<div class="text-3xl font-bold" id="latency">-- ms</div>
</div>
<!-- 带宽卡片 -->
<div class="bg-white rounded-lg shadow-lg p-6">
<div class="flex items-center justify-between mb-4">
<h3 class="text-gray-600 text-sm font-medium">传输速率</h3>
<svg class="w-6 h-6 text-yellow-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M9 19l3 3m0 0l3-3m-3 3V10" />
</svg>
</div>
<div class="text-3xl font-bold" id="bandwidth">-- Mbps</div>
</div>
</div>
<!-- 控制面板 -->
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-8">
<!-- 控制按钮区 -->
<div class="bg-white rounded-lg shadow-lg p-6">
<h3 class="text-xl font-bold mb-6">服务控制</h3>
<div class="space-y-4">
<button onclick="startVPN()"
class="w-full bg-green-600 hover:bg-green-700 text-white font-bold py-3 px-6 rounded-lg transition-all flex items-center justify-center">
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
启动 VPN
</button>
<button onclick="stopVPN()"
class="w-full bg-red-600 hover:bg-red-700 text-white font-bold py-3 px-6 rounded-lg transition-all flex items-center justify-center">
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M9 10a1 1 0 011-1h4a1 1 0 011 1v4a1 1 0 01-1 1h-4a1 1 0 01-1-1v-4z" />
</svg>
停止 VPN
</button>
<button onclick="runFullTest()"
class="w-full bg-blue-600 hover:bg-blue-700 text-white font-bold py-3 px-6 rounded-lg transition-all flex items-center justify-center">
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
</svg>
运行性能测试
</button>
</div>
</div>
<!-- 系统信息区 -->
<div class="bg-white rounded-lg shadow-lg p-6">
<h3 class="text-xl font-bold mb-6">系统信息</h3>
<div class="space-y-3">
<div class="flex justify-between items-center py-2 border-b">
<span class="text-gray-600">主机名</span>
<span class="font-medium" id="sys-hostname">--</span>
</div>
<div class="flex justify-between items-center py-2 border-b">
<span class="text-gray-600">内核版本</span>
<span class="font-medium" id="sys-kernel">--</span>
</div>
<div class="flex justify-between items-center py-2 border-b">
<span class="text-gray-600">运行时间</span>
<span class="font-medium" id="sys-uptime">--</span>
</div>
<div class="flex justify-between items-center py-2">
<span class="text-gray-600">负载</span>
<span class="font-medium" id="sys-load">--</span>
</div>
</div>
</div>
</div>
<!-- 日志输出区 -->
<div class="bg-white rounded-lg shadow-lg p-6">
<div class="flex justify-between items-center mb-4">
<h3 class="text-xl font-bold">系统日志</h3>
<button onclick="clearLogs()"
class="bg-gray-500 hover:bg-gray-600 text-white px-4 py-2 rounded text-sm">
清空日志
</button>
</div>
<div id="log-output"
class="bg-gray-900 text-green-400 p-4 rounded font-mono text-sm h-64 overflow-y-auto">
<div class="text-gray-500">等待操作...</div>
</div>
</div>
</div>
<!-- 页脚 -->
<footer class="bg-gray-800 text-white mt-12 py-6">
<div class="container mx-auto px-4 text-center">
<p>基于WireGuard的VPN安全远程接入系统 | 西南财经大学天府学院</p>
<p class="text-sm text-gray-400 mt-2">作者:谢北然 (42006606) | 指导教师:裴剑辉</p>
</div>
</footer>
<!-- JavaScript代码 -->
<script>
// API基础URL
const API_BASE = 'http://192.168.1.66:5000';
// 更新时钟
function updateClock() {
const now = new Date();
const timeString = now.toLocaleString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
});
document.getElementById('clock').textContent = timeString;
}
setInterval(updateClock, 1000);
updateClock();
// 添加日志
function addLog(message, type = 'info') {
const logOutput = document.getElementById('log-output');
const timestamp = new Date().toLocaleTimeString('zh-CN');
const colors = {
'info': 'text-green-400',
'error': 'text-red-400',
'warning': 'text-yellow-400',
'success': 'text-blue-400'
};
const color = colors[type] || colors['info'];
logOutput.innerHTML += `<div class="${color}">[${timestamp}] ${message}</div>`;
logOutput.scrollTop = logOutput.scrollHeight;
}
// 清空日志
function clearLogs() {
document.getElementById('log-output').innerHTML = '<div class="text-gray-500">日志已清空</div>';
}
// 获取VPN状态
async function getStatus() {
try {
const response = await fetch(`${API_BASE}/api/vpn/status`);
const data = await response.json();
if (data.status === 'running') {
document.getElementById('vpn-status').textContent = '运行中';
document.getElementById('status-indicator').className = 'status-indicator status-running';
document.getElementById('peer-count').textContent = data.peers;
} else {
document.getElementById('vpn-status').textContent = '已停止';
document.getElementById('status-indicator').className = 'status-indicator status-stopped';
document.getElementById('peer-count').textContent = '0';
}
} catch (error) {
console.error('Error fetching status:', error);
document.getElementById('vpn-status').textContent = '错误';
}
}
// 启动VPN
async function startVPN() {
addLog('正在启动VPN服务...', 'info');
try {
const response = await fetch(`${API_BASE}/api/vpn/start`, {
method: 'POST'
});
const data = await response.json();
if (data.success) {
addLog('VPN启动成功!', 'success');
getStatus();
} else {
addLog(`VPN启动失败: ${data.message}`, 'error');
}
} catch (error) {
addLog(`错误: ${error.message}`, 'error');
}
}
// 停止VPN
async function stopVPN() {
if (!confirm('确定要停止VPN服务吗?')) return;
addLog('正在停止VPN服务...', 'info');
try {
const response = await fetch(`${API_BASE}/api/vpn/stop`, {
method: 'POST'
});
const data = await response.json();
if (data.success) {
addLog('VPN已停止', 'warning');
getStatus();
} else {
addLog(`VPN停止失败: ${data.message}`, 'error');
}
} catch (error) {
addLog(`错误: ${error.message}`, 'error');
}
}
// 延迟测试
async function testLatency() {
addLog('正在进行延迟测试...', 'info');
try {
const response = await fetch(`${API_BASE}/api/vpn/test/latency`);
const data = await response.json();
if (data.success) {
document.getElementById('latency').textContent = `${data.rtt_avg} ms`;
addLog(`延迟测试完成: 平均 ${data.rtt_avg}ms, 最小 ${data.rtt_min}ms, 最大 ${data.rtt_max}ms, 丢包率 ${data.packet_loss}%`, 'success');
} else {
addLog(`延迟测试失败: ${data.message}`, 'error');
}
} catch (error) {
addLog(`错误: ${error.message}`, 'error');
}
}
// 带宽测试
async function testBandwidth() {
addLog('正在进行带宽测试...', 'info');
try {
const response = await fetch(`${API_BASE}/api/vpn/test/bandwidth`);
const data = await response.json();
if (data.success) {
document.getElementById('bandwidth').textContent = `↓${data.rx_mbps} ↑${data.tx_mbps}`;
addLog(`带宽测试完成: 下行 ${data.rx_mbps} Mbps, 上行 ${data.tx_mbps} Mbps`, 'success');
} else {
addLog(`带宽测试失败: ${data.message}`, 'error');
}
} catch (error) {
addLog(`错误: ${error.message}`, 'error');
}
}
// 完整性能测试
async function runFullTest() {
addLog('========== 开始完整性能测试 ==========', 'info');
await testLatency();
await new Promise(resolve => setTimeout(resolve, 1000));
await testBandwidth();
addLog('========== 性能测试完成 ==========', 'info');
}
// 获取系统信息
async function getSystemInfo() {
try {
const response = await fetch(`${API_BASE}/api/system/info`);
const data = await response.json();
document.getElementById('sys-hostname').textContent = data.hostname;
document.getElementById('sys-kernel').textContent = data.kernel;
document.getElementById('sys-uptime').textContent = data.uptime;
document.getElementById('sys-load').textContent = data.load_average;
} catch (error) {
console.error('Error fetching system info:', error);
}
}
// 初始化
window.onload = function() {
addLog('系统初始化...', 'info');
getStatus();
getSystemInfo();
// 定时刷新状态(每5秒)
setInterval(() => {
getStatus();
getSystemInfo();
}, 5000);
addLog('系统就绪,等待操作...', 'success');
};
</script>
</body>
</html>
保存文件(Ctrl+X, Y, Enter)。
13.2 HTTP服务启动
13.2.1 使用Python内置HTTP服务器
Python http.server模块:
Python 3自带的http.server模块可以快速启动一个简单的HTTP服务器,用于提供静态文件服务。
基本用法:
# 在指定目录启动HTTP服务器
cd /home/vpn-demo
python3 -m http.server 8000
预期输出:
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
指定监听地址:
# 仅监听本地(安全)
python3 -m http.server 8000 --bind 127.0.0.1
# 监听所有接口(本项目使用)
python3 -m http.server 8000 --bind 0.0.0.0
# 指定目录
python3 -m http.server 8000 --directory /home/vpn-demo
后台运行:
# 使用nohup后台运行
nohup python3 -m http.server 8000 > /var/log/vpn-demo/http.log 2>&1 &
# 查看进程
ps aux | grep "http.server"
# 停止服务
pkill -f "http.server"
13.2.2 处理端口占用问题
检查端口占用:
# 方法1:netstat
sudo netstat -tulnp | grep :8000
# 方法2:lsof
sudo lsof -i :8000
# 方法3:ss
sudo ss -tulnp | grep :8000
输出示例(端口被占用):
tcp 0 0 0.0.0.0:8000 0.0.0.0:* LISTEN 1234/python3
解决端口占用:
# 方法1:找到PID并杀死进程
sudo lsof -i :8000
# 输出:python3 1234 root 3u IPv4 12345 0t0 TCP *:8000 (LISTEN)
sudo kill -9 1234
# 方法2:使用fuser直接杀死
sudo fuser -k 8000/tcp
# 方法3:使用pkill
sudo pkill -f "python3 -m http.server"
验证端口已释放:
# 再次检查,应该没有输出
sudo lsof -i :8000
13.2.3 配置防火墙
开放8000端口:
# 方法1:ufw防火墙
sudo ufw allow 8000/tcp
# 方法2:仅允许内网访问
sudo ufw allow from 192.168.1.0/24 to any port 8000
# 方法3:iptables
sudo iptables -A INPUT -p tcp --dport 8000 -j ACCEPT
限制访问源:
# 仅允许指定IP访问
sudo ufw allow from 192.168.1.100 to any port 8000
# 允许IP段访问
sudo ufw allow from 192.168.1.0/24 to any port 8000
# 拒绝其他所有访问
sudo ufw deny 8000/tcp
查看防火墙规则:
# ufw规则
sudo ufw status numbered
# iptables规则
sudo iptables -L INPUT -n --line-numbers | grep 8000
13.2.4 测试HTTP服务
本地测试:
# 使用curl测试
curl http://localhost:8000
# 查看HTTP头
curl -I http://localhost:8000
# 下载index.html
curl -O http://localhost:8000/index.html
远程测试:
# 从另一台机器测试
curl http://192.168.1.66:8000
# Windows PowerShell测试
Invoke-WebRequest -Uri http://192.168.1.66:8000
浏览器测试:
打开浏览器,访问:
http://192.168.1.66:8000
预期结果:
显示VPN管理系统界面
13.2.5 HTTP服务日志
查看访问日志:
# http.server输出到终端
# 示例:
192.168.1.100 - - [23/Mar/2025 10:30:15] "GET / HTTP/1.1" 200 -
192.168.1.100 - - [23/Mar/2025 10:30:16] "GET /index.html HTTP/1.1" 200 -
重定向日志到文件:
# 启动时指定日志文件
python3 -m http.server 8000 > /var/log/vpn-demo/http.log 2>&1 &
# 实时查看日志
tail -f /var/log/vpn-demo/http.log
日志格式说明:
192.168.1.100 # 客户端IP
- # 远程用户(通常为-)
- # 认证用户(通常为-)
[23/Mar/2025 10:30:15] # 时间戳
"GET / HTTP/1.1" # 请求方法和路径
200 # HTTP状态码
- # 响应大小(字节)
13.3 访问地址配置
13.3.1 服务访问地址
完整的访问地址清单:
| 服务 | 地址 | 端口 | 协议 | 用途 |
|---|---|---|---|---|
| Web管理界面 | http://192.168.1.66:8000 | 8000 | HTTP | 前端控制面板 |
| Flask API | http://192.168.1.66:5000 | 5000 | HTTP | 后端API |
| WireGuard VPN | 192.168.1.66:51820 | 51820 | UDP | VPN隧道 |
| 内网演示服务 | http://10.8.0.1:8000 | 8000 | HTTP | 通过VPN访问 |
13.3.2 主机名配置(可选)
配置域名解析(本地):
如果不想记IP地址,可以配置本地hosts:
Linux/macOS:
# 编辑hosts文件
sudo nano /etc/hosts
# 添加以下行
192.168.1.66 vpn.local
192.168.1.66 vpn-api.local
# 保存后测试
ping vpn.local
curl http://vpn.local:8000
Windows:
# 以管理员身份运行记事本
notepad C:\Windows\System32\drivers\etc\hosts
# 添加以下行
192.168.1.66 vpn.local
192.168.1.66 vpn-api.local
# 保存后测试
ping vpn.local
访问地址变更为:
Web界面:http://vpn.local:8000
API:http://vpn-api.local:5000
13.3.3 CORS配置(跨域访问)
如果前端和后端部署在不同端口或域名,需要配置CORS。
安装flask-cors:
sudo pip3 install flask-cors
修改vpn_api.py:
from flask import Flask
from flask_cors import CORS
app = Flask(__name__)
# 允许所有来源(开发环境)
CORS(app)
# 或限制特定来源(生产环境)
# CORS(app, resources={r"/api/*": {"origins": "http://192.168.1.66:8000"}})
测试CORS:
# 发送跨域请求
curl -H "Origin: http://example.com" \
-H "Access-Control-Request-Method: POST" \
-X OPTIONS http://192.168.1.66:5000/api/vpn/start
13.3.4 HTTPS配置(可选)
使用Nginx反向代理+Let’s Encrypt:
安装Nginx:
sudo apt install nginx
配置反向代理:
sudo nano /etc/nginx/sites-available/vpn-mgmt
配置内容:
server {
listen 80;
server_name vpn.example.com;
# 前端静态文件
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
# API接口
location /api/ {
proxy_pass http://127.0.0.1:5000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
启用配置:
sudo ln -s /etc/nginx/sites-available/vpn-mgmt /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl restart nginx
安装SSL证书(Let’s Encrypt):
# 安装certbot
sudo apt install certbot python3-certbot-nginx
# 获取证书
sudo certbot --nginx -d vpn.example.com
# 自动续期
sudo certbot renew --dry-run
访问地址变更为:
HTTPS:https://vpn.example.com
💡 提示:本项目为内网环境,使用HTTP即可;公网环境建议配置HTTPS。
13.3.5 移动端访问
同一局域网访问:
确保移动设备连接到同一WiFi网络(192.168.1.0/24),然后访问:
http://192.168.1.66:8000
通过VPN访问:
移动设备连接VPN后,可以使用内网地址:
http://10.8.0.1:8000
二维码快速访问:
生成二维码方便移动设备扫描:
# 安装qrencode
sudo apt install qrencode
# 生成二维码(终端显示)
qrencode -t ANSI "http://192.168.1.66:8000"
# 生成二维码图片
qrencode -o vpn-qrcode.png "http://192.168.1.66:8000"
14. 统一启动脚本
14.1 start-api.sh脚本内容
14.1.1 完整启动脚本
创建启动脚本:
# 进入项目目录
cd /home/vpn-demo
# 创建脚本文件
sudo nano start-api.sh
完整脚本内容:
#!/bin/bash
#######################################
# WireGuard VPN 管理系统启动脚本
#
# 功能:
# 1. 停止旧进程
# 2. 启动Flask API后端
# 3. 启动HTTP前端服务
# 4. 显示服务状态
#
# 作者:谢北然 (42006606)
# 日期:2025-03-23
#######################################
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# 配置变量
PROJECT_DIR="/home/vpn-demo"
API_PORT=5000
WEB_PORT=8000
LOG_DIR="/var/log/vpn-demo"
API_LOG="${LOG_DIR}/api.log"
WEB_LOG="${LOG_DIR}/web.log"
# 打印带颜色的消息
print_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
print_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
print_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# 打印分隔线
print_separator() {
echo "=========================================="
}
# 检查是否以root运行
check_root() {
if [ "$EUID" -ne 0 ]; then
print_error "请使用root权限运行此脚本"
echo "使用方法: sudo $0"
exit 1
fi
}
# 创建日志目录
create_log_dir() {
if [ ! -d "$LOG_DIR" ]; then
print_info "创建日志目录: $LOG_DIR"
mkdir -p "$LOG_DIR"
fi
}
# 停止旧进程
stop_old_processes() {
print_info "正在停止旧服务..."
# 停止Flask API
API_PID=$(pgrep -f "vpn_api.py")
if [ -n "$API_PID" ]; then
print_warning "发现Flask API进程 (PID: $API_PID),正在停止..."
pkill -f vpn_api.py
sleep 1
fi
# 停止HTTP服务器
WEB_PID=$(pgrep -f "python3 -m http.server.*${WEB_PORT}")
if [ -n "$WEB_PID" ]; then
print_warning "发现HTTP服务进程 (PID: $WEB_PID),正在停止..."
pkill -f "python3 -m http.server.*${WEB_PORT}"
sleep 1
fi
# 强制释放端口
fuser -k ${API_PORT}/tcp 2>/dev/null
fuser -k ${WEB_PORT}/tcp 2>/dev/null
print_success "旧服务已停止"
}
# 检查端口占用
check_port() {
local port=$1
local service=$2
if lsof -Pi :${port} -sTCP:LISTEN -t >/dev/null 2>&1 ; then
print_error "${service}端口 ${port} 仍被占用"
print_info "占用进程信息:"
lsof -i :${port}
return 1
else
print_success "${service}端口 ${port} 可用"
return 0
fi
}
# 启动Flask API
start_flask_api() {
print_info "正在启动Flask API (端口: ${API_PORT})..."
cd "$PROJECT_DIR"
# 后台启动Flask
nohup python3 vpn_api.py > "$API_LOG" 2>&1 &
# 等待服务启动
sleep 3
# 检查是否启动成功
if pgrep -f "vpn_api.py" > /dev/null; then
API_PID=$(pgrep -f "vpn_api.py")
print_success "Flask API 启动成功 (PID: ${API_PID})"
# 测试API健康检查
if curl -s http://localhost:${API_PORT}/health > /dev/null 2>&1; then
print_success "API健康检查通过"
else
print_warning "API健康检查失败,请检查日志"
fi
else
print_error "Flask API 启动失败"
print_info "查看日志: tail -f $API_LOG"
return 1
fi
}
# 启动HTTP服务器
start_http_server() {
print_info "正在启动HTTP服务器 (端口: ${WEB_PORT})..."
cd "$PROJECT_DIR"
# 后台启动HTTP服务器
nohup python3 -m http.server ${WEB_PORT} > "$WEB_LOG" 2>&1 &
# 等待服务启动
sleep 2
# 检查是否启动成功
if pgrep -f "python3 -m http.server.*${WEB_PORT}" > /dev/null; then
WEB_PID=$(pgrep -f "python3 -m http.server.*${WEB_PORT}")
print_success "HTTP服务器启动成功 (PID: ${WEB_PID})"
# 测试HTTP访问
if curl -s http://localhost:${WEB_PORT} > /dev/null 2>&1; then
print_success "HTTP服务测试通过"
else
print_warning "HTTP服务测试失败,请检查日志"
fi
else
print_error "HTTP服务器启动失败"
print_info "查看日志: tail -f $WEB_LOG"
return 1
fi
}
# 显示服务状态
show_status() {
print_separator
print_info "服务状态:"
print_separator
# Flask API状态
if pgrep -f "vpn_api.py" > /dev/null; then
API_PID=$(pgrep -f "vpn_api.py")
print_success "Flask API: 运行中 (PID: ${API_PID})"
else
print_error "Flask API: 已停止"
fi
# HTTP服务器状态
if pgrep -f "python3 -m http.server.*${WEB_PORT}" > /dev/null; then
WEB_PID=$(pgrep -f "python3 -m http.server.*${WEB_PORT}")
print_success "HTTP服务器: 运行中 (PID: ${WEB_PID})"
else
print_error "HTTP服务器: 已停止"
fi
print_separator
}
# 显示访问信息
show_access_info() {
# 获取本机IP
LOCAL_IP=$(hostname -I | awk '{print $1}')
print_separator
print_info "访问地址:"
print_separator
echo ""
echo -e " ${GREEN}Web管理界面:${NC}"
echo -e " http://localhost:${WEB_PORT}"
echo -e " http://${LOCAL_IP}:${WEB_PORT}"
echo ""
echo -e " ${GREEN}API接口:${NC}"
echo -e " http://localhost:${API_PORT}"
echo -e " http://${LOCAL_IP}:${API_PORT}"
echo ""
echo -e " ${GREEN}API文档:${NC}"
echo -e " 健康检查: http://${LOCAL_IP}:${API_PORT}/health"
echo -e " VPN状态: http://${LOCAL_IP}:${API_PORT}/api/vpn/status"
echo ""
print_separator
print_info "日志文件:"
print_separator
echo " API日志: tail -f $API_LOG"
echo " Web日志: tail -f $WEB_LOG"
echo ""
print_separator
print_info "停止服务:"
print_separator
echo " 停止所有: sudo pkill -f vpn_api.py && sudo pkill -f 'python3 -m http.server'"
echo " 停止API: sudo pkill -f vpn_api.py"
echo " 停止Web: sudo pkill -f 'python3 -m http.server'"
echo ""
print_separator
}
# 主函数
main() {
clear
print_separator
echo -e "${BLUE}WireGuard VPN 管理系统启动脚本${NC}"
print_separator
echo ""
# 检查root权限
check_root
# 创建日志目录
create_log_dir
# 停止旧进程
stop_old_processes
# 等待端口释放
sleep 2
# 检查端口
check_port $API_PORT "Flask API" || exit 1
check_port $WEB_PORT "HTTP服务器" || exit 1
echo ""
# 启动服务
start_flask_api || exit 1
echo ""
start_http_server || exit 1
echo ""
# 显示状态
show_status
# 显示访问信息
show_access_info
print_success "所有服务启动完成!"
echo ""
}
# 执行主函数
main
保存文件(Ctrl+X, Y, Enter)。
14.1.2 脚本功能说明
脚本主要功能:
-
环境检查
- 检查root权限
- 创建日志目录
- 检查端口占用
-
停止旧服务
- 查找并停止Flask API进程
- 查找并停止HTTP服务器进程
- 强制释放端口
-
启动新服务
- 后台启动Flask API
- 后台启动HTTP服务器
- 验证服务健康状态
-
状态显示
- 显示进程PID
- 显示访问地址
- 显示日志路径
- 显示停止命令
脚本特点:
- ✅ 彩色输出,易于阅读
- ✅ 完善的错误处理
- ✅ 自动检测端口冲突
- ✅ 健康检查验证
- ✅ 详细的日志记录
14.2 脚本权限设置
14.2.1 设置可执行权限
添加执行权限:
# 设置所有者为root
sudo chown root:root /home/vpn-demo/start-api.sh
# 添加执行权限
sudo chmod +x /home/vpn-demo/start-api.sh
# 验证权限
ls -l /home/vpn-demo/start-api.sh
预期输出:
-rwxr-xr-x 1 root root 8192 Mar 23 10:00 /home/vpn-demo/start-api.sh
权限说明:
rwx(所有者):读、写、执行r-x(用户组):读、执行r-x(其他人):读、执行
14.2.2 创建符号链接(可选)
方便全局调用:
# 创建符号链接到/usr/local/bin
sudo ln -s /home/vpn-demo/start-api.sh /usr/local/bin/vpn-start
# 验证链接
ls -l /usr/local/bin/vpn-start
# 现在可以直接运行
sudo vpn-start
14.2.3 设置别名(可选)
添加bash别名:
# 编辑.bashrc
nano ~/.bashrc
# 添加别名
alias vpn-start='sudo /home/vpn-demo/start-api.sh'
alias vpn-stop='sudo pkill -f vpn_api.py && sudo pkill -f "python3 -m http.server"'
alias vpn-status='sudo systemctl status vpn-api'
alias vpn-logs='sudo tail -f /var/log/vpn-demo/api.log'
# 重新加载配置
source ~/.bashrc
# 使用别名
vpn-start
14.3 启动方法
14.3.1 手动启动
基本启动:
# 进入目录
cd /home/vpn-demo
# 以root权限执行
sudo ./start-api.sh
从任意位置启动:
# 使用绝对路径
sudo /home/vpn-demo/start-api.sh
# 或使用符号链接
sudo vpn-start
# 或使用别名
vpn-start
14.3.2 启动输出示例
==========================================
WireGuard VPN 管理系统启动脚本
==========================================
[INFO] 正在停止旧服务...
[SUCCESS] 旧服务已停止
[SUCCESS] Flask API端口 5000 可用
[SUCCESS] HTTP服务器端口 8000 可用
[INFO] 正在启动Flask API (端口: 5000)...
[SUCCESS] Flask API 启动成功 (PID: 1234)
[SUCCESS] API健康检查通过
[INFO] 正在启动HTTP服务器 (端口: 8000)...
[SUCCESS] HTTP服务器启动成功 (PID: 5678)
[SUCCESS] HTTP服务测试通过
==========================================
[INFO] 服务状态:
==========================================
[SUCCESS] Flask API: 运行中 (PID: 1234)
[SUCCESS] HTTP服务器: 运行中 (PID: 5678)
==========================================
==========================================
[INFO] 访问地址:
==========================================
Web管理界面:
http://localhost:8000
http://192.168.1.66:8000
API接口:
http://localhost:5000
http://192.168.1.66:5000
API文档:
健康检查: http://192.168.1.66:5000/health
VPN状态: http://192.168.1.66:5000/api/vpn/status
==========================================
[INFO] 日志文件:
==========================================
API日志: tail -f /var/log/vpn-demo/api.log
Web日志: tail -f /var/log/vpn-demo/web.log
==========================================
[INFO] 停止服务:
==========================================
停止所有: sudo pkill -f vpn_api.py && sudo pkill -f 'python3 -m http.server'
停止API: sudo pkill -f vpn_api.py
停止Web: sudo pkill -f 'python3 -m http.server'
==========================================
[SUCCESS] 所有服务启动完成!
14.3.3 开机自启动
方法1:使用crontab
# 编辑root的crontab
sudo crontab -e
# 添加以下行(开机时执行)
@reboot /home/vpn-demo/start-api.sh
# 保存退出后查看
sudo crontab -l
方法2:使用rc.local(传统方法)
# 编辑rc.local
sudo nano /etc/rc.local
# 在exit 0之前添加
/home/vpn-demo/start-api.sh
# 确保rc.local可执行
sudo chmod +x /etc/rc.local
# 启用rc-local服务
sudo systemctl enable rc-local
方法3:使用systemd(最推荐)
创建systemd服务文件:
sudo nano /etc/systemd/system/vpn-mgmt.service
服务配置:
[Unit]
Description=VPN Management System (API + Web)
After=network.target [email protected]
Wants[email protected]
[Service]
Type=forking
ExecStart=/home/vpn-demo/start-api.sh
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.target
启用服务:
# 重新加载systemd
sudo systemctl daemon-reload
# 启用开机自启
sudo systemctl enable vpn-mgmt
# 立即启动
sudo systemctl start vpn-mgmt
# 查看状态
sudo systemctl status vpn-mgmt
14.4 停止方法
14.4.1 手动停止
停止所有服务:
# 停止Flask API
sudo pkill -f vpn_api.py
# 停止HTTP服务器
sudo pkill -f "python3 -m http.server"
# 一行命令停止所有
sudo pkill -f vpn_api.py && sudo pkill -f "python3 -m http.server"
# 强制释放端口
sudo fuser -k 5000/tcp
sudo fuser -k 8000/tcp
使用别名停止:
# 如果配置了别名
vpn-stop
验证停止:
# 检查进程
ps aux | grep vpn_api.py
ps aux | grep "http.server"
# 检查端口
sudo lsof -i :5000
sudo lsof -i :8000
# 都应该没有输出,表示服务已停止
14.4.2 使用systemd停止
如果使用systemd管理:
# 停止服务
sudo systemctl stop vpn-mgmt
# 禁用开机自启
sudo systemctl disable vpn-mgmt
# 查看状态
sudo systemctl status vpn-mgmt
14.4.3 紧急停止
强制杀死所有Python进程(危险):
# 查看所有Python进程
ps aux | grep python3
# 杀死特定PID
sudo kill -9 <PID>
# 杀死所有相关进程(谨慎使用)
sudo killall -9 python3
⚠️ 警告:killall python3会杀死所有Python进程,可能影响其他服务。
14.5 状态检查
14.5.1 检查服务运行状态
查看进程:
# 检查Flask API
ps aux | grep vpn_api.py | grep -v grep
# 检查HTTP服务器
ps aux | grep "http.server" | grep -v grep
# 检查所有相关进程
ps aux | grep -E "vpn_api|http.server" | grep -v grep
输出示例:
root 1234 0.1 0.5 50000 10000 ? S 10:00 0:05 python3 vpn_api.py
root 5678 0.0 0.3 40000 7000 ? S 10:00 0:02 python3 -m http.server 8000
14.5.2 检查端口监听
查看监听端口:
# 方法1:netstat
sudo netstat -tulnp | grep -E "5000|8000"
# 方法2:ss
sudo ss -tulnp | grep -E "5000|8000"
# 方法3:lsof
sudo lsof -i :5000
sudo lsof -i :8000
输出示例:
tcp 0 0 0.0.0.0:5000 0.0.0.0:* LISTEN 1234/python3
tcp 0 0 0.0.0.0:8000 0.0.0.0:* LISTEN 5678/python3
14.5.3 API健康检查
测试API接口:
# 健康检查
curl http://localhost:5000/health
# 预期输出
# {"status":"healthy","service":"VPN Management API","version":"1.0"}
# VPN状态查询
curl http://localhost:5000/api/vpn/status
# 系统信息
curl http://localhost:5000/api/system/info
14.5.4 Web服务检查
测试Web界面:
# 访问首页
curl -I http://localhost:8000
# 预期输出
# HTTP/1.0 200 OK
# Server: SimpleHTTP/0.6 Python/3.10.12
# Date: ...
# Content-type: text/html
# 下载index.html
curl -s http://localhost:8000/index.html | head -n 5
14.5.5 查看实时日志
查看API日志:
# 实时查看
tail -f /var/log/vpn-demo/api.log
# 查看最近100行
tail -n 100 /var/log/vpn-demo/api.log
# 查看错误日志
grep -i error /var/log/vpn-demo/api.log
查看Web日志:
# 实时查看
tail -f /var/log/vpn-demo/web.log
# 查看访问记录
tail -n 50 /var/log/vpn-demo/web.log | grep "GET"
systemd日志(如果使用systemd):
# 查看服务日志
sudo journalctl -u vpn-mgmt -f
# 查看最近100行
sudo journalctl -u vpn-mgmt -n 100
# 查看错误日志
sudo journalctl -u vpn-mgmt -p err
# 查看今天的日志
sudo journalctl -u vpn-mgmt --since today
14.5.6 性能监控
监控资源使用:
# CPU和内存使用
ps -p $(pgrep -f vpn_api.py) -o %cpu,%mem,cmd
# 使用top监控
top -p $(pgrep -f vpn_api.py)
# 使用htop(更友好)
htop -p $(pgrep -f vpn_api.py),$(pgrep -f "http.server")
网络连接统计:
# 查看连接数
sudo netstat -an | grep :5000 | grep ESTABLISHED | wc -l
sudo netstat -an | grep :8000 | grep ESTABLISHED | wc -l
# 查看详细连接
sudo netstat -anp | grep -E "5000|8000"