feat: 增加防火墙开关及行状态切换功能

This commit is contained in:
ssongliu
2023-04-06 09:01:57 +08:00
committed by ssongliu
parent db74d010d7
commit c1c324af23
16 changed files with 1373 additions and 23 deletions
+45 -1
View File
@@ -8,6 +8,22 @@ import (
"github.com/gin-gonic/gin"
)
// @Tags Firewall
// @Summary Load firewall base info
// @Description 获取防火墙基础信息
// @Success 200 {object} dto.FirewallBaseInfo
// @Security ApiKeyAuth
// @Router /hosts/firewall/base [get]
func (b *BaseApi) LoadFirewallBaseInfo(c *gin.Context) {
data, err := firewallService.LoadBaseInfo()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, data)
}
// @Tags Firewall
// @Summary Page firewall rules
// @Description 获取防火墙规则列表分页
@@ -35,6 +51,34 @@ func (b *BaseApi) SearchFirewallRule(c *gin.Context) {
})
}
// @Tags Firewall
// @Summary Page firewall status
// @Description 修改防火墙状态
// @Accept json
// @Param request body dto.FirewallOperation true "request"
// @Success 200 {object} dto.PageResult
// @Security ApiKeyAuth
// @Router /hosts/firewall/operate [post]
// @x-panel-log {"bodyKeys":["operation"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"[operation] 防火墙","formatEN":"[operation] firewall"}
func (b *BaseApi) OperateFirewall(c *gin.Context) {
var req dto.FirewallOperation
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := global.VALID.Struct(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := firewallService.OperateFirewall(req.Operation); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Firewall
// @Summary Create group
// @Description 创建防火墙端口规则
@@ -65,7 +109,7 @@ func (b *BaseApi) OperatePortRule(c *gin.Context) {
// @Summary Create group
// @Description 创建防火墙 IP 规则
// @Accept json
// @Param request body dto.AddressCreate true "request"
// @Param request body dto.AddrRuleOperate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /hosts/firewall/ip [post]
+10
View File
@@ -1,11 +1,21 @@
package dto
type FirewallBaseInfo struct {
Name string `json:"name"`
Status string `json:"status"`
Version string `json:"version"`
}
type RuleSearch struct {
PageInfo
Info string `json:"info"`
Type string `json:"type" validate:"required"`
}
type FirewallOperation struct {
Operation string `json:"operation" validate:"required,oneof=start stop reload"`
}
type PortRuleOperate struct {
Operation string `json:"operation" validate:"required,oneof=add remove"`
Address string `json:"address"`
+41
View File
@@ -1,6 +1,7 @@
package service
import (
"fmt"
"strings"
"github.com/1Panel-dev/1Panel/backend/app/dto"
@@ -12,7 +13,9 @@ import (
type FirewallService struct{}
type IFirewallService interface {
LoadBaseInfo() (dto.FirewallBaseInfo, error)
SearchWithPage(search dto.RuleSearch) (int64, interface{}, error)
OperateFirewall(operation string) error
OperatePortRule(req dto.PortRuleOperate, reload bool) error
OperateAddressRule(req dto.AddrRuleOperate, reload bool) error
UpdatePortRule(req dto.PortRuleUpdate) error
@@ -24,6 +27,28 @@ func NewIFirewallService() IFirewallService {
return &FirewallService{}
}
func (u *FirewallService) LoadBaseInfo() (dto.FirewallBaseInfo, error) {
var baseInfo dto.FirewallBaseInfo
client, err := firewall.NewFirewallClient()
if err != nil {
return baseInfo, err
}
baseInfo.Name = client.Name()
baseInfo.Status, err = client.Status()
if err != nil {
return baseInfo, err
}
if baseInfo.Status == "not running" {
baseInfo.Version = "-"
return baseInfo, err
}
baseInfo.Version, err = client.Version()
if err != nil {
return baseInfo, err
}
return baseInfo, nil
}
func (u *FirewallService) SearchWithPage(req dto.RuleSearch) (int64, interface{}, error) {
var (
datas []fireClient.FireInfo
@@ -75,6 +100,22 @@ func (u *FirewallService) SearchWithPage(req dto.RuleSearch) (int64, interface{}
return int64(total), backDatas, nil
}
func (u *FirewallService) OperateFirewall(operation string) error {
client, err := firewall.NewFirewallClient()
if err != nil {
return err
}
switch operation {
case "start":
return client.Start()
case "stop":
return client.Stop()
case "reload":
return client.Reload()
}
return fmt.Errorf("not support such operation: %s", operation)
}
func (u *FirewallService) OperatePortRule(req dto.PortRuleOperate, reload bool) error {
client, err := firewall.NewFirewallClient()
if err != nil {
+2
View File
@@ -26,7 +26,9 @@ func (s *HostRouter) InitHostRouter(Router *gin.RouterGroup) {
hostRouter.POST("/test/byid/:id", baseApi.TestByID)
hostRouter.GET(":id", baseApi.GetHostInfo)
hostRouter.GET("/firewall/base", baseApi.LoadFirewallBaseInfo)
hostRouter.POST("/firewall/search", baseApi.SearchFirewallRule)
hostRouter.POST("/firewall/operate", baseApi.OperateFirewall)
hostRouter.POST("/firewall/port", baseApi.OperatePortRule)
hostRouter.POST("/firewall/ip", baseApi.OperateIPRule)
hostRouter.POST("/firewall/batch", baseApi.BatchOperateRule)
+3 -2
View File
@@ -10,6 +10,7 @@ type FirewallClient interface {
Stop() error
Reload() error
Status() (string, error)
Version() (string, error)
ListPort() ([]client.FireInfo, error)
ListAddress() ([]client.FireInfo, error)
@@ -20,10 +21,10 @@ type FirewallClient interface {
func NewFirewallClient() (FirewallClient, error) {
// if _, err := os.Stat("/usr/sbin/firewalld"); err == nil {
// return client.NewFirewalld()
return client.NewFirewalld()
// }
// if _, err := os.Stat("/usr/sbin/ufw"); err == nil {
return client.NewUfw()
// return client.NewUfw()
// }
// return nil, errors.New("no such type")
}
+9 -4
View File
@@ -26,13 +26,18 @@ func (f *Firewall) Name() string {
}
func (f *Firewall) Status() (string, error) {
stdout, err := f.Client.Run("firewall-cmd --state")
if err != nil {
return "", fmt.Errorf("load the firewall status failed, err: %s", stdout)
}
stdout, _ := f.Client.Run("firewall-cmd --state")
return strings.ReplaceAll(stdout, "\n", ""), nil
}
func (f *Firewall) Version() (string, error) {
stdout, err := f.Client.Run("firewall-cmd --version")
if err != nil {
return "", fmt.Errorf("load the firewall version failed, err: %s", stdout)
}
return strings.ReplaceAll(stdout, "\n ", ""), nil
}
func (f *Firewall) Start() error {
stdout, err := f.Client.Run("systemctl start firewalld")
if err != nil {
+24 -3
View File
@@ -4,6 +4,7 @@ import (
"fmt"
"strings"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/utils/ssh"
)
@@ -26,24 +27,44 @@ func (f *Ufw) Name() string {
}
func (f *Ufw) Status() (string, error) {
stdout, err := f.Client.Run("sudo ufw status")
stdout, err := f.Client.Run("sudo ufw status | grep Status")
if err != nil {
return "", fmt.Errorf("load the firewall status failed, err: %s", stdout)
}
if stdout == "Status: inactive\n" {
if stdout == "Status: active\n" {
return "running", nil
}
return "not running", nil
}
func (f *Ufw) Version() (string, error) {
stdout, err := f.Client.Run("sudo ufw version | grep ufw")
if err != nil {
return "", fmt.Errorf("load the firewall status failed, err: %s", stdout)
}
info := strings.ReplaceAll(stdout, "\n", "")
return strings.ReplaceAll(info, "ufw ", ""), nil
}
func (f *Ufw) Start() error {
stdout, err := f.Client.Run("sudo ufw enable")
stdout, err := f.Client.Run("echo y | sudo ufw enable")
if err != nil {
return fmt.Errorf("enable the firewall failed, err: %s", stdout)
}
return nil
}
func (f *Ufw) PingStatus() (string, error) {
stdout, err := f.Client.Run("cat /etc/ufw/sysctl.conf | grep net/ipv4/icmp_echo_ignore_all= ")
if err != nil {
return constant.StatusDisable, fmt.Errorf("enable the firewall failed, err: %s", stdout)
}
if stdout == "net/ipv4/icmp_echo_ignore_all=1\n" {
return constant.StatusEnable, nil
}
return constant.StatusDisable, nil
}
func (f *Ufw) Stop() error {
stdout, err := f.Client.Run("sudo ufw disable")
if err != nil {
+380
View File
@@ -5066,6 +5066,251 @@ var doc = `{
}
}
},
"/hosts/firewall/base": {
"get": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "获取防火墙基础信息",
"tags": [
"Firewall"
],
"summary": "Load firewall base info",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/dto.FirewallBaseInfo"
}
}
}
}
},
"/hosts/firewall/ip": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "批量删除防火墙规则",
"consumes": [
"application/json"
],
"tags": [
"Firewall"
],
"summary": "Create group",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.BatchRuleOperate"
}
}
],
"responses": {
"200": {
"description": ""
}
}
}
},
"/hosts/firewall/operate": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "修改防火墙状态",
"consumes": [
"application/json"
],
"tags": [
"Firewall"
],
"summary": "Page firewall status",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.FirewallOperation"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/dto.PageResult"
}
}
},
"x-panel-log": {
"BeforeFuntions": [],
"bodyKeys": [
"operation"
],
"formatEN": "[operation] firewall",
"formatZH": "[operation] 防火墙",
"paramKeys": []
}
}
},
"/hosts/firewall/port": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "创建防火墙端口规则",
"consumes": [
"application/json"
],
"tags": [
"Firewall"
],
"summary": "Create group",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.PortRuleOperate"
}
}
],
"responses": {
"200": {
"description": ""
}
},
"x-panel-log": {
"BeforeFuntions": [],
"bodyKeys": [
"port",
"strategy"
],
"formatEN": "create port rules {[strategy][port]}",
"formatZH": "添加端口规则 {[strategy] [port]}",
"paramKeys": []
}
}
},
"/hosts/firewall/search": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "获取防火墙规则列表分页",
"consumes": [
"application/json"
],
"tags": [
"Firewall"
],
"summary": "Page firewall rules",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.SearchWithPage"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/dto.PageResult"
}
}
}
}
},
"/hosts/firewall/update/ip": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "更新 ip 防火墙规则",
"consumes": [
"application/json"
],
"tags": [
"Firewall"
],
"summary": "Create group",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.AddrRuleUpdate"
}
}
],
"responses": {
"200": {
"description": ""
}
}
}
},
"/hosts/firewall/update/port": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "更新端口防火墙规则",
"consumes": [
"application/json"
],
"tags": [
"Firewall"
],
"summary": "Create group",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.PortRuleUpdate"
}
}
],
"responses": {
"200": {
"description": ""
}
}
}
},
"/hosts/group": {
"post": {
"security": [
@@ -8512,6 +8757,44 @@ var doc = `{
}
},
"definitions": {
"dto.AddrRuleOperate": {
"type": "object",
"required": [
"address",
"operation",
"strategy"
],
"properties": {
"address": {
"type": "string"
},
"operation": {
"type": "string",
"enum": [
"add",
"remove"
]
},
"strategy": {
"type": "string",
"enum": [
"accept",
"drop"
]
}
}
},
"dto.AddrRuleUpdate": {
"type": "object",
"properties": {
"newRule": {
"$ref": "#/definitions/dto.AddrRuleOperate"
},
"oldRule": {
"$ref": "#/definitions/dto.AddrRuleOperate"
}
}
},
"dto.BackupOperate": {
"type": "object",
"required": [
@@ -8578,6 +8861,23 @@ var doc = `{
}
}
},
"dto.BatchRuleOperate": {
"type": "object",
"required": [
"type"
],
"properties": {
"rules": {
"type": "array",
"items": {
"$ref": "#/definitions/dto.PortRuleOperate"
}
},
"type": {
"type": "string"
}
}
},
"dto.CaptchaResponse": {
"type": "object",
"properties": {
@@ -9380,6 +9680,36 @@ var doc = `{
}
}
},
"dto.FirewallBaseInfo": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"status": {
"type": "string"
},
"version": {
"type": "string"
}
}
},
"dto.FirewallOperation": {
"type": "object",
"required": [
"operation"
],
"properties": {
"operation": {
"type": "string",
"enum": [
"start",
"stop",
"reload"
]
}
}
},
"dto.ForBuckets": {
"type": "object",
"required": [
@@ -10142,6 +10472,56 @@ var doc = `{
}
}
},
"dto.PortRuleOperate": {
"type": "object",
"required": [
"operation",
"port",
"protocol",
"strategy"
],
"properties": {
"address": {
"type": "string"
},
"operation": {
"type": "string",
"enum": [
"add",
"remove"
]
},
"port": {
"type": "string"
},
"protocol": {
"type": "string",
"enum": [
"tcp",
"udp",
"tcp/udp"
]
},
"strategy": {
"type": "string",
"enum": [
"accept",
"drop"
]
}
}
},
"dto.PortRuleUpdate": {
"type": "object",
"properties": {
"newRule": {
"$ref": "#/definitions/dto.PortRuleOperate"
},
"oldRule": {
"$ref": "#/definitions/dto.PortRuleOperate"
}
}
},
"dto.PortUpdate": {
"type": "object",
"required": [
+380
View File
@@ -5052,6 +5052,251 @@
}
}
},
"/hosts/firewall/base": {
"get": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "获取防火墙基础信息",
"tags": [
"Firewall"
],
"summary": "Load firewall base info",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/dto.FirewallBaseInfo"
}
}
}
}
},
"/hosts/firewall/ip": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "批量删除防火墙规则",
"consumes": [
"application/json"
],
"tags": [
"Firewall"
],
"summary": "Create group",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.BatchRuleOperate"
}
}
],
"responses": {
"200": {
"description": ""
}
}
}
},
"/hosts/firewall/operate": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "修改防火墙状态",
"consumes": [
"application/json"
],
"tags": [
"Firewall"
],
"summary": "Page firewall status",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.FirewallOperation"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/dto.PageResult"
}
}
},
"x-panel-log": {
"BeforeFuntions": [],
"bodyKeys": [
"operation"
],
"formatEN": "[operation] firewall",
"formatZH": "[operation] 防火墙",
"paramKeys": []
}
}
},
"/hosts/firewall/port": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "创建防火墙端口规则",
"consumes": [
"application/json"
],
"tags": [
"Firewall"
],
"summary": "Create group",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.PortRuleOperate"
}
}
],
"responses": {
"200": {
"description": ""
}
},
"x-panel-log": {
"BeforeFuntions": [],
"bodyKeys": [
"port",
"strategy"
],
"formatEN": "create port rules {[strategy][port]}",
"formatZH": "添加端口规则 {[strategy] [port]}",
"paramKeys": []
}
}
},
"/hosts/firewall/search": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "获取防火墙规则列表分页",
"consumes": [
"application/json"
],
"tags": [
"Firewall"
],
"summary": "Page firewall rules",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.SearchWithPage"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/dto.PageResult"
}
}
}
}
},
"/hosts/firewall/update/ip": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "更新 ip 防火墙规则",
"consumes": [
"application/json"
],
"tags": [
"Firewall"
],
"summary": "Create group",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.AddrRuleUpdate"
}
}
],
"responses": {
"200": {
"description": ""
}
}
}
},
"/hosts/firewall/update/port": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "更新端口防火墙规则",
"consumes": [
"application/json"
],
"tags": [
"Firewall"
],
"summary": "Create group",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.PortRuleUpdate"
}
}
],
"responses": {
"200": {
"description": ""
}
}
}
},
"/hosts/group": {
"post": {
"security": [
@@ -8498,6 +8743,44 @@
}
},
"definitions": {
"dto.AddrRuleOperate": {
"type": "object",
"required": [
"address",
"operation",
"strategy"
],
"properties": {
"address": {
"type": "string"
},
"operation": {
"type": "string",
"enum": [
"add",
"remove"
]
},
"strategy": {
"type": "string",
"enum": [
"accept",
"drop"
]
}
}
},
"dto.AddrRuleUpdate": {
"type": "object",
"properties": {
"newRule": {
"$ref": "#/definitions/dto.AddrRuleOperate"
},
"oldRule": {
"$ref": "#/definitions/dto.AddrRuleOperate"
}
}
},
"dto.BackupOperate": {
"type": "object",
"required": [
@@ -8564,6 +8847,23 @@
}
}
},
"dto.BatchRuleOperate": {
"type": "object",
"required": [
"type"
],
"properties": {
"rules": {
"type": "array",
"items": {
"$ref": "#/definitions/dto.PortRuleOperate"
}
},
"type": {
"type": "string"
}
}
},
"dto.CaptchaResponse": {
"type": "object",
"properties": {
@@ -9366,6 +9666,36 @@
}
}
},
"dto.FirewallBaseInfo": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"status": {
"type": "string"
},
"version": {
"type": "string"
}
}
},
"dto.FirewallOperation": {
"type": "object",
"required": [
"operation"
],
"properties": {
"operation": {
"type": "string",
"enum": [
"start",
"stop",
"reload"
]
}
}
},
"dto.ForBuckets": {
"type": "object",
"required": [
@@ -10128,6 +10458,56 @@
}
}
},
"dto.PortRuleOperate": {
"type": "object",
"required": [
"operation",
"port",
"protocol",
"strategy"
],
"properties": {
"address": {
"type": "string"
},
"operation": {
"type": "string",
"enum": [
"add",
"remove"
]
},
"port": {
"type": "string"
},
"protocol": {
"type": "string",
"enum": [
"tcp",
"udp",
"tcp/udp"
]
},
"strategy": {
"type": "string",
"enum": [
"accept",
"drop"
]
}
}
},
"dto.PortRuleUpdate": {
"type": "object",
"properties": {
"newRule": {
"$ref": "#/definitions/dto.PortRuleOperate"
},
"oldRule": {
"$ref": "#/definitions/dto.PortRuleOperate"
}
}
},
"dto.PortUpdate": {
"type": "object",
"required": [
+244
View File
@@ -1,5 +1,31 @@
basePath: /api/v1
definitions:
dto.AddrRuleOperate:
properties:
address:
type: string
operation:
enum:
- add
- remove
type: string
strategy:
enum:
- accept
- drop
type: string
required:
- address
- operation
- strategy
type: object
dto.AddrRuleUpdate:
properties:
newRule:
$ref: '#/definitions/dto.AddrRuleOperate'
oldRule:
$ref: '#/definitions/dto.AddrRuleOperate'
type: object
dto.BackupOperate:
properties:
accessKey:
@@ -43,6 +69,17 @@ definitions:
required:
- ids
type: object
dto.BatchRuleOperate:
properties:
rules:
items:
$ref: '#/definitions/dto.PortRuleOperate'
type: array
type:
type: string
required:
- type
type: object
dto.CaptchaResponse:
properties:
captchaID:
@@ -583,6 +620,26 @@ definitions:
required:
- path
type: object
dto.FirewallBaseInfo:
properties:
name:
type: string
status:
type: string
version:
type: string
type: object
dto.FirewallOperation:
properties:
operation:
enum:
- start
- stop
- reload
type: string
required:
- operation
type: object
dto.ForBuckets:
properties:
accessKey:
@@ -1091,6 +1148,41 @@ definitions:
hostPort:
type: integer
type: object
dto.PortRuleOperate:
properties:
address:
type: string
operation:
enum:
- add
- remove
type: string
port:
type: string
protocol:
enum:
- tcp
- udp
- tcp/udp
type: string
strategy:
enum:
- accept
- drop
type: string
required:
- operation
- port
- protocol
- strategy
type: object
dto.PortRuleUpdate:
properties:
newRule:
$ref: '#/definitions/dto.PortRuleOperate'
oldRule:
$ref: '#/definitions/dto.PortRuleOperate'
type: object
dto.PortUpdate:
properties:
serverPort:
@@ -5820,6 +5912,158 @@ paths:
formatEN: delete host [addrs]
formatZH: 删除主机 [addrs]
paramKeys: []
/hosts/firewall/base:
get:
description: 获取防火墙基础信息
responses:
"200":
description: OK
schema:
$ref: '#/definitions/dto.FirewallBaseInfo'
security:
- ApiKeyAuth: []
summary: Load firewall base info
tags:
- Firewall
/hosts/firewall/ip:
post:
consumes:
- application/json
description: 批量删除防火墙规则
parameters:
- description: request
in: body
name: request
required: true
schema:
$ref: '#/definitions/dto.BatchRuleOperate'
responses:
"200":
description: ""
security:
- ApiKeyAuth: []
summary: Create group
tags:
- Firewall
/hosts/firewall/operate:
post:
consumes:
- application/json
description: 修改防火墙状态
parameters:
- description: request
in: body
name: request
required: true
schema:
$ref: '#/definitions/dto.FirewallOperation'
responses:
"200":
description: OK
schema:
$ref: '#/definitions/dto.PageResult'
security:
- ApiKeyAuth: []
summary: Page firewall status
tags:
- Firewall
x-panel-log:
BeforeFuntions: []
bodyKeys:
- operation
formatEN: '[operation] firewall'
formatZH: '[operation] 防火墙'
paramKeys: []
/hosts/firewall/port:
post:
consumes:
- application/json
description: 创建防火墙端口规则
parameters:
- description: request
in: body
name: request
required: true
schema:
$ref: '#/definitions/dto.PortRuleOperate'
responses:
"200":
description: ""
security:
- ApiKeyAuth: []
summary: Create group
tags:
- Firewall
x-panel-log:
BeforeFuntions: []
bodyKeys:
- port
- strategy
formatEN: create port rules {[strategy][port]}
formatZH: 添加端口规则 {[strategy] [port]}
paramKeys: []
/hosts/firewall/search:
post:
consumes:
- application/json
description: 获取防火墙规则列表分页
parameters:
- description: request
in: body
name: request
required: true
schema:
$ref: '#/definitions/dto.SearchWithPage'
responses:
"200":
description: OK
schema:
$ref: '#/definitions/dto.PageResult'
security:
- ApiKeyAuth: []
summary: Page firewall rules
tags:
- Firewall
/hosts/firewall/update/ip:
post:
consumes:
- application/json
description: 更新 ip 防火墙规则
parameters:
- description: request
in: body
name: request
required: true
schema:
$ref: '#/definitions/dto.AddrRuleUpdate'
responses:
"200":
description: ""
security:
- ApiKeyAuth: []
summary: Create group
tags:
- Firewall
/hosts/firewall/update/port:
post:
consumes:
- application/json
description: 更新端口防火墙规则
parameters:
- description: request
in: body
name: request
required: true
schema:
$ref: '#/definitions/dto.PortRuleUpdate'
responses:
"200":
description: ""
security:
- ApiKeyAuth: []
summary: Create group
tags:
- Firewall
/hosts/group:
post:
consumes:
+5
View File
@@ -53,6 +53,11 @@ export namespace Host {
info?: string;
}
export interface FirewallBase {
name: string;
status: string;
version: string;
}
export interface RuleSearch extends ReqPage {
info: string;
type: string;
+6
View File
@@ -72,9 +72,15 @@ export const deleteCommand = (params: { ids: number[] }) => {
};
// firewall
export const loadFireBaseInfo = () => {
return http.get<Host.FirewallBase>(`/hosts/firewall/base`);
};
export const searchFireRule = (params: Host.RuleSearch) => {
return http.post<ResPage<Host.RuleInfo>>(`/hosts/firewall/search`, params);
};
export const operateFire = (operation: string) => {
return http.post(`/hosts/firewall/operate`, { operation: operation });
};
export const operatePortRule = (params: Host.RulePort) => {
return http.post<Host.RulePort>(`/hosts/firewall/port`, params);
};
+10
View File
@@ -22,6 +22,8 @@ const message = {
clean: '清空',
login: '登录',
close: '关闭',
stop: '关闭',
start: '开启',
view: '详情',
watch: '追踪',
handle: '执行',
@@ -1182,8 +1184,14 @@ const message = {
cookieBlockList: 'Cookie 黑名单',
firewall: '防火墙',
firewallHelper: '{0}系统防火墙',
firewallNotStart: '当前未开启防火墙服务,请先开启!',
stopFirewallHelper: '停用系统防火墙,服务器将失去安全防护,是否继续操作?',
startFirewallHelper: '启用系统防火墙后,可以更好的防护当前的服务器安全,是否继续操作?',
protocol: '协议',
port: '端口',
changeStrategy: '修改{0}策略',
changeStrategyHelper: '修改 [{1}] {0}策略为 [{2}],设置后该{0}将{2}外部访问,是否继续操作?',
portHelper: '支持输入多个端口,如 80,81 或者范围端口,如 80-88',
strategy: '策略',
accept: '允许',
@@ -1191,6 +1199,8 @@ const message = {
source: '来源',
anyWhere: '所有 IP',
address: '指定 IP',
allow: '放行',
deny: '屏蔽',
addressHelper1: '支持输入多个 IP ,如 172.16.10.11,172.16.10.99',
addressHelper2: '支持输入 IP 段,如 172.16.10.0/24',
addressHelper3: '支持输入 IP 范围,如 172.16.10.11-172.16.10.99',
+62 -7
View File
@@ -1,7 +1,13 @@
<template>
<div>
<div v-loading="loading">
<FireRouter />
<LayoutContent v-loading="loading" :title="$t('firewall.firewall')">
<FireStatus ref="fireStatuRef" @search="search" v-model:loading="loading" v-model:status="fireStatus" />
<el-card v-if="fireStatus != 'running'" class="mask-prompt">
<span>{{ $t('firewall.firewallNotStart') }}</span>
</el-card>
<LayoutContent :title="$t('firewall.ipRule')" :class="{ mask: fireStatus != 'running' }">
<template #toolbar>
<el-row>
<el-col :span="16">
@@ -44,8 +50,17 @@
</el-table-column>
<el-table-column :min-width="80" :label="$t('firewall.strategy')" prop="strategy">
<template #default="{ row }">
<el-tag v-if="row.strategy === 'accept'" type="success">{{ $t('firewall.accept') }}</el-tag>
<el-tag v-if="row.strategy === 'drop'" type="danger">{{ $t('firewall.drop') }}</el-tag>
<el-button
v-if="row.strategy === 'accept'"
@click="onChangeStatus(row, 'drop')"
link
type="success"
>
{{ $t('firewall.allow') }}
</el-button>
<el-button v-else link type="danger" @click="onChangeStatus(row, 'accept')">
{{ $t('firewall.deny') }}
</el-button>
</template>
</el-table-column>
<fu-table-operations
@@ -68,9 +83,10 @@ import ComplexTable from '@/components/complex-table/index.vue';
import OperatrDialog from '@/views/host/firewall/ip/operate/index.vue';
import FireRouter from '@/views/host/firewall/index.vue';
import TableSetting from '@/components/table-setting/index.vue';
import FireStatus from '@/views/host/firewall/status/index.vue';
import LayoutContent from '@/layout/layout-content.vue';
import { onMounted, reactive, ref } from 'vue';
import { batchOperateRule, searchFireRule } from '@/api/modules/host';
import { batchOperateRule, searchFireRule, updateAddrRule } from '@/api/modules/host';
import { Host } from '@/api/interface/host';
import { ElMessageBox } from 'element-plus';
import i18n from '@/lang';
@@ -81,6 +97,9 @@ const activeTag = ref('address');
const selects = ref<any>([]);
const searchName = ref();
const fireStatus = ref('running');
const fireStatuRef = ref();
const data = ref();
const paginationConfig = reactive({
currentPage: 1,
@@ -91,7 +110,7 @@ const paginationConfig = reactive({
const search = async () => {
let params = {
type: activeTag.value,
info: '',
info: searchName.value,
page: paginationConfig.currentPage,
pageSize: paginationConfig.pageSize,
};
@@ -121,6 +140,41 @@ const onOpenDialog = async (
dialogRef.value!.acceptParams(params);
};
const onChangeStatus = async (row: Host.RuleInfo, status: string) => {
let operation = status === 'accept' ? i18n.global.t('firewall.allow') : i18n.global.t('firewall.deny');
ElMessageBox.confirm(
i18n.global.t('firewall.changeStrategyHelper', ['IP', row.address, operation]),
i18n.global.t('firewall.changeStrategy', ['IP']),
{
confirmButtonText: i18n.global.t('commons.button.confirm'),
cancelButtonText: i18n.global.t('commons.button.cancel'),
},
).then(async () => {
let params = {
oldRule: {
operation: 'remove',
address: row.address,
strategy: row.strategy,
},
newRule: {
operation: 'add',
address: row.address,
strategy: status,
},
};
loading.value = true;
await updateAddrRule(params)
.then(() => {
loading.value = false;
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
search();
})
.catch(() => {
loading.value = false;
});
});
};
const onDelete = async (row: Host.RuleIP | null) => {
ElMessageBox.confirm(i18n.global.t('commons.msg.delete'), i18n.global.t('commons.msg.deleteTitle'), {
confirmButtonText: i18n.global.t('commons.button.confirm'),
@@ -178,6 +232,7 @@ const buttons = [
];
onMounted(() => {
search();
loading.value = true;
fireStatuRef.value.acceptParams();
});
</script>
@@ -1,7 +1,14 @@
<template>
<div>
<div v-loading="loading">
<FireRouter />
<LayoutContent v-loading="loading" :title="$t('firewall.firewall')">
<FireStatus ref="fireStatuRef" @search="search" v-model:loading="loading" v-model:status="fireStatus" />
<el-card v-if="fireStatus != 'running'" class="mask-prompt">
<span>{{ $t('firewall.firewallNotStart') }}</span>
</el-card>
<LayoutContent :title="$t('firewall.portRule')" :class="{ mask: fireStatus != 'running' }">
<template #toolbar>
<el-row>
<el-col :span="16">
@@ -40,8 +47,17 @@
<el-table-column :label="$t('firewall.port')" :min-width="120" prop="port" />
<el-table-column :min-width="80" :label="$t('firewall.strategy')" prop="strategy">
<template #default="{ row }">
<el-tag v-if="row.strategy === 'accept'" type="success">{{ $t('firewall.accept') }}</el-tag>
<el-tag v-if="row.strategy === 'drop'" type="danger">{{ $t('firewall.drop') }}</el-tag>
<el-button
v-if="row.strategy === 'accept'"
@click="onChangeStatus(row, 'drop')"
link
type="success"
>
{{ $t('firewall.accept') }}
</el-button>
<el-button v-else link type="danger" @click="onChangeStatus(row, 'accept')">
{{ $t('firewall.drop') }}
</el-button>
</template>
</el-table-column>
<el-table-column :min-width="80" :label="$t('firewall.address')" prop="address">
@@ -70,9 +86,10 @@ import ComplexTable from '@/components/complex-table/index.vue';
import FireRouter from '@/views/host/firewall/index.vue';
import TableSetting from '@/components/table-setting/index.vue';
import OperatrDialog from '@/views/host/firewall/port/operate/index.vue';
import FireStatus from '@/views/host/firewall/status/index.vue';
import LayoutContent from '@/layout/layout-content.vue';
import { onMounted, reactive, ref } from 'vue';
import { batchOperateRule, searchFireRule } from '@/api/modules/host';
import { batchOperateRule, searchFireRule, updatePortRule } from '@/api/modules/host';
import { Host } from '@/api/interface/host';
import i18n from '@/lang';
import { MsgSuccess } from '@/utils/message';
@@ -83,6 +100,9 @@ const activeTag = ref('port');
const selects = ref<any>([]);
const searchName = ref();
const fireStatus = ref('running');
const fireStatuRef = ref();
const data = ref();
const paginationConfig = reactive({
currentPage: 1,
@@ -125,6 +145,47 @@ const onOpenDialog = async (
dialogRef.value!.acceptParams(params);
};
const onChangeStatus = async (row: Host.RuleInfo, status: string) => {
let operation = i18n.global.t('firewall.' + status);
ElMessageBox.confirm(
i18n.global.t('firewall.changeStrategyHelper', [i18n.global.t('firewall.port'), row.port, operation]),
i18n.global.t('firewall.changeStrategy', [i18n.global.t('firewall.port')]),
{
confirmButtonText: i18n.global.t('commons.button.confirm'),
cancelButtonText: i18n.global.t('commons.button.cancel'),
},
).then(async () => {
let params = {
oldRule: {
operation: 'remove',
address: row.address,
port: row.port,
source: '',
protocol: row.protocol,
strategy: row.strategy,
},
newRule: {
operation: 'add',
address: row.address,
port: row.port,
source: '',
protocol: row.protocol,
strategy: status,
},
};
loading.value = true;
await updatePortRule(params)
.then(() => {
loading.value = false;
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
search();
})
.catch(() => {
loading.value = false;
});
});
};
const onDelete = async (row: Host.RuleInfo | null) => {
ElMessageBox.confirm(i18n.global.t('commons.msg.delete'), i18n.global.t('commons.msg.deleteTitle'), {
confirmButtonText: i18n.global.t('commons.button.confirm'),
@@ -182,6 +243,7 @@ const buttons = [
];
onMounted(() => {
search();
loading.value = true;
fireStatuRef.value.acceptParams();
});
</script>
@@ -0,0 +1,84 @@
<template>
<div>
<div class="app-status" style="margin-top: 20px">
<el-card>
<div>
<el-tag style="float: left" effect="dark" type="success">{{ baseInfo.name }}</el-tag>
<el-tag round class="status-content" v-if="baseInfo.status === 'running'" type="success">
{{ $t('commons.status.running') }}
</el-tag>
<el-tag round class="status-content" v-if="baseInfo.status === 'not running'" type="info">
{{ $t('commons.status.stopped') }}
</el-tag>
<el-tag class="status-content">{{ $t('app.version') }}: {{ baseInfo.version }}</el-tag>
<span v-if="baseInfo.status === 'running'" class="buttons">
<el-button type="primary" @click="onOperate('stop')" link>
{{ $t('commons.button.stop') }}
</el-button>
</span>
<span v-if="baseInfo.status === 'not running'" class="buttons">
<el-button type="primary" @click="onOperate('start')" link>
{{ $t('commons.button.start') }}
</el-button>
</span>
</div>
</el-card>
</div>
</div>
</template>
<script lang="ts" setup>
import { Host } from '@/api/interface/host';
import { loadFireBaseInfo, operateFire } from '@/api/modules/host';
import i18n from '@/lang';
import { ElMessageBox } from 'element-plus';
import { ref } from 'vue';
const baseInfo = ref<Host.FirewallBase>({ status: '', name: '', version: '' });
const acceptParams = (): void => {
loadBaseInfo();
};
const emit = defineEmits(['search', 'update:status', 'update:loading']);
const loadBaseInfo = async () => {
await loadFireBaseInfo()
.then((res) => {
baseInfo.value = res.data;
emit('update:status', baseInfo.value.status);
if (baseInfo.value.status === 'running') {
emit('search');
} else {
emit('update:loading', false);
}
})
.catch(() => {
emit('update:loading', false);
});
};
const onOperate = async (operation: string) => {
let operationHelper = i18n.global.t('firewall.' + operation + 'FirewallHelper');
let title = i18n.global.t('firewall.firewallHelper', [i18n.global.t('commons.button.' + operation)]);
ElMessageBox.confirm(operationHelper, title, {
confirmButtonText: i18n.global.t('commons.button.confirm'),
cancelButtonText: i18n.global.t('commons.button.cancel'),
}).then(async () => {
emit('update:loading', true);
emit('update:status', 'running');
await operateFire(operation)
.then(() => {
loadBaseInfo();
})
.catch(() => {
emit('update:loading', false);
});
});
};
defineExpose({
acceptParams,
});
</script>