├── .gitignore
├── server
├── data
│ ├── servers.json
│ ├── users.json
│ └── rules.json
├── .DS_Store
├── public
│ ├── fonts
│ │ ├── element-icons.f1a45d74.ttf
│ │ └── element-icons.ff18efd1.woff
│ ├── index.html
│ └── css
│ │ └── app.6e665435.css
├── nodemon.json
├── .env.example
├── config.json
├── routes
│ ├── authRoutes.js
│ ├── serverRoutes.js
│ └── rulesRoutes.js
├── package.json
├── start.sh
├── middlewares
│ └── authMiddleware.js
├── scripts
│ └── createAdmin.js
├── models
│ ├── User.js
│ ├── Server.js
│ └── Rule.js
├── controllers
│ ├── authController.js
│ ├── rulesController.js
│ └── serverController.js
├── services
│ └── cacheService.js
└── app.js
├── client
├── .npmrc
├── .DS_Store
├── babel.config.js
├── src
│ ├── views
│ │ ├── extensions
│ │ │ └── RulesForward.js
│ │ ├── Home.vue
│ │ ├── Profile.vue
│ │ ├── Login.vue
│ │ ├── Register.vue
│ │ └── Rules.vue
│ ├── store
│ │ ├── index.js
│ │ └── modules
│ │ │ ├── auth.js
│ │ │ ├── servers.js
│ │ │ └── rules.js
│ ├── main.js
│ ├── router
│ │ └── index.js
│ ├── App.vue
│ └── components
│ │ ├── ChangePasswordForm.vue
│ │ └── ServerForm.vue
├── public
│ └── index.html
├── vue.config.js
└── package.json
├── readme
├── index.png
├── login.png
├── rules.png
├── addssh.png
├── servers.png
└── firsttime.png
├── deployimg
├── 001.png
├── 002.png
├── 003.png
├── 004.png
└── 005.png
├── .env.example
├── USE.md
├── .env
├── start-all.sh
├── package.json
├── Clawcloud.md
├── Dockerfile
├── Localdo.md
├── intcentos.sh
├── Dockerdo.md
├── projectStructure.txt
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
3 | data
--------------------------------------------------------------------------------
/server/data/servers.json:
--------------------------------------------------------------------------------
1 | {
2 | "servers": []
3 | }
--------------------------------------------------------------------------------
/client/.npmrc:
--------------------------------------------------------------------------------
1 | legacy-peer-deps=true
2 | fund=false
3 | audit=false
--------------------------------------------------------------------------------
/client/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Fiftonb/Gnftato/HEAD/client/.DS_Store
--------------------------------------------------------------------------------
/readme/index.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Fiftonb/Gnftato/HEAD/readme/index.png
--------------------------------------------------------------------------------
/readme/login.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Fiftonb/Gnftato/HEAD/readme/login.png
--------------------------------------------------------------------------------
/readme/rules.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Fiftonb/Gnftato/HEAD/readme/rules.png
--------------------------------------------------------------------------------
/server/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Fiftonb/Gnftato/HEAD/server/.DS_Store
--------------------------------------------------------------------------------
/deployimg/001.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Fiftonb/Gnftato/HEAD/deployimg/001.png
--------------------------------------------------------------------------------
/deployimg/002.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Fiftonb/Gnftato/HEAD/deployimg/002.png
--------------------------------------------------------------------------------
/deployimg/003.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Fiftonb/Gnftato/HEAD/deployimg/003.png
--------------------------------------------------------------------------------
/deployimg/004.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Fiftonb/Gnftato/HEAD/deployimg/004.png
--------------------------------------------------------------------------------
/deployimg/005.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Fiftonb/Gnftato/HEAD/deployimg/005.png
--------------------------------------------------------------------------------
/readme/addssh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Fiftonb/Gnftato/HEAD/readme/addssh.png
--------------------------------------------------------------------------------
/readme/servers.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Fiftonb/Gnftato/HEAD/readme/servers.png
--------------------------------------------------------------------------------
/readme/firsttime.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Fiftonb/Gnftato/HEAD/readme/firsttime.png
--------------------------------------------------------------------------------
/client/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | '@vue/cli-plugin-babel/preset'
4 | ],
5 | plugins: []
6 | };
--------------------------------------------------------------------------------
/server/public/fonts/element-icons.f1a45d74.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Fiftonb/Gnftato/HEAD/server/public/fonts/element-icons.f1a45d74.ttf
--------------------------------------------------------------------------------
/server/public/fonts/element-icons.ff18efd1.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Fiftonb/Gnftato/HEAD/server/public/fonts/element-icons.ff18efd1.woff
--------------------------------------------------------------------------------
/client/src/views/extensions/RulesForward.js:
--------------------------------------------------------------------------------
1 | // RulesForward.js - 端口转发功能相关扩展
2 |
3 | export default {
4 | methods: {
5 | // 这里放置所有与转发功能相关的方法
6 | }
7 | };
--------------------------------------------------------------------------------
/client/src/store/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import Vuex from 'vuex';
3 | import servers from './modules/servers';
4 | import rules from './modules/rules';
5 | import auth from './modules/auth';
6 |
7 | Vue.use(Vuex);
8 |
9 | export default new Vuex.Store({
10 | modules: {
11 | servers,
12 | rules,
13 | auth
14 | }
15 | });
--------------------------------------------------------------------------------
/server/data/users.json:
--------------------------------------------------------------------------------
1 | {
2 | "users": [
3 | {
4 | "id": "1745080953892",
5 | "username": "admin",
6 | "password": "$2b$10$ImfljVlZ2RGq5lmHC9a0KuZaeXv1AFgTTGKNCYhS0sZJUUvPMHaSG",
7 | "isAdmin": true,
8 | "createdAt": "2025-04-19T16:42:33.892Z",
9 | "updatedAt": "2025-04-19T17:42:36.935Z"
10 | }
11 | ]
12 | }
--------------------------------------------------------------------------------
/server/nodemon.json:
--------------------------------------------------------------------------------
1 | {
2 | "watch": ["**/*.js", "**/*.json"],
3 | "ignore": [
4 | "node_modules",
5 | "logs/*",
6 | "*.log",
7 | "tmp/*",
8 | ".git",
9 | "public/*",
10 | "sessions/*",
11 | ".ssh/*",
12 | "**/.DS_Store",
13 | "**/.tmp.*"
14 | ],
15 | "verbose": true,
16 | "ext": "js,json",
17 | "delay": "1000"
18 | }
--------------------------------------------------------------------------------
/server/.env.example:
--------------------------------------------------------------------------------
1 | # 服务器配置
2 | PORT=3001
3 | HOST=0.0.0.0
4 | CORS_ORIGIN=*
5 |
6 | # 数据目录配置
7 | DATA_DIR=./server/data
8 |
9 | # 数据库配置
10 | DB_PATH=./data/database.json
11 |
12 | # 日志配置
13 | LOG_LEVEL=info
14 | LOG_DIR=./logs
15 |
16 | # 临时文件目录
17 | TMP_DIR=./tmp
18 |
19 | # 应用模式配置
20 | NODE_ENV=development
21 | # 设置为 true 可避免 nodemon 频繁重启
22 | STABLE_MODE=true
--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------
1 | # 服务器配置
2 | PORT=3001
3 | HOST=0.0.0.0
4 | CORS_ORIGIN=*
5 |
6 | # 数据目录配置
7 | DATA_DIR=./server/data
8 |
9 | # JWT配置
10 | JWT_SECRET=your-secret-key-change-this
11 | JWT_EXPIRES_IN=7d
12 |
13 | # 日志配置
14 | LOG_LEVEL=info
15 | LOG_DIR=./logs
16 |
17 | # 临时文件目录
18 | TMP_DIR=./tmp
19 |
20 | # 应用模式配置
21 | NODE_ENV=development
22 | # 设置为 true 可避免 nodemon 频繁重启
23 | STABLE_MODE=true
--------------------------------------------------------------------------------
/server/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "PORT": 3001,
3 | "HOST": "0.0.0.0",
4 | "CORS_ORIGIN": "*",
5 | "DATA_DIR": "./server/data",
6 | "DB_PATH": "./data/database.json",
7 | "JWT_SECRET": "iptato-secure-jwt-secret-key",
8 | "JWT_EXPIRES_IN": "7d",
9 | "LOG_LEVEL": "info",
10 | "LOG_DIR": "./logs",
11 | "TMP_DIR": "./tmp",
12 | "NODE_ENV": "development",
13 | "STABLE_MODE": "true"
14 | }
--------------------------------------------------------------------------------
/USE.md:
--------------------------------------------------------------------------------
1 | ## 使用指南
2 |
3 | 1. 访问前端界面,使用管理员账户登录系统
4 | 
5 | 2. 登录后进入首页
6 | 
7 | 3. 点击开始管理服务器进入服务器管理界面
8 | 
9 | 4. 添加服务器:点击"添加服务器",填写服务器信息并测试连接
10 | 
11 | 5. 连接服务器:在服务器列表中点击"连接"按钮
12 | 6. 管理规则:点击"管理规则"进入相应服务器的规则管理页面
13 | 7. 若首次部署,需点击开始部署
14 | 
15 | 8. 根据需要配置出入网规则
16 | 
--------------------------------------------------------------------------------
/.env:
--------------------------------------------------------------------------------
1 | # 服务器配置
2 | PORT=3001
3 | HOST=0.0.0.0
4 | CORS_ORIGIN=*
5 |
6 | # 数据目录配置
7 | DATA_DIR=./server/data
8 |
9 | # 数据库配置
10 | DB_PATH=./data/database.json
11 |
12 | # JWT配置
13 | JWT_SECRET=iptato-secure-jwt-secret-key
14 | JWT_EXPIRES_IN=7d
15 |
16 | # 日志配置
17 | LOG_LEVEL=info
18 | LOG_DIR=./logs
19 |
20 | # 临时文件目录
21 | TMP_DIR=./tmp
22 |
23 | # 应用模式配置
24 | NODE_ENV=development
25 | # 设置为 true 可避免 nodemon 频繁重启
26 | STABLE_MODE=true
--------------------------------------------------------------------------------
/client/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Nftato防火墙管理面板
9 |
10 |
11 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/server/routes/authRoutes.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const router = express.Router();
3 | const authController = require('../controllers/authController');
4 | const { protect } = require('../middlewares/authMiddleware');
5 |
6 | // 注册新用户
7 | router.post('/register', authController.register);
8 |
9 | // 用户登录
10 | router.post('/login', authController.login);
11 |
12 | // 获取当前登录用户信息 (需要身份验证)
13 | router.get('/me', protect, authController.getCurrentUser);
14 |
15 | // 更新用户密码 (需要身份验证)
16 | router.put('/update-password', protect, authController.updatePassword);
17 |
18 | module.exports = router;
--------------------------------------------------------------------------------
/server/public/index.html:
--------------------------------------------------------------------------------
1 | Nftato防火墙管理面板
--------------------------------------------------------------------------------
/start-all.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # 显示启动信息
4 | echo "正在启动 Gnftato 应用..."
5 |
6 | # 启动后端(稳定模式)
7 | echo "启动后端服务器..."
8 | cd server
9 | ./start.sh &
10 | BACKEND_PID=$!
11 | cd ..
12 |
13 | # 等待后端启动
14 | sleep 2
15 |
16 | # 检查前端目录
17 | if [ -d "client" ]; then
18 | echo "启动前端应用..."
19 | cd client
20 | npm run serve &
21 | FRONTEND_PID=$!
22 | cd ..
23 | else
24 | echo "错误: 找不到前端目录!"
25 | kill $BACKEND_PID
26 | exit 1
27 | fi
28 |
29 | echo "应用启动完成!"
30 | echo "前端运行在: http://localhost:8080"
31 | echo "后端运行在: http://localhost:3001"
32 | echo "按 Ctrl+C 停止所有服务"
33 |
34 | # 等待用户中断
35 | wait
36 |
37 | # 确保子进程被终止
38 | trap "kill $BACKEND_PID $FRONTEND_PID 2>/dev/null" EXIT
--------------------------------------------------------------------------------
/client/vue.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | devServer: {
3 | proxy: {
4 | '/api': {
5 | target: 'http://localhost:3001',
6 | changeOrigin: true,
7 | logLevel: 'debug',
8 | pathRewrite: {
9 | '^/api': '/api'
10 | },
11 | onProxyReq(proxyReq, req, res) {
12 | console.log('代理请求:', req.method, req.url, '->',
13 | proxyReq.protocol + '//' + proxyReq.host + proxyReq.path);
14 | },
15 | onError(err, req, res) {
16 | console.error('代理错误:', err);
17 | }
18 | }
19 | },
20 | host: '0.0.0.0'
21 | },
22 | outputDir: '../server/public',
23 | publicPath: '/',
24 | lintOnSave: false,
25 | transpileDependencies: true
26 | };
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "nftato-panel",
3 | "version": "1.0.0",
4 | "description": "Nftato防火墙规则多服务器管理面板",
5 | "main": "index.js",
6 | "scripts": {
7 | "install:all": "npm install && cd server && npm install && cd ../client && npm install",
8 | "start": "cd server && npm start",
9 | "dev:server": "cd server && npm run dev",
10 | "dev:client": "cd client && npm run serve",
11 | "dev": "concurrently \"npm run dev:server\" \"npm run dev:client\"",
12 | "build": "cd client && npm run build",
13 | "setup": "npm i && cd server && npm i && mkdir -p public && cd ../client && npm i --force",
14 | "test": "echo \"Error: no test specified\" && exit 1"
15 | },
16 | "author": "",
17 | "license": "MIT",
18 | "devDependencies": {
19 | "concurrently": "^6.3.0"
20 | }
21 | }
--------------------------------------------------------------------------------
/server/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Nftato-panel-server",
3 | "version": "1.0.0",
4 | "description": "Nftato防火墙规则多服务器管理面板后端",
5 | "main": "app.js",
6 | "scripts": {
7 | "start": "node app.js",
8 | "dev": "nodemon --config nodemon.json app.js",
9 | "dev:stable": "nodemon --config nodemon.json --ignore '**/*' app.js",
10 | "test": "echo \"Error: no test specified\" && exit 1",
11 | "create-admin": "node scripts/createAdmin.js"
12 | },
13 | "author": "",
14 | "license": "MIT",
15 | "dependencies": {
16 | "bcryptjs": "^2.4.3",
17 | "cors": "^2.8.5",
18 | "dotenv": "^10.0.0",
19 | "express": "^4.17.1",
20 | "jsonwebtoken": "^9.0.2",
21 | "nodemon": "^2.0.14",
22 | "socket.io": "^4.7.2",
23 | "ssh2": "^1.5.0",
24 | "uuid": "^8.3.2"
25 | },
26 | "devDependencies": {
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Clawcloud.md:
--------------------------------------------------------------------------------
1 |
2 | # CLAWCLOUD Run 平台部署Gnftato教程
3 |
4 | ## 准备工作
5 |
6 |
7 | 如果您还没有注册CLAWCLOUD Run账号,请先[点击此处注册](https://console.run.claw.cloud/signin?link=9IOYACCW0AQ4)
8 |
9 | ## 部署步骤
10 |
11 | 1. 注册完成后,进入控制台(建议左上角选美国东部地区)
12 |
13 | 2. 在控制台中选择并点击 **App Launchpad**
14 |
15 | 
16 |
17 | 3. 点击 **Create App** 按钮创建新应用
18 |
19 | 
20 |
21 | 4. 在创建应用页面填写以下信息:
22 | - **Application Name**:填写 `gnftato`
23 | - **Image**:选择 **Public**,然后填入 `fiftonb/gnftato`
24 | - **Usage**:根据您的实际需求选择合适的配置
25 | - **Network**:确保开放端口 **3001**
26 |
27 | 
28 |
29 | 5. 填写完所有信息后,滑动回页面顶部,点击 **Deploy Application** 按钮完成部署
30 |
31 | 
32 |
33 | 6. 等待几分钟,系统将完成应用的部署
34 |
35 | ## 访问应用
36 |
37 | 部署成功后,您可以通过分配的URL访问您的Gnftato应用。URL通常显示在应用详情页面。
38 |
39 | ## 常见问题
40 |
41 | 如遇到部署问题,请检查:
42 | - 网络连接是否正常
43 | - 镜像名称是否正确
44 | - 端口是否已正确配置
45 |
46 | 希望本教程对您有所帮助!
47 |
--------------------------------------------------------------------------------
/client/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Nftato-panel-client",
3 | "version": "1.0.0",
4 | "description": "Nftato防火墙规则多服务器管理面板前端",
5 | "private": true,
6 | "scripts": {
7 | "serve": "vue-cli-service serve",
8 | "build": "vue-cli-service build",
9 | "lint": "vue-cli-service lint"
10 | },
11 | "dependencies": {
12 | "axios": "^0.27.2",
13 | "core-js": "^3.25.0",
14 | "element-ui": "^2.15.10",
15 | "socket.io-client": "^4.7.2",
16 | "vue": "^2.7.10",
17 | "vue-router": "^3.6.5",
18 | "vuex": "^3.6.2"
19 | },
20 | "devDependencies": {
21 | "@vue/cli-plugin-babel": "~5.0.8",
22 | "@vue/cli-plugin-eslint": "~5.0.8",
23 | "@vue/cli-plugin-router": "~5.0.8",
24 | "@vue/cli-plugin-vuex": "~5.0.8",
25 | "@vue/cli-service": "~5.0.8",
26 | "babel-eslint": "^10.1.0",
27 | "eslint": "^8.23.0",
28 | "eslint-plugin-vue": "^9.4.0",
29 | "sass": "^1.54.8",
30 | "sass-loader": "^13.0.2",
31 | "vue-template-compiler": "^2.7.10"
32 | }
33 | }
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:20-alpine
2 |
3 | WORKDIR /app
4 |
5 | # 设置默认环境变量
6 | ENV NODE_ENV=development
7 | ENV PORT=3001
8 | ENV JWT_SECRET=default_secret_please_change
9 | ENV CORS_ORIGIN=*
10 |
11 | # 复制package.json文件
12 | COPY package*.json ./
13 | COPY server/package*.json ./server/
14 | COPY client/package*.json ./client/
15 |
16 | # 安装依赖
17 | RUN npm install
18 | RUN cd server && npm install --production=false
19 | RUN cd client && npm install
20 |
21 | # 复制应用程序代码
22 | COPY . .
23 |
24 | # 构建前端
25 | RUN npm run build
26 |
27 | # 给启动脚本添加执行权限
28 | RUN chmod +x /app/server/start.sh
29 | # 确保文件使用Unix格式的换行符
30 | RUN if [ -f /app/server/start.sh ]; then \
31 | sed -i 's/\r$//' /app/server/start.sh; \
32 | echo "start.sh文件存在并已修复换行符"; \
33 | else \
34 | echo "start.sh文件不存在!"; \
35 | exit 1; \
36 | fi
37 |
38 | # 暴露端口
39 | EXPOSE 3001
40 |
41 | # 设置工作目录
42 | WORKDIR /app/server
43 |
44 | # 安装jq工具以支持config.json解析
45 | RUN apk add --no-cache jq
46 |
47 | # 启动命令(使用绝对路径)
48 | CMD ["/bin/sh", "/app/server/start.sh"]
--------------------------------------------------------------------------------
/client/src/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import ElementUI from 'element-ui';
3 | import 'element-ui/lib/theme-chalk/index.css';
4 | import App from './App.vue';
5 | import router from './router';
6 | import store from './store';
7 | import axios from 'axios';
8 |
9 | // 设置axios默认配置
10 | axios.defaults.baseURL = process.env.VUE_APP_API_URL || '';
11 |
12 | // 添加响应拦截器处理认证错误
13 | axios.interceptors.response.use(
14 | response => response,
15 | error => {
16 | if (error.response && error.response.status === 401) {
17 | // 如果接收到401错误,清除认证状态并重定向到登录页
18 | store.dispatch('logout');
19 | router.push('/login');
20 | }
21 | return Promise.reject(error);
22 | }
23 | );
24 |
25 | // 如果已经有令牌,设置默认请求头
26 | const token = localStorage.getItem('token');
27 | if (token) {
28 | axios.defaults.headers.common['Authorization'] = `Bearer ${token}`;
29 | }
30 |
31 | Vue.prototype.$http = axios;
32 | Vue.use(ElementUI);
33 | Vue.config.productionTip = false;
34 |
35 | new Vue({
36 | router,
37 | store,
38 | render: h => h(App)
39 | }).$mount('#app');
--------------------------------------------------------------------------------
/server/start.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # 尝试加载环境变量,优先从.env文件加载
4 | if [ -f ../.env ]; then
5 | echo "从../.env加载环境变量"
6 | export $(cat ../.env | grep -v '#' | awk '/=/ {print $1}')
7 | elif [ -f ./.env ]; then
8 | echo "从./.env加载环境变量"
9 | export $(cat ./.env | grep -v '#' | awk '/=/ {print $1}')
10 | elif [ -f ./config.json ]; then
11 | echo "从config.json加载环境变量"
12 | # 使用jq工具解析JSON并设置环境变量(如果Docker容器中有jq)
13 | if command -v jq &> /dev/null; then
14 | while IFS="=" read -r key value; do
15 | export "$key"="$value"
16 | done < <(jq -r 'to_entries | map("\(.key)=\(.value)") | .[]' ./config.json)
17 | else
18 | echo "警告: 没有安装jq工具,无法解析config.json"
19 | fi
20 | else
21 | echo "警告: 找不到.env或config.json文件,将使用默认环境变量"
22 | fi
23 |
24 | # 创建初始管理员账户
25 | echo "检查并创建管理员账户..."
26 | node scripts/createAdmin.js
27 |
28 | # 根据环境变量选择启动模式
29 | if [ "$NODE_ENV" = "production" ]; then
30 | echo "以生产模式启动服务器..."
31 | npm run start
32 | elif [ "$STABLE_MODE" = "true" ]; then
33 | echo "以稳定开发模式启动服务器..."
34 | npm run dev:stable
35 | else
36 | echo "以开发模式启动服务器..."
37 | npm run dev
38 | fi
--------------------------------------------------------------------------------
/client/src/views/Home.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
欢迎使用Nftato防火墙管理面板
7 |
8 |
9 |
通过这个面板,您可以轻松管理多台服务器的nftables防火墙规则。
10 |
11 | - 封禁/解封垃圾邮件端口
12 | - 自定义封禁/解封出网端口
13 | - 管理入网端口白名单
14 | - 管理入网IP白名单
15 | - 管理DDOS防护规则
16 |
17 |
开始管理服务器
18 |
19 |
20 |
21 |
22 |
23 |
24 |
29 |
30 |
--------------------------------------------------------------------------------
/server/routes/serverRoutes.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const serverController = require('../controllers/serverController');
3 | const router = express.Router();
4 | const { protect } = require('../middlewares/authMiddleware');
5 |
6 | // 应用认证中间件保护所有路由
7 | router.use(protect);
8 |
9 | // 获取所有服务器
10 | router.get('/', serverController.getAllServers);
11 |
12 | // 获取单个服务器
13 | router.get('/:id', serverController.getServer);
14 |
15 | // 添加服务器
16 | router.post('/', serverController.createServer);
17 |
18 | // 更新服务器
19 | router.put('/:id', serverController.updateServer);
20 |
21 | // 删除服务器
22 | router.delete('/:id', serverController.deleteServer);
23 |
24 | // 服务器连接路由
25 | router.post('/:id/connect', serverController.connectServer);
26 | router.post('/:id/disconnect', serverController.disconnectServer);
27 | router.post('/test-connection', serverController.testConnection);
28 | router.post('/:id/execute', serverController.executeCommand);
29 | router.get('/:id/status', serverController.checkServerStatus);
30 | router.get('/:id/logs', serverController.getServerLogs);
31 |
32 | // Nftato部署路由
33 | router.post('/:id/deploy', serverController.deployIptato);
34 |
35 | // 检查脚本是否存在路由
36 | router.get('/:id/checkScript', serverController.checkScriptExists);
37 |
38 | module.exports = router;
--------------------------------------------------------------------------------
/Localdo.md:
--------------------------------------------------------------------------------
1 | ### 2. 安装依赖
2 |
3 | 安装nodejs环境(建议debian11+系统):
4 |
5 | ```bash
6 | apt-get remove nodejs npm
7 | rm -rf /usr/local/lib/node_modules
8 | rm -rf /usr/local/bin/npm
9 | rm -rf /usr/local/bin/node
10 | rm -rf ~/.npm
11 | source <(curl -L https://nodejs-install.netlify.app/install.sh) -v 22.2.0
12 | ```
13 |
14 | 一键安装所有依赖:
15 |
16 | ```bash
17 | npm run setup
18 | ```
19 |
20 | 可直接看第四步骤
21 |
22 | ### 3. 配置环境变量(项目自带可忽略)
23 |
24 | 复制`.env.example`文件为`.env`,或直接创建`.env`文件,并根据实际情况修改:
25 |
26 | ```bash
27 | cp .env.example .env
28 | ```
29 |
30 | 配置示例:
31 |
32 | ```
33 | # 服务器配置
34 | PORT=3001
35 | CORS_ORIGIN=http://localhost:8080
36 |
37 | # 数据目录配置
38 | DATA_DIR=./server/data
39 |
40 | # JWT配置
41 | JWT_SECRET=your-secret-key-change-this
42 | JWT_EXPIRES_IN=7d
43 |
44 | # 日志配置
45 | LOG_LEVEL=info
46 | LOG_DIR=./logs
47 |
48 | # 临时文件目录
49 | TMP_DIR=./tmp
50 |
51 | # 应用模式配置
52 | NODE_ENV=development
53 | # 设置为 true 可避免 nodemon 频繁重启
54 | STABLE_MODE=true
55 | ```
56 |
57 | ### 4. 构建与启动
58 |
59 | 一键构建前端并启动服务:
60 |
61 | ```bash
62 | # 构建前端 (将构建结果输出到 server/public 目录)
63 | npm run build
64 |
65 | # 启动后端服务器
66 | npm start
67 | ```
68 |
69 | 或使用一键启动脚本(仅开发模式):
70 |
71 | ```bash
72 | ./start-all.sh
73 | ```
74 |
75 | ## 开发模式
76 |
77 | 同时启动前端和后端开发服务器:
78 |
79 | ```bash
80 | npm run dev
81 | ```
--------------------------------------------------------------------------------
/client/src/router/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import VueRouter from 'vue-router';
3 | import Home from '../views/Home.vue';
4 | import Servers from '../views/Servers.vue';
5 | import Rules from '../views/Rules.vue';
6 | import Login from '../views/Login.vue';
7 | import Profile from '../views/Profile.vue';
8 | import store from '../store';
9 |
10 | Vue.use(VueRouter);
11 |
12 | const routes = [
13 | {
14 | path: '/',
15 | name: 'home',
16 | component: Home,
17 | meta: { requiresAuth: true }
18 | },
19 | {
20 | path: '/servers',
21 | name: 'servers',
22 | component: Servers,
23 | meta: { requiresAuth: true }
24 | },
25 | {
26 | path: '/rules/:serverId',
27 | name: 'rules',
28 | component: Rules,
29 | props: true,
30 | meta: { requiresAuth: true }
31 | },
32 | {
33 | path: '/profile',
34 | name: 'profile',
35 | component: Profile,
36 | meta: { requiresAuth: true }
37 | },
38 | {
39 | path: '/login',
40 | name: 'login',
41 | component: Login
42 | }
43 | ];
44 |
45 | const router = new VueRouter({
46 | mode: 'history',
47 | base: process.env.BASE_URL,
48 | routes
49 | });
50 |
51 | // 全局前置守卫
52 | router.beforeEach((to, from, next) => {
53 | const requiresAuth = to.matched.some(record => record.meta.requiresAuth);
54 | const isAuthenticated = store.getters.isAuthenticated;
55 |
56 | if (requiresAuth && !isAuthenticated) {
57 | next('/login');
58 | } else {
59 | next();
60 | }
61 | });
62 |
63 | export default router;
--------------------------------------------------------------------------------
/server/middlewares/authMiddleware.js:
--------------------------------------------------------------------------------
1 | const jwt = require('jsonwebtoken');
2 | const User = require('../models/User');
3 |
4 | // JWT密钥
5 | const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key';
6 |
7 | // 验证令牌是否有效的中间件
8 | exports.protect = async (req, res, next) => {
9 | try {
10 | let token;
11 |
12 | // 检查Authorization请求头
13 | if (
14 | req.headers.authorization &&
15 | req.headers.authorization.startsWith('Bearer')
16 | ) {
17 | // 获取Bearer令牌
18 | token = req.headers.authorization.split(' ')[1];
19 | }
20 |
21 | // 如果没有令牌,返回错误
22 | if (!token) {
23 | return res.status(401).json({
24 | success: false,
25 | message: '请先登录以获取访问权限'
26 | });
27 | }
28 |
29 | // 验证令牌
30 | const decoded = jwt.verify(token, JWT_SECRET);
31 |
32 | // 查找用户
33 | const currentUser = User.findUserByUsername(decoded.username);
34 |
35 | if (!currentUser) {
36 | return res.status(401).json({
37 | success: false,
38 | message: '此令牌的用户不存在'
39 | });
40 | }
41 |
42 | // 将用户信息添加到req对象
43 | req.user = {
44 | id: currentUser.id,
45 | username: currentUser.username
46 | };
47 |
48 | next();
49 | } catch (error) {
50 | if (error.name === 'JsonWebTokenError') {
51 | return res.status(401).json({
52 | success: false,
53 | message: '无效的令牌'
54 | });
55 | }
56 |
57 | if (error.name === 'TokenExpiredError') {
58 | return res.status(401).json({
59 | success: false,
60 | message: '令牌已过期'
61 | });
62 | }
63 |
64 | res.status(500).json({
65 | success: false,
66 | message: '服务器错误',
67 | error: error.message
68 | });
69 | }
70 | };
--------------------------------------------------------------------------------
/server/data/rules.json:
--------------------------------------------------------------------------------
1 | {
2 | "rules": {
3 | "25f15b80-623f-49a9-b76f-9920f57e59d3": {
4 | "lastUpdate": "2025-05-01T14:20:26.794Z",
5 | "data": {
6 | "sshPortStatus": "文件存在 脚本不是初次运行\n\nSSH 端口为 22",
7 | "inboundIPs": [
8 | {
9 | "ip": "1.1.1.1"
10 | }
11 | ],
12 | "inboundPorts": {
13 | "tcp": [
14 | 22,
15 | 555
16 | ],
17 | "udp": [
18 | 22,
19 | 555
20 | ]
21 | },
22 | "defenseStatus": "文件存在 脚本不是初次运行\n============当前防御状态============\n\u001b[32m[已启用]\u001b[0m DDoS防御\n已阻止HTTP连接次数: 0\n已阻止HTTPS连接次数: 0\n\n\u001b[33m=== 当前保护的端口列表 ===\u001b[0m\n标准端口: 80(HTTP), 443(HTTPS)\n自定义端口: 777,999\n\n\u001b[33m=== 端口保护配置详情 ===\u001b[0m\n格式说明: [端口号] - [连接限制/分钟] [连接限制/秒] [每IP最大连接数] [最大并发连接数] [封禁时长]\n80(HTTP) - 500/分钟 300/秒 600 100000 1d (已阻止: 0)\n443(HTTPS) - 500/分钟 300/秒 600 100000 1d (已阻止: 0)\n777(TCP+UDP) - 500/分钟 300/秒 600 100000 12h (已阻止: 0)\n999(TCP+UDP) - 500/分钟 300/秒 600 100000 2d2h (已阻止: 0)\n\n\u001b[33m=== 总体防御情况 ===\u001b[0m\n总拦截连接数: 0\n当前白名单IPv4数量: 0\n当前白名单IPv6数量: 0\n当前黑名单IPv4数量: 0\n当前黑名单IPv6数量: 0\n=================================="
23 | }
24 | },
25 | "09166f22-d6ae-46ab-8736-112abc960d82": {
26 | "lastUpdate": "2025-05-01T14:31:24.605Z",
27 | "data": {
28 | "inboundPorts": {
29 | "tcp": [
30 | 22,
31 | 555
32 | ],
33 | "udp": [
34 | 22,
35 | 555
36 | ]
37 | },
38 | "sshPortStatus": "文件存在 脚本不是初次运行\n\nSSH 端口为 22",
39 | "inboundIPs": [
40 | {
41 | "ip": "1.1.1.1"
42 | }
43 | ]
44 | }
45 | }
46 | }
47 | }
--------------------------------------------------------------------------------
/intcentos.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -e
3 |
4 | echo "开始配置CentOS 9 nftables防火墙..."
5 |
6 | # 获取SSH端口
7 | SSH_PORT=$(ss -tlnp | grep sshd | awk '{print $4}' | awk -F ':' '{print $NF}' | head -1)
8 | [ -z "$SSH_PORT" ] && SSH_PORT=22
9 | echo "检测到SSH端口: $SSH_PORT"
10 |
11 | # 配置nftables
12 | echo "配置nftables规则..."
13 |
14 | # 创建规则文件 - 关键是使用正确的CentOS 9路径
15 | mkdir -p /etc/nftables
16 | cat > /etc/nftables/main.nft << EOF
17 | #!/usr/sbin/nft -f
18 |
19 | # 清空现有规则
20 | flush ruleset
21 |
22 | # 基本防火墙规则
23 | table inet filter {
24 | chain input {
25 | type filter hook input priority 0; policy drop;
26 |
27 | # 允许本地回环
28 | iifname "lo" accept
29 |
30 | # 允许已建立连接
31 | ct state established,related accept
32 |
33 | # 允许ICMP
34 | ip protocol icmp accept
35 | ip6 nexthdr icmpv6 accept
36 |
37 | # 允许SSH
38 | tcp dport ${SSH_PORT} accept
39 | }
40 |
41 | chain forward {
42 | type filter hook forward priority 0; policy drop;
43 | }
44 |
45 | chain output {
46 | type filter hook output priority 0; policy accept;
47 | }
48 | }
49 | EOF
50 |
51 | # 创建正确的配置文件 - CentOS 9中使用/etc/sysconfig/nftables.conf
52 | cat > /etc/sysconfig/nftables.conf << EOF
53 | # 加载主规则文件
54 | include "/etc/nftables/main.nft"
55 | EOF
56 |
57 | echo "应用nftables规则..."
58 | nft -f /etc/nftables/main.nft
59 |
60 | # 设置正确的文件权限和SELinux上下文
61 | chmod 600 /etc/nftables/main.nft
62 | chmod 600 /etc/sysconfig/nftables.conf
63 | restorecon -v /etc/nftables/main.nft || true
64 | restorecon -v /etc/sysconfig/nftables.conf || true
65 |
66 | # 重启nftables服务
67 | systemctl restart nftables
68 |
69 | # 确保服务开机启动
70 | systemctl enable nftables
71 |
72 | echo "检查nftables规则..."
73 | nft list ruleset
74 |
75 | echo "防火墙配置完成"
76 | echo "已放行SSH端口 ${SSH_PORT}"
77 | echo "出站流量不受限制"
--------------------------------------------------------------------------------
/client/src/views/Profile.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
11 |
12 | 账号信息
13 |
14 |
15 |
用户名: {{ currentUser.username }}
16 |
创建时间: {{ formatDate(currentUser.createdAt) }}
17 |
18 |
19 |
20 |
21 |
22 | 修改密码
23 |
24 |
25 |
26 |
27 |
28 |
29 |
56 |
57 |
--------------------------------------------------------------------------------
/server/scripts/createAdmin.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 创建管理员账户的脚本
3 | *
4 | * 用法: node createAdmin.js
5 | */
6 |
7 | const bcrypt = require('bcryptjs');
8 | const fs = require('fs');
9 | const path = require('path');
10 | require('dotenv').config({ path: path.join(__dirname, '../../.env') });
11 |
12 | // 默认管理员配置
13 | const DEFAULT_ADMIN = {
14 | username: 'admin',
15 | password: 'admin123'
16 | };
17 |
18 | // 数据目录
19 | let dataDir;
20 | if (process.env.DATA_DIR) {
21 | if (process.env.DATA_DIR.startsWith('./')) {
22 | dataDir = path.join(__dirname, '../..', process.env.DATA_DIR.substring(2));
23 | } else {
24 | dataDir = path.resolve(process.env.DATA_DIR);
25 | }
26 | } else {
27 | dataDir = path.join(__dirname, '../data');
28 | }
29 |
30 | // 用户数据文件路径
31 | const usersFilePath = path.join(dataDir, 'users.json');
32 |
33 | // 检查并创建数据目录
34 | if (!fs.existsSync(dataDir)) {
35 | fs.mkdirSync(dataDir, { recursive: true });
36 | console.log(`数据目录已创建: ${dataDir}`);
37 | }
38 |
39 | // 读取现有用户或创建空数组
40 | let users = [];
41 | if (fs.existsSync(usersFilePath)) {
42 | try {
43 | const data = fs.readFileSync(usersFilePath, 'utf8');
44 | users = JSON.parse(data).users;
45 | } catch (error) {
46 | console.error('读取用户数据失败:', error);
47 | process.exit(1);
48 | }
49 | }
50 |
51 | // 检查是否已存在管理员
52 | const adminExists = users.some(user => user.username === DEFAULT_ADMIN.username);
53 |
54 | if (adminExists) {
55 | console.log(`管理员账户 '${DEFAULT_ADMIN.username}' 已存在,无需创建`);
56 | process.exit(0);
57 | }
58 |
59 | // 创建管理员账户
60 | async function createAdmin() {
61 | try {
62 | // 加密密码
63 | const salt = await bcrypt.genSalt(10);
64 | const hashedPassword = await bcrypt.hash(DEFAULT_ADMIN.password, salt);
65 |
66 | // 创建管理员用户
67 | const admin = {
68 | id: Date.now().toString(),
69 | username: DEFAULT_ADMIN.username,
70 | password: hashedPassword,
71 | isAdmin: true,
72 | createdAt: new Date().toISOString()
73 | };
74 |
75 | // 添加到用户列表
76 | users.push(admin);
77 |
78 | // 保存到文件
79 | fs.writeFileSync(usersFilePath, JSON.stringify({ users }, null, 2));
80 |
81 | console.log('管理员账户创建成功!');
82 | console.log(`用户名: ${DEFAULT_ADMIN.username}`);
83 | console.log(`密码: ${DEFAULT_ADMIN.password}`);
84 | console.log('请登录后立即修改默认密码');
85 | } catch (error) {
86 | console.error('创建管理员失败:', error);
87 | process.exit(1);
88 | }
89 | }
90 |
91 | createAdmin();
--------------------------------------------------------------------------------
/server/routes/rulesRoutes.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const rulesController = require('../controllers/rulesController');
3 | const router = express.Router();
4 | const { protect } = require('../middlewares/authMiddleware');
5 |
6 | // 应用认证中间件保护所有路由
7 | router.use(protect);
8 |
9 | // 规则缓存路由
10 | router.get('/:serverId/cache', rulesController.getServerRulesCache);
11 | router.get('/:serverId/cache/last-update', rulesController.getCacheLastUpdate);
12 | router.delete('/:serverId/cache', rulesController.clearServerCache);
13 | router.put('/:serverId/cache/:key', rulesController.updateCacheItem);
14 |
15 | // 出网控制路由
16 | router.get('/:serverId/blocklist', rulesController.getBlockList);
17 | router.post('/:serverId/block/bt-pt', rulesController.blockBTPT);
18 | router.post('/:serverId/block/spam', rulesController.blockSPAM);
19 | router.post('/:serverId/block/all', rulesController.blockAll);
20 | router.post('/:serverId/block/ports', rulesController.blockCustomPorts);
21 | router.post('/:serverId/block/keyword', rulesController.blockCustomKeyword);
22 | router.post('/:serverId/unblock/bt-pt', rulesController.unblockBTPT);
23 | router.post('/:serverId/unblock/spam', rulesController.unblockSPAM);
24 | router.post('/:serverId/unblock/all', rulesController.unblockAll);
25 | router.post('/:serverId/unblock/ports', rulesController.unblockCustomPorts);
26 | router.post('/:serverId/unblock/keyword', rulesController.unblockCustomKeyword);
27 | router.post('/:serverId/unblock/all-keywords', rulesController.unblockAllKeywords);
28 |
29 | // 入网控制路由
30 | router.get('/:serverId/inbound/ports', rulesController.getInboundPorts);
31 | router.get('/:serverId/inbound/ips', rulesController.getInboundIPs);
32 | router.post('/:serverId/inbound/allow/ports', rulesController.allowInboundPorts);
33 | router.post('/:serverId/inbound/disallow/ports', rulesController.disallowInboundPorts);
34 | router.post('/:serverId/inbound/allow/ips', rulesController.allowInboundIPs);
35 | router.post('/:serverId/inbound/disallow/ips', rulesController.disallowInboundIPs);
36 |
37 | // DDoS防御路由
38 | router.post('/:serverId/ddos/protection', rulesController.setupDdosProtection);
39 | router.post('/:serverId/ddos/custom-port', rulesController.setupCustomPortProtection);
40 | router.post('/:serverId/ddos/ip-lists', rulesController.manageIpLists);
41 | router.get('/:serverId/ddos/status', rulesController.viewDefenseStatus);
42 |
43 | // 增强功能路由
44 | router.get('/:serverId/ssh-port', rulesController.getSSHPort);
45 | router.post('/:serverId/clear-all', rulesController.clearAllRules);
46 |
47 | module.exports = router;
--------------------------------------------------------------------------------
/Dockerdo.md:
--------------------------------------------------------------------------------
1 | ### 5. 使用Docker部署
2 |
3 | 本项目支持使用Docker进行容器化部署,方便在不同环境中快速启动服务。
4 |
5 | #### 5.1 构建Docker镜像(不想构建直接从5.5看)
6 |
7 | ```bash
8 | # 在项目根目录下执行
9 | docker build -t nftato-app .
10 | ```
11 |
12 | #### 5.2 运行Docker容器
13 |
14 | 基本运行命令:
15 |
16 | ```bash
17 | docker run -d -p 3001:3001 --name nftato-container nftato-app
18 | ```
19 |
20 | #### 5.3 数据持久化
21 |
22 | 为了确保数据持久化(包括服务器配置、用户数据等),需要挂载数据目录:
23 |
24 | ```bash
25 | # 在项目根目录下
26 | # 确保数据目录存在
27 | mkdir -p $(pwd)/server/data
28 |
29 | # 运行容器并挂载数据目录
30 | docker run -d -p 3001:3001 \
31 | -v $(pwd)/server/data:/app/server/data \
32 | -v $(pwd)/server/config.json:/app/server/config.json \
33 | --name nftato-container nftato-app
34 | ```
35 |
36 | #### 5.4 使用Docker卷实现更好的数据管理(可选)
37 |
38 | ```bash
39 | # 创建数据卷
40 | docker volume create nftato-data
41 |
42 | # 使用数据卷运行容器
43 | docker run -d -p 3001:3001 \
44 | -v nftato-data:/app/server/data \
45 | -v $(pwd)/server/config.json:/app/server/config.json \
46 | --name nftato-container nftato-app
47 | ```
48 |
49 | #### 5.5 从Docker Hub直接拉取镜像(无需本地构建)
50 |
51 | 您也可以直接从Docker Hub拉取预构建的镜像:
52 |
53 | ```bash
54 | # 拉取镜像
55 | docker pull fiftonb/gnftato:latest
56 |
57 | # 运行容器
58 | docker run -d -p 3001:3001 \
59 | -v $(pwd)/server/data:/app/server/data \
60 | -v $(pwd)/server/config.json:/app/server/config.json \
61 | --name nftato-container fiftonb/gnftato:latest
62 | ```
63 |
64 | 这种方式无需在本地构建镜像,可以直接使用已发布的镜像运行应用。
65 |
66 | #### 5.6 设置环境变量
67 |
68 | 您可以通过环境变量自定义应用配置:
69 |
70 | ```bash
71 | # 使用-e选项设置单个环境变量
72 | docker run -d -p 3001:3001 \
73 | -e NODE_ENV=production \
74 | -e JWT_SECRET=your_custom_secret \
75 | -v $(pwd)/server/data:/app/server/data \
76 | --name nftato-container fiftonb/gnftato:latest
77 | ```
78 |
79 | 或者使用环境变量文件:
80 |
81 | ```bash
82 | # 创建.env文件
83 | cat > .env << EOL
84 | NODE_ENV=production
85 | JWT_SECRET=your_custom_secret
86 | PORT=3001
87 | CORS_ORIGIN=*
88 | EOL
89 |
90 | # 使用环境变量文件运行容器
91 | docker run -d -p 3001:3001 \
92 | --env-file .env \
93 | -v $(pwd)/server/data:/app/server/data \
94 | --name nftato-container fiftonb/gnftato:latest
95 | ```
96 |
97 | 可用的环境变量及其说明:
98 |
99 | | 环境变量 | 说明 | 默认值 |
100 | |---------|------|--------|
101 | | PORT | 服务器监听端口 | 3001 |
102 | | HOST | 服务器监听地址 | 0.0.0.0 |
103 | | NODE_ENV | 运行模式 | development |
104 | | JWT_SECRET | JWT签名密钥 | iptato-secure-jwt-secret-key |
105 | | JWT_EXPIRES_IN | JWT过期时间 | 7d |
106 | | CORS_ORIGIN | CORS允许的源 | * |
107 | | STABLE_MODE | 稳定模式 | true |
108 |
109 | #### 5.7 查看容器日志
110 |
111 | ```bash
112 | docker logs nftato-container
113 | ```
114 |
115 | #### 5.8 停止和重启容器
116 |
117 | ```bash
118 | # 停止容器
119 | docker stop nftato-container
120 |
121 | # 重启容器
122 | docker start nftato-container
123 |
124 | # 删除容器(数据卷不会被删除)
125 | docker rm nftato-container
126 | ```
--------------------------------------------------------------------------------
/projectStructure.txt:
--------------------------------------------------------------------------------
1 | Gnftato/
2 | ├── client/ # 前端Vue项目
3 | │ ├── src/
4 | │ │ ├── views/ # 页面组件
5 | │ │ │ ├── Home.vue # 主页/仪表盘,显示系统概况
6 | │ │ │ ├── Servers.vue # 服务器管理页面,用于添加/编辑/删除服务器
7 | │ │ │ ├── Rules.vue # 防火墙规则管理,配置nftables规则
8 | │ │ │ ├── Login.vue # 用户登录页面
9 | │ │ │ ├── Register.vue # 用户注册页面
10 | │ │ │ ├── Profile.vue # 用户资料设置页面
11 | │ │ ├── components/ # 通用组件
12 | │ │ │ ├── ServerForm.vue # 服务器添加/编辑表单组件
13 | │ │ │ ├── ChangePasswordForm.vue # 修改密码表单组件
14 | │ │ │ ├── rules/ # 规则相关子组件
15 | │ │ ├── router/ # Vue路由管理
16 | │ │ │ ├── index.js # 路由配置文件,定义应用所有路由
17 | │ │ ├── store/ # Vuex状态管理
18 | │ │ │ ├── index.js # Vuex入口,组装模块并导出store
19 | │ │ │ ├── modules/ # Vuex模块目录
20 | │ │ │ ├── auth.js # 认证状态管理模块
21 | │ │ │ ├── servers.js # 服务器状态管理模块
22 | │ │ │ ├── rules.js # 规则状态管理模块
23 | │ │ ├── App.vue # 应用主组件,根组件
24 | │ │ ├── main.js # 应用入口文件,初始化Vue实例
25 | │ ├── public/ # 静态资源
26 | │ │ ├── index.html # HTML模板文件
27 | │ ├── package.json # 前端依赖管理
28 | │ ├── vue.config.js # Vue配置文件
29 | │ ├── babel.config.js # Babel配置
30 | │ ├── .npmrc # NPM配置文件
31 | ├── server/ # 后端Express项目
32 | │ ├── controllers/ # 控制器
33 | │ │ ├── serverController.js # 服务器管理控制器,处理服务器CRUD操作
34 | │ │ ├── rulesController.js # 规则管理控制器,处理防火墙规则操作
35 | │ │ ├── authController.js # 认证控制器,处理登录/注册等操作
36 | │ ├── models/ # 数据模型
37 | │ │ ├── Server.js # 服务器模型,定义服务器数据结构
38 | │ │ ├── Rule.js # 规则模型,定义防火墙规则数据结构
39 | │ │ ├── User.js # 用户模型,定义用户数据结构及认证逻辑
40 | │ ├── services/ # 业务逻辑
41 | │ │ ├── sshService.js # SSH连接服务,处理与远程服务器的通信
42 | │ │ ├── nftablesService.js # nftables管理服务,处理nftables规则应用
43 | │ │ ├── cacheService.js # 缓存服务,缓存服务器状态和规则数据
44 | │ ├── routes/ # API路由
45 | │ │ ├── serverRoutes.js # 服务器相关API路由
46 | │ │ ├── rulesRoutes.js # 规则相关API路由
47 | │ │ ├── authRoutes.js # 认证相关API路由
48 | │ ├── middlewares/ # 中间件
49 | │ │ ├── authMiddleware.js # 认证中间件,处理API访问权限验证
50 | │ ├── scripts/ # 脚本文件
51 | │ │ ├── Nftato.sh # nftables核心主脚本备份
52 | │ │ ├── createAdmin.js # 创建管理员用户脚本
53 | │ ├── data/ # 数据存储目录,用于JSON文件存储
54 | │ ├── public/ # 静态资源
55 | │ ├── app.js # 主应用入口,Express服务器配置
56 | │ ├── config.json # 应用配置文件
57 | │ ├── start.sh # 后端启动脚本
58 | │ ├── nodemon.json # Nodemon配置,用于开发环境
59 | ├── Nftato.sh # nftables核心主脚本
60 | ├── intcentos.sh # CentOS系统安装脚本(仅测试)
61 | ├── start-all.sh # 前后端一键启动脚本
62 | ├── Dockerfile # Docker配置文件,用于容器化部署
63 | ├── .env # 环境变量配置
64 | ├── .env.example # 环境变量示例文件
65 | ├── package.json # 项目根目录依赖管理
66 | ├── package-lock.json # 依赖版本锁定文件
67 | ├── .gitignore # Git忽略配置
68 | ├── README.md # 项目说明文档
--------------------------------------------------------------------------------
/server/models/User.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const path = require('path');
3 | const bcrypt = require('bcryptjs');
4 |
5 | class User {
6 | constructor() {
7 | // 获取用户数据文件路径
8 | let dataDir;
9 | if (process.env.DATA_DIR) {
10 | if (process.env.DATA_DIR.startsWith('./')) {
11 | dataDir = path.join(__dirname, '../..', process.env.DATA_DIR.substring(2));
12 | } else {
13 | dataDir = path.resolve(process.env.DATA_DIR);
14 | }
15 | } else {
16 | dataDir = path.join(__dirname, '../data');
17 | }
18 |
19 | this.usersFilePath = path.join(dataDir, 'users.json');
20 |
21 | // 如果用户数据文件不存在,创建一个空的
22 | if (!fs.existsSync(this.usersFilePath)) {
23 | fs.writeFileSync(this.usersFilePath, JSON.stringify({ users: [] }, null, 2));
24 | }
25 | }
26 |
27 | // 获取所有用户
28 | getUsers() {
29 | const data = fs.readFileSync(this.usersFilePath, 'utf8');
30 | return JSON.parse(data).users;
31 | }
32 |
33 | // 保存用户数据
34 | saveUsers(users) {
35 | fs.writeFileSync(this.usersFilePath, JSON.stringify({ users }, null, 2));
36 | }
37 |
38 | // 通过用户名查找用户
39 | findUserByUsername(username) {
40 | const users = this.getUsers();
41 | return users.find(user => user.username === username);
42 | }
43 |
44 | // 创建新用户
45 | async createUser(username, password) {
46 | // 检查用户名是否已存在
47 | if (this.findUserByUsername(username)) {
48 | throw new Error('用户名已存在');
49 | }
50 |
51 | const users = this.getUsers();
52 |
53 | // 加密密码
54 | const salt = await bcrypt.genSalt(10);
55 | const hashedPassword = await bcrypt.hash(password, salt);
56 |
57 | // 创建新用户
58 | const newUser = {
59 | id: Date.now().toString(),
60 | username,
61 | password: hashedPassword,
62 | createdAt: new Date().toISOString()
63 | };
64 |
65 | users.push(newUser);
66 | this.saveUsers(users);
67 |
68 | // 返回不含密码的用户信息
69 | const { password: _, ...userWithoutPassword } = newUser;
70 | return userWithoutPassword;
71 | }
72 |
73 | // 验证用户登录
74 | async validateUser(username, password) {
75 | const user = this.findUserByUsername(username);
76 |
77 | if (!user) {
78 | return null;
79 | }
80 |
81 | const isMatch = await bcrypt.compare(password, user.password);
82 |
83 | if (!isMatch) {
84 | return null;
85 | }
86 |
87 | // 返回不含密码的用户信息
88 | const { password: _, ...userWithoutPassword } = user;
89 | return userWithoutPassword;
90 | }
91 |
92 | // 更新用户密码
93 | async updatePassword(userId, newPassword) {
94 | const users = this.getUsers();
95 | const userIndex = users.findIndex(user => user.id === userId);
96 |
97 | if (userIndex === -1) {
98 | throw new Error('用户不存在');
99 | }
100 |
101 | // 加密新密码
102 | const salt = await bcrypt.genSalt(10);
103 | const hashedPassword = await bcrypt.hash(newPassword, salt);
104 |
105 | // 更新用户密码
106 | users[userIndex].password = hashedPassword;
107 | users[userIndex].updatedAt = new Date().toISOString();
108 |
109 | // 保存更新
110 | this.saveUsers(users);
111 |
112 | return true;
113 | }
114 | }
115 |
116 | module.exports = new User();
--------------------------------------------------------------------------------
/client/src/views/Login.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
Gnftato 防火墙管理系统
6 |
7 |
14 |
15 |
16 |
17 |
18 |
24 |
25 |
26 | 登录
27 |
28 |
29 |
30 | 默认管理员账户:admin / admin123
31 |
32 |
33 |
34 |
35 |
36 |
101 |
102 |
--------------------------------------------------------------------------------
/client/src/store/modules/auth.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 |
3 | // 初始状态
4 | const state = {
5 | token: localStorage.getItem('token') || null,
6 | user: null,
7 | loading: false
8 | };
9 |
10 | // Getters
11 | const getters = {
12 | isAuthenticated: state => !!state.token,
13 | currentUser: state => state.user,
14 | isLoading: state => state.loading
15 | };
16 |
17 | // Actions
18 | const actions = {
19 | // 登录
20 | async login({ commit }, credentials) {
21 | commit('SET_LOADING', true);
22 | try {
23 | const response = await axios.post('/api/auth/login', credentials);
24 | const { token, user } = response.data.data;
25 |
26 | // 存储令牌到本地存储和状态
27 | localStorage.setItem('token', token);
28 | commit('SET_TOKEN', token);
29 | commit('SET_USER', user);
30 |
31 | // 设置全局认证头
32 | axios.defaults.headers.common['Authorization'] = `Bearer ${token}`;
33 |
34 | return response;
35 | } catch (error) {
36 | commit('SET_TOKEN', null);
37 | commit('SET_USER', null);
38 | localStorage.removeItem('token');
39 | throw error;
40 | } finally {
41 | commit('SET_LOADING', false);
42 | }
43 | },
44 |
45 | /**
46 | * 注册功能 - 仅供API调用,前端不使用
47 | * 保留此代码以便将来通过API工具或后台管理使用
48 | */
49 | async register({ commit, dispatch }, credentials) {
50 | commit('SET_LOADING', true);
51 | try {
52 | const response = await axios.post('/api/auth/register', credentials);
53 | const { token, user } = response.data.data;
54 |
55 | // 存储令牌到本地存储和状态
56 | localStorage.setItem('token', token);
57 | commit('SET_TOKEN', token);
58 | commit('SET_USER', user);
59 |
60 | // 设置全局认证头
61 | axios.defaults.headers.common['Authorization'] = `Bearer ${token}`;
62 |
63 | return response;
64 | } catch (error) {
65 | commit('SET_TOKEN', null);
66 | commit('SET_USER', null);
67 | localStorage.removeItem('token');
68 | throw error;
69 | } finally {
70 | commit('SET_LOADING', false);
71 | }
72 | },
73 |
74 | // 获取当前用户信息
75 | async getCurrentUser({ commit, state }) {
76 | if (!state.token) return;
77 |
78 | commit('SET_LOADING', true);
79 | try {
80 | const response = await axios.get('/api/auth/me');
81 | commit('SET_USER', response.data.data.user);
82 | return response;
83 | } catch (error) {
84 | // 如果令牌无效或过期,清除认证状态
85 | if (error.response && error.response.status === 401) {
86 | commit('SET_TOKEN', null);
87 | commit('SET_USER', null);
88 | localStorage.removeItem('token');
89 | }
90 | throw error;
91 | } finally {
92 | commit('SET_LOADING', false);
93 | }
94 | },
95 |
96 | // 登出
97 | logout({ commit }) {
98 | commit('SET_TOKEN', null);
99 | commit('SET_USER', null);
100 | localStorage.removeItem('token');
101 | delete axios.defaults.headers.common['Authorization'];
102 | }
103 | };
104 |
105 | // Mutations
106 | const mutations = {
107 | SET_TOKEN(state, token) {
108 | state.token = token;
109 | },
110 | SET_USER(state, user) {
111 | state.user = user;
112 | },
113 | SET_LOADING(state, isLoading) {
114 | state.loading = isLoading;
115 | }
116 | };
117 |
118 | export default {
119 | state,
120 | getters,
121 | actions,
122 | mutations
123 | };
--------------------------------------------------------------------------------
/client/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
19 |
20 |
21 |
22 |
23 |
60 |
61 |
--------------------------------------------------------------------------------
/client/src/components/ChangePasswordForm.vue:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
15 |
16 |
17 |
22 |
23 |
24 |
30 |
31 |
32 | 修改密码
33 | 重置
34 |
35 |
36 |
37 |
38 |
112 |
113 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # GNftato Panel - 多服务器防火墙规则管理面板
2 |
3 | 基于Nftato.sh脚本开发的可视化多服务器防火墙规则管理面板,支持通过SSH远程连接管理多台服务器的nftables规则。
4 |
5 | > 前端现在也不是很满意,但是,也就这样了(能用)
6 | > 另外关于测试用例覆盖啥的将就吧,精力不够,还是能用就行了
7 | > 有能力的自己二开吧,虽然代码像坨屎,能跑就行...
8 |
9 | ## 功能特色
10 |
11 | - **多服务器管理**:集中管理多台服务器的防火墙规则
12 | - **出网控制**:封禁/解封 SPAM端口、自定义端口
13 | - **入网控制**:管理入网端口和IP白名单
14 | - **SSH远程控制**:通过SSH安全连接到远程服务器执行命令
15 | - **可视化操作**:直观的界面操作替代复杂的命令行管理
16 | - **状态监控**:实时查看各服务器的连接状态和规则列表
17 | - **登录认证**:用户身份验证,保护管理界面安全
18 | - **DDOS防御**:借鉴Goedge防御规则实现的脚本防御
19 |
20 | > 需要注意,使用同类用到nftables命令的工具会使规则冲突。清除规则则可以夺回控制权。脚本首次运行默认只放行ssh端口,且ssh端口无法取消放行。
21 |
22 | ## TODO
23 |
24 | - [X] Debian11+ 脚本测试通过
25 | - [X] Ubuntu20+ 脚本测试通过
26 | - [X] Centos9+ 脚本测试通过
27 | - [X] 重写前端业务逻辑
28 | - [X] 优化部署脚本指令
29 | - [X] 自动更新核心代码功能
30 | - [ ] 一键清除黑白名单
31 | - [ ] 获取黑白名单IP列表
32 | - [ ] 批量导入IP添加黑白名单
33 | - [ ] 实现端口转发
34 | - [X] 完善部署文档
35 | - [X] 搭建预览链接
36 |
37 | ## 技术栈
38 |
39 | - **后端**:Node.js、Express、SSH2、本地JSON存储、JWT认证
40 | - **前端**:Vue.js 2.x、Element UI、Axios、Vuex状态管理
41 | - **通信**:RESTful API
42 | - **认证**:基于JWT的用户认证系统
43 |
44 | ## 系统要求
45 |
46 | - Node.js 12.x以上
47 | - 远程服务器需支持SSH连接
48 |
49 | ## 安装部署
50 |
51 | ### 1. 克隆项目
52 |
53 | ```bash
54 | git clone https://github.com/Fiftonb/Gnftato.git
55 | cd Gnftato
56 | ```
57 | Clawcloud Run云平台部署教程=>[点击查看](https://github.com/Fiftonb/Gnftato/blob/main/Clawcloud.md)
58 |
59 | Docker部署教程=>[点击查看](https://github.com/Fiftonb/Gnftato/blob/main/Dockerdo.md)
60 |
61 | 本地环境部署教程=>[点击查看](https://github.com/Fiftonb/Gnftato/blob/main/Localdo.md)
62 |
63 | ## 用户认证
64 |
65 | 系统采用固定管理员模式,不支持开放注册。系统启动时会自动创建默认管理员账户:
66 |
67 | - **用户名**: admin
68 | - **密码**: admin123
69 |
70 | 您也可以通过命令行创建/重置管理员账户:
71 |
72 | ```bash
73 | cd server
74 | npm run create-admin
75 | ```
76 |
77 | ## 服务访问
78 |
79 | - 前端界面: http://localhost:8080 (开发模式)或 http://localhost:3001 (生产模式)
80 | - 后端API: http://localhost:3001/api
81 |
82 | ## 使用演示
83 |
84 | 使用演示=>[点击查看](https://github.com/Fiftonb/Gnftato/blob/main/USE.md)
85 |
86 | ## 功能说明
87 |
88 | ### 放行IP与IP黑白名单的区别
89 |
90 | 系统提供两种IP管理功能,它们服务于不同的目的:
91 |
92 | 1. **放行IP (入网方向功能 - 第17项)**
93 | - 作用于基本防火墙层面,控制哪些IP可以访问服务器
94 | - 如果服务器防火墙默认策略是拒绝(DROP),只有被放行的IP才能建立连接
95 | - 未被放行的IP会被基本防火墙直接拒绝访问
96 | - 命令实现: `nft add rule inet filter input ip saddr $IP accept`
97 |
98 | 2. **IP黑白名单 (DDoS防御功能 - 第24项)**
99 | - 作用于DDoS防御层面,位于基本防火墙之后
100 | - **白名单IP**: 可以绕过DDoS防御检测,不受连接频率和数量限制
101 | - **黑名单IP**: 被直接拒绝,不论连接次数和频率
102 | - IP必须先通过基本防火墙(被放行或防火墙默认允许),才会到达DDoS防御层
103 |
104 | 使用建议:
105 | - 如果服务器设置为默认拒绝所有连接,需要先使用"放行IP"功能
106 | - 如果已开启DDoS防御,对于需要频繁访问的可信IP,建议添加到白名单
107 | - 如果只需简单的访问控制,使用"放行IP"即可
108 | - 如果需要防御DDoS攻击同时允许特定IP不受限制,应使用白名单功能
109 |
110 | ## 安全提示
111 |
112 | - 登录系统后请立即修改默认管理员密码
113 | - 确保JWT密钥安全,不要使用默认的密钥
114 | - 请确保使用安全的密码
115 | - 建议使用SSH密钥认证而非密码认证
116 | - 服务器连接信息(特别是密码和私钥)存储在本地JSON文件中
117 |
118 |
119 | ## 项目参考
120 |
121 | 本项目基于[GiPtato](https://github.com/Fiftonb/GiPtato)开发,内核脚本从iptables迁移到nftables的升级版本。
122 | > 使用nftables替代iptables实现更现代化的防火墙管理。
123 |
124 | 不使用面板只想使用脚本(完善后的脚本)
125 |
126 | ```bash
127 | wget -N --no-check-certificate https://raw.githubusercontent.com/Fiftonb/Gnftato/refs/heads/main/Nftato.sh && chmod +x Nftato.sh && bash Nftato.sh
128 | ```
129 | 二次使用目录下执行
130 | ```sh
131 | ./Nftato.sh
132 | ```
133 |
134 | ## 免责声明
135 |
136 | * 此项目开发目的为本人自用,因此本人不能保证向后兼容性。
137 | * 由于本人能力有限,不能保证所有功能的可用性,如果出现问题请在Issues反馈。
138 | * 本人不对任何人使用本项目造成的任何后果承担责任。
139 | * 本人比较多变,因此本项目可能会随想法或思路的变动随性更改项目结构或大规模重构代码,若不能接受请勿使用。
140 |
141 | ## 许可证
142 |
143 | MIT License
144 |
145 | ## Stargazers over time
146 | [](https://starchart.cc/Fiftonb/Gnftato)
147 |
148 |
149 |
150 |
--------------------------------------------------------------------------------
/server/controllers/authController.js:
--------------------------------------------------------------------------------
1 | const User = require('../models/User');
2 | const jwt = require('jsonwebtoken');
3 |
4 | // JWT密钥
5 | const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key';
6 | const JWT_EXPIRES_IN = process.env.JWT_EXPIRES_IN || '7d';
7 |
8 | // 生成JWT令牌
9 | const generateToken = (user) => {
10 | return jwt.sign(
11 | { id: user.id, username: user.username },
12 | JWT_SECRET,
13 | { expiresIn: JWT_EXPIRES_IN }
14 | );
15 | };
16 |
17 | // 用户注册
18 | exports.register = async (req, res) => {
19 | try {
20 | const { username, password } = req.body;
21 |
22 | // 验证输入
23 | if (!username || !password) {
24 | return res.status(400).json({
25 | success: false,
26 | message: '用户名和密码不能为空'
27 | });
28 | }
29 |
30 | // 创建用户
31 | const user = await User.createUser(username, password);
32 |
33 | // 生成令牌
34 | const token = generateToken(user);
35 |
36 | res.status(201).json({
37 | success: true,
38 | message: '用户注册成功',
39 | data: {
40 | user,
41 | token
42 | }
43 | });
44 | } catch (error) {
45 | res.status(400).json({
46 | success: false,
47 | message: error.message
48 | });
49 | }
50 | };
51 |
52 | // 用户登录
53 | exports.login = async (req, res) => {
54 | try {
55 | const { username, password } = req.body;
56 |
57 | // 验证输入
58 | if (!username || !password) {
59 | return res.status(400).json({
60 | success: false,
61 | message: '用户名和密码不能为空'
62 | });
63 | }
64 |
65 | // 验证用户
66 | const user = await User.validateUser(username, password);
67 |
68 | if (!user) {
69 | return res.status(401).json({
70 | success: false,
71 | message: '用户名或密码错误'
72 | });
73 | }
74 |
75 | // 生成令牌
76 | const token = generateToken(user);
77 |
78 | res.status(200).json({
79 | success: true,
80 | message: '登录成功',
81 | data: {
82 | user,
83 | token
84 | }
85 | });
86 | } catch (error) {
87 | res.status(500).json({
88 | success: false,
89 | message: '服务器错误',
90 | error: error.message
91 | });
92 | }
93 | };
94 |
95 | // 获取当前用户信息
96 | exports.getCurrentUser = async (req, res) => {
97 | try {
98 | // 用户信息已经在身份验证中间件中添加到req对象
99 | res.status(200).json({
100 | success: true,
101 | data: {
102 | user: req.user
103 | }
104 | });
105 | } catch (error) {
106 | res.status(500).json({
107 | success: false,
108 | message: '服务器错误',
109 | error: error.message
110 | });
111 | }
112 | };
113 |
114 | // 更新用户密码
115 | exports.updatePassword = async (req, res) => {
116 | try {
117 | const { currentPassword, newPassword } = req.body;
118 |
119 | // 验证输入
120 | if (!currentPassword || !newPassword) {
121 | return res.status(400).json({
122 | success: false,
123 | message: '当前密码和新密码不能为空'
124 | });
125 | }
126 |
127 | // 获取当前用户信息
128 | const userId = req.user.id;
129 | const username = req.user.username;
130 |
131 | // 验证当前密码
132 | const user = await User.validateUser(username, currentPassword);
133 |
134 | if (!user) {
135 | return res.status(401).json({
136 | success: false,
137 | message: '当前密码错误'
138 | });
139 | }
140 |
141 | // 更新密码
142 | await User.updatePassword(userId, newPassword);
143 |
144 | res.status(200).json({
145 | success: true,
146 | message: '密码更新成功'
147 | });
148 | } catch (error) {
149 | res.status(500).json({
150 | success: false,
151 | message: '更新密码失败',
152 | error: error.message
153 | });
154 | }
155 | };
--------------------------------------------------------------------------------
/server/services/cacheService.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const path = require('path');
3 | const util = require('util');
4 |
5 | const readFile = util.promisify(fs.readFile);
6 | const writeFile = util.promisify(fs.writeFile);
7 |
8 | const RULES_CACHE_PATH = path.join(__dirname, '../data/rules.json');
9 |
10 | /**
11 | * 读取规则缓存数据
12 | * @returns {Promise