feat: 实现禁 ping 功能

This commit is contained in:
ssongliu 2023-03-30 18:03:21 +08:00 committed by ssongliu
parent c1c324af23
commit 1c5d01b11c
12 changed files with 191 additions and 67 deletions

View File

@ -4,6 +4,7 @@ type FirewallBaseInfo struct {
Name string `json:"name"`
Status string `json:"status"`
Version string `json:"version"`
PingStatus string `json:"pingStatus"`
}
type RuleSearch struct {
@ -13,7 +14,7 @@ type RuleSearch struct {
}
type FirewallOperation struct {
Operation string `json:"operation" validate:"required,oneof=start stop reload"`
Operation string `json:"operation" validate:"required,oneof=start stop disablePing enablePing"`
}
type PortRuleOperate struct {

View File

@ -38,6 +38,10 @@ func (u *FirewallService) LoadBaseInfo() (dto.FirewallBaseInfo, error) {
if err != nil {
return baseInfo, err
}
baseInfo.PingStatus, err = client.PingStatus()
if err != nil {
return baseInfo, err
}
if baseInfo.Status == "not running" {
baseInfo.Version = "-"
return baseInfo, err
@ -110,8 +114,10 @@ func (u *FirewallService) OperateFirewall(operation string) error {
return client.Start()
case "stop":
return client.Stop()
case "reload":
return client.Reload()
case "disablePing":
return client.UpdatePingStatus("0")
case "enablePing":
return client.UpdatePingStatus("1")
}
return fmt.Errorf("not support such operation: %s", operation)
}

View File

@ -1,16 +1,23 @@
package firewall
import (
"errors"
"os"
"github.com/1Panel-dev/1Panel/backend/utils/firewall/client"
)
type FirewallClient interface {
Name() string
Name() string // ufw firewalld
Start() error
Stop() error
Reload() error
Status() (string, error)
Status() (string, error) // running not running
Version() (string, error)
PingStatus() (string, error) // Enable Disable
UpdatePingStatus(enabel string) error
ListPort() ([]client.FireInfo, error)
ListAddress() ([]client.FireInfo, error)
@ -20,11 +27,11 @@ type FirewallClient interface {
}
func NewFirewallClient() (FirewallClient, error) {
// if _, err := os.Stat("/usr/sbin/firewalld"); err == nil {
if _, err := os.Stat("/usr/sbin/firewalld"); err == nil {
return client.NewFirewalld()
// }
// if _, err := os.Stat("/usr/sbin/ufw"); err == nil {
// return client.NewUfw()
// }
// return nil, errors.New("no such type")
}
if _, err := os.Stat("/usr/sbin/ufw"); err == nil {
return client.NewUfw()
}
return nil, errors.New("no such type")
}

View File

@ -4,21 +4,14 @@ import (
"fmt"
"strings"
"github.com/1Panel-dev/1Panel/backend/utils/ssh"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
)
type Firewall struct {
Client ssh.ConnInfo
}
type Firewall struct{}
func NewFirewalld() (*Firewall, error) {
ConnInfo := ssh.ConnInfo{
Addr: "172.16.10.143",
User: "root",
AuthMode: "password",
Port: 22,
}
return &Firewall{Client: ConnInfo}, nil
return &Firewall{}, nil
}
func (f *Firewall) Name() string {
@ -26,12 +19,12 @@ func (f *Firewall) Name() string {
}
func (f *Firewall) Status() (string, error) {
stdout, _ := f.Client.Run("firewall-cmd --state")
stdout, _ := cmd.Exec("firewall-cmd --state")
return strings.ReplaceAll(stdout, "\n", ""), nil
}
func (f *Firewall) Version() (string, error) {
stdout, err := f.Client.Run("firewall-cmd --version")
stdout, err := cmd.Exec("firewall-cmd --version")
if err != nil {
return "", fmt.Errorf("load the firewall version failed, err: %s", stdout)
}
@ -39,15 +32,35 @@ func (f *Firewall) Version() (string, error) {
}
func (f *Firewall) Start() error {
stdout, err := f.Client.Run("systemctl start firewalld")
stdout, err := cmd.Exec("systemctl start firewalld")
if err != nil {
return fmt.Errorf("enable the firewall failed, err: %s", stdout)
}
return nil
}
func (f *Firewall) PingStatus() (string, error) {
stdout, _ := cmd.Exec("firewall-cmd --query-rich-rule='rule protocol value=icmp drop'")
if stdout == "yes\n" {
return constant.StatusEnable, nil
}
return constant.StatusDisable, nil
}
func (f *Firewall) UpdatePingStatus(enabel string) error {
operation := "add"
if enabel == "0" {
operation = "remove"
}
stdout, err := cmd.Execf("firewall-cmd --permanent --%s-rich-rule='rule protocol value=icmp drop'", operation)
if err != nil {
return fmt.Errorf("update firewall ping status failed, err: %s", stdout)
}
return f.Reload()
}
func (f *Firewall) Stop() error {
stdout, err := f.Client.Run("systemctl stop firewalld")
stdout, err := cmd.Exec("systemctl stop firewalld")
if err != nil {
return fmt.Errorf("stop the firewall failed, err: %s", stdout)
}
@ -55,7 +68,7 @@ func (f *Firewall) Stop() error {
}
func (f *Firewall) Reload() error {
stdout, err := f.Client.Run("firewall-cmd --reload")
stdout, err := cmd.Exec("firewall-cmd --reload")
if err != nil {
return fmt.Errorf("reload firewall failed, err: %s", stdout)
}
@ -63,7 +76,7 @@ func (f *Firewall) Reload() error {
}
func (f *Firewall) ListPort() ([]FireInfo, error) {
stdout, err := f.Client.Run("firewall-cmd --zone=public --list-ports")
stdout, err := cmd.Exec("firewall-cmd --zone=public --list-ports")
if err != nil {
return nil, err
}
@ -79,7 +92,7 @@ func (f *Firewall) ListPort() ([]FireInfo, error) {
datas = append(datas, itemPort)
}
stdout1, err := f.Client.Run("firewall-cmd --zone=public --list-rich-rules")
stdout1, err := cmd.Exec("firewall-cmd --zone=public --list-rich-rules")
if err != nil {
return nil, err
}
@ -97,7 +110,7 @@ func (f *Firewall) ListPort() ([]FireInfo, error) {
}
func (f *Firewall) ListAddress() ([]FireInfo, error) {
stdout, err := f.Client.Run("firewall-cmd --zone=public --list-rich-rules")
stdout, err := cmd.Exec("firewall-cmd --zone=public --list-rich-rules")
if err != nil {
return nil, err
}
@ -116,7 +129,7 @@ func (f *Firewall) ListAddress() ([]FireInfo, error) {
}
func (f *Firewall) Port(port FireInfo, operation string) error {
stdout, err := f.Client.Run(fmt.Sprintf("firewall-cmd --zone=public --%s-port=%s/%s --permanent", operation, port.Port, port.Protocol))
stdout, err := cmd.Execf("firewall-cmd --zone=public --%s-port=%s/%s --permanent", operation, port.Port, port.Protocol)
if err != nil {
return fmt.Errorf("%s port failed, err: %s", operation, stdout)
}
@ -136,7 +149,7 @@ func (f *Firewall) RichRules(rule FireInfo, operation string) error {
}
ruleStr += rule.Strategy
stdout, err := f.Client.Run(fmt.Sprintf("firewall-cmd --zone=public --%s-rich-rule '%s' --permanent", operation, ruleStr))
stdout, err := cmd.Execf("firewall-cmd --zone=public --%s-rich-rule '%s' --permanent", operation, ruleStr)
if err != nil {
return fmt.Errorf("%s rich rules failed, err: %s", operation, stdout)
}
@ -149,7 +162,7 @@ func (f *Firewall) PortForward(info Forward, operation string) error {
ruleStr = fmt.Sprintf("firewall-cmd --%s-forward-port=port=%s:proto=%s:toaddr=%s:toport=%s --permanent", operation, info.Port, info.Protocol, info.Address, info.Target)
}
stdout, err := f.Client.Run(ruleStr)
stdout, err := cmd.Exec(ruleStr)
if err != nil {
return fmt.Errorf("%s port forward failed, err: %s", operation, stdout)
}

View File

@ -2,24 +2,20 @@ package client
import (
"fmt"
"io/ioutil"
"os"
"strings"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/utils/ssh"
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
)
type Ufw struct {
Client ssh.ConnInfo
}
const confPath = "/etc/ufw/sysctl.conf"
type Ufw struct{}
func NewUfw() (*Ufw, error) {
ConnInfo := ssh.ConnInfo{
Addr: "172.16.10.234",
User: "ubuntu",
AuthMode: "password",
Port: 22,
}
return &Ufw{Client: ConnInfo}, nil
return &Ufw{}, nil
}
func (f *Ufw) Name() string {
@ -27,7 +23,7 @@ func (f *Ufw) Name() string {
}
func (f *Ufw) Status() (string, error) {
stdout, err := f.Client.Run("sudo ufw status | grep Status")
stdout, err := cmd.Exec("sudo ufw status | grep Status")
if err != nil {
return "", fmt.Errorf("load the firewall status failed, err: %s", stdout)
}
@ -38,7 +34,7 @@ func (f *Ufw) Status() (string, error) {
}
func (f *Ufw) Version() (string, error) {
stdout, err := f.Client.Run("sudo ufw version | grep ufw")
stdout, err := cmd.Exec("sudo ufw version | grep ufw")
if err != nil {
return "", fmt.Errorf("load the firewall status failed, err: %s", stdout)
}
@ -47,7 +43,7 @@ func (f *Ufw) Version() (string, error) {
}
func (f *Ufw) Start() error {
stdout, err := f.Client.Run("echo y | sudo ufw enable")
stdout, err := cmd.Exec("echo y | sudo ufw enable")
if err != nil {
return fmt.Errorf("enable the firewall failed, err: %s", stdout)
}
@ -55,9 +51,9 @@ func (f *Ufw) Start() error {
}
func (f *Ufw) PingStatus() (string, error) {
stdout, err := f.Client.Run("cat /etc/ufw/sysctl.conf | grep net/ipv4/icmp_echo_ignore_all= ")
stdout, err := cmd.Exec("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)
return constant.StatusDisable, fmt.Errorf("load firewall ping status failed, err: %s", stdout)
}
if stdout == "net/ipv4/icmp_echo_ignore_all=1\n" {
return constant.StatusEnable, nil
@ -65,8 +61,35 @@ func (f *Ufw) PingStatus() (string, error) {
return constant.StatusDisable, nil
}
func (f *Ufw) UpdatePingStatus(enabel string) error {
lineBytes, err := ioutil.ReadFile(confPath)
if err != nil {
return err
}
files := strings.Split(string(lineBytes), "\n")
var newFiles []string
for _, line := range files {
if strings.Contains(line, "net/ipv4/icmp_echo_ignore_all") || strings.HasPrefix(line, "net/ipv4/icmp_echo_ignore_all") {
newFiles = append(newFiles, "net/ipv4/icmp_echo_ignore_all="+enabel)
} else {
newFiles = append(newFiles, line)
}
}
file, err := os.OpenFile(confPath, os.O_WRONLY|os.O_TRUNC, 0666)
if err != nil {
return err
}
defer file.Close()
_, err = file.WriteString(strings.Join(newFiles, "\n"))
if err != nil {
return err
}
return nil
}
func (f *Ufw) Stop() error {
stdout, err := f.Client.Run("sudo ufw disable")
stdout, err := cmd.Exec("sudo ufw disable")
if err != nil {
return fmt.Errorf("stop the firewall failed, err: %s", stdout)
}
@ -78,7 +101,7 @@ func (f *Ufw) Reload() error {
}
func (f *Ufw) ListPort() ([]FireInfo, error) {
stdout, err := f.Client.Run("sudo ufw status verbose")
stdout, err := cmd.Exec("sudo ufw status verbose")
if err != nil {
return nil, err
}
@ -103,7 +126,7 @@ func (f *Ufw) ListPort() ([]FireInfo, error) {
}
func (f *Ufw) ListAddress() ([]FireInfo, error) {
stdout, err := f.Client.Run("sudo ufw status verbose")
stdout, err := cmd.Exec("sudo ufw status verbose")
if err != nil {
return nil, err
}
@ -150,7 +173,7 @@ func (f *Ufw) Port(port FireInfo, operation string) error {
if len(port.Protocol) != 0 {
command += fmt.Sprintf("/%s", port.Protocol)
}
stdout, err := f.Client.Run(command)
stdout, err := cmd.Exec(command)
if err != nil {
return fmt.Errorf("%s port failed, err: %s", operation, stdout)
}
@ -183,7 +206,7 @@ func (f *Ufw) RichRules(rule FireInfo, operation string) error {
ruleStr += fmt.Sprintf("to any port %s ", rule.Port)
}
stdout, err := f.Client.Run(ruleStr)
stdout, err := cmd.Exec(ruleStr)
if err != nil {
return fmt.Errorf("%s rich rules failed, err: %s", operation, stdout)
}
@ -196,7 +219,7 @@ func (f *Ufw) PortForward(info Forward, operation string) error {
ruleStr = fmt.Sprintf("firewall-cmd --%s-forward-port=port=%s:proto=%s:toaddr=%s:toport=%s --permanent", operation, info.Port, info.Protocol, info.Address, info.Target)
}
stdout, err := f.Client.Run(ruleStr)
stdout, err := cmd.Exec(ruleStr)
if err != nil {
return fmt.Errorf("%s port forward failed, err: %s", operation, stdout)
}

View File

@ -57,6 +57,7 @@ export namespace Host {
name: string;
status: string;
version: string;
pingStatus: string;
}
export interface RuleSearch extends ReqPage {
info: string;

View File

@ -21,6 +21,8 @@ const message = {
clean: 'Clean',
login: 'Login',
close: 'Close',
stop: 'Stop',
start: 'Start',
view: 'View',
watch: 'Watch',
handle: 'Handle',
@ -1181,6 +1183,38 @@ const message = {
argsCheck: 'GET parameter check',
postCheck: 'POST parameter verification',
cookieBlockList: 'Cookie Blacklist',
firewall: 'Firewall',
firewallHelper: '{0} System firewall',
firewallNotStart: 'The firewall service is not enabled at present, please enable it first!',
stopFirewallHelper:
'If the firewall is disabled, the server loses security protection. Do you want to continue?',
startFirewallHelper:
'After the firewall is enabled, the current server security can be better protected. Do you want to continue?',
noPing: 'Disable ping',
noPingHelper:
'If the ping function is disabled, the server cannot be pinged. Do you want to continue the operation?',
onPingHelper: 'If you disable ping, hackers may discover your server. Do you want to continue?',
protocol: 'Protocol',
port: 'Port',
changeStrategy: 'Change the {0} strategy',
changeStrategyHelper:
'Change [{1}] {0} strategy to [{2}]. After setting, {0} will access {2} externally. Do you want to continue?',
portHelper: 'Multiple ports can be entered, such as 80,81, or range ports, such as 80-88',
strategy: 'Strategy',
accept: 'Accept',
drop: 'Drop',
source: 'Source',
anyWhere: 'AnyWhere',
address: 'Specified IP',
allow: 'Allow',
deny: 'Deny',
addressHelper1: 'Support for multiple IP, such as 172.16.10.11 172.16.10.99',
addressHelper2: 'You can enter an IP address segment, for example, 172.16.10.0/24',
addressHelper3: 'You can enter an IP address range, such as 172.16.10.11-172.16.10.99',
allIP: 'All IP',
portRule: 'Port rule',
ipRule: 'IP rule',
},
};

View File

@ -1188,6 +1188,9 @@ const message = {
firewallNotStart: '当前未开启防火墙服务请先开启',
stopFirewallHelper: '停用系统防火墙服务器将失去安全防护是否继续操作',
startFirewallHelper: '启用系统防火墙后可以更好的防护当前的服务器安全是否继续操作',
noPing: ' ping',
noPingHelper: ' ping 后不影响服务器正常使用但无法 ping 通服务器是否继续操作',
onPingHelper: '解除禁 ping 状态可能会被黑客发现您的服务器是否继续操作',
protocol: '协议',
port: '端口',
changeStrategy: '修改{0}策略',

View File

@ -15,7 +15,7 @@
<el-button type="primary" @click="onOpenDialog('create')">
{{ $t('commons.button.create') }}{{ $t('cronjob.cronTask') }}
</el-button>
<el-button type="primary" plain :disabled="selects.length === 0" @click="onBatchDelete(null)">
<el-button plain :disabled="selects.length === 0" @click="onBatchDelete(null)">
{{ $t('commons.button.delete') }}
</el-button>
</el-col>

View File

@ -14,7 +14,7 @@
<el-button type="primary" @click="onOpenDialog('create')">
{{ $t('commons.button.create') }} {{ $t('firewall.ipRule') }}
</el-button>
<el-button @click="onDelete(null)">
<el-button @click="onDelete(null)" plain :disabled="selects.length === 0">
{{ $t('commons.button.delete') }}
</el-button>
</el-col>

View File

@ -15,7 +15,7 @@
<el-button type="primary" @click="onOpenDialog('create')">
{{ $t('commons.button.create') }}{{ $t('firewall.portRule') }}
</el-button>
<el-button @click="onDelete(null)">
<el-button @click="onDelete(null)" plain :disabled="selects.length === 0">
{{ $t('commons.button.delete') }}
</el-button>
</el-col>

View File

@ -16,6 +16,15 @@
<el-button type="primary" @click="onOperate('stop')" link>
{{ $t('commons.button.stop') }}
</el-button>
<el-divider direction="vertical" />
<el-button type="primary" link>{{ $t('firewall.noPing') }}</el-button>
<el-switch
style="margin-left: 10px"
inactive-value="Disable"
active-value="Enable"
@change="onPingOperate(baseInfo.pingStatus)"
v-model="onPing"
/>
</span>
<span v-if="baseInfo.status === 'not running'" class="buttons">
@ -36,19 +45,21 @@ import i18n from '@/lang';
import { ElMessageBox } from 'element-plus';
import { ref } from 'vue';
const baseInfo = ref<Host.FirewallBase>({ status: '', name: '', version: '' });
const baseInfo = ref<Host.FirewallBase>({ status: '', name: '', version: '', pingStatus: '' });
const onPing = ref();
const acceptParams = (): void => {
loadBaseInfo();
loadBaseInfo(true);
};
const emit = defineEmits(['search', 'update:status', 'update:loading']);
const loadBaseInfo = async () => {
const loadBaseInfo = async (search: boolean) => {
await loadFireBaseInfo()
.then((res) => {
baseInfo.value = res.data;
onPing.value = baseInfo.value.pingStatus;
emit('update:status', baseInfo.value.status);
if (baseInfo.value.status === 'running') {
if (baseInfo.value.status === 'running' && search) {
emit('search');
} else {
emit('update:loading', false);
@ -70,7 +81,7 @@ const onOperate = async (operation: string) => {
emit('update:status', 'running');
await operateFire(operation)
.then(() => {
loadBaseInfo();
loadBaseInfo(true);
})
.catch(() => {
emit('update:loading', false);
@ -78,6 +89,31 @@ const onOperate = async (operation: string) => {
});
};
const onPingOperate = async (operation: string) => {
let operationHelper =
operation === 'Enabel' ? i18n.global.t('firewall.noPingHelper') : i18n.global.t('firewall.onPingHelper');
ElMessageBox.confirm(operationHelper, i18n.global.t('firewall.noPing'), {
confirmButtonText: i18n.global.t('commons.button.confirm'),
cancelButtonText: i18n.global.t('commons.button.cancel'),
})
.then(async () => {
emit('update:loading', true);
emit('update:status', 'running');
operation = operation === 'Disable' ? 'enablePing' : 'disablePing';
await operateFire(operation)
.then(() => {
loadBaseInfo(false);
})
.catch(() => {
loadBaseInfo(false);
});
})
.catch(() => {
emit('update:loading', true);
loadBaseInfo(false);
});
};
defineExpose({
acceptParams,
});