Files
odboy 400812d114 feat: 新增 file/svc/archive/sys/log/fw 分组并扩充 net、proc
- file:findbig 按大小、findrecent 按修改时间、findname 按名称查找文件
- svc:systemctl / launchctl / sc 统一接口,status、enable、disable、logs
- archive:按扩展名自动识别格式并解压,支持 tar.gz/tgz/tar.bz2/tar.xz/tar/zip/gz
- sysinfo:OS、CPU、内存、磁盘一览
- log:journalctl / log show / wevtutil 搜索关键字与查看最近日志
- fw:ufw / firewalld / iptables / ALF / netsh 统一接口,status、allow、deny
- net 新增 ping、trace(mtr 优先)、dns(dig 优先)、curl HTTP 诊断
- proc 新增 topmem、topcpu、killname
- 危险操作(svc enable/disable、proc killname、fw allow/deny)需 --yes 确认
- 注册表、executor 派发表、cli 分组说明与 README 同步更新
2026-11-02 22:42:18 +08:00

240 lines
7.6 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package executor
import (
"context"
"fmt"
"os/exec"
"runtime"
"strings"
)
// runFwStatus implements `kopsc fw status`: show current firewall rules.
func runFwStatus(ctx context.Context, req BuiltinRequest) error {
switch runtime.GOOS {
case "linux":
return fwStatusLinux(ctx, req)
case "darwin":
return fwStatusDarwin(ctx, req)
case "windows":
return fwStatusWindows(ctx, req)
default:
return fmt.Errorf("不支持的操作系统: %s", runtime.GOOS)
}
}
func fwStatusLinux(ctx context.Context, req BuiltinRequest) error {
// Try ufw first, then firewalld, then iptables.
if path, err := exec.LookPath("ufw"); err == nil {
cmd := exec.CommandContext(ctx, path, "status", "verbose")
cmd.Stdout = req.Stdout
cmd.Stderr = req.Stderr
return cmd.Run()
}
if path, err := exec.LookPath("firewall-cmd"); err == nil {
fmt.Fprintln(req.Stdout, "=== firewalld 状态 ===")
cmd := exec.CommandContext(ctx, path, "--state")
cmd.Stdout = req.Stdout
cmd.Stderr = req.Stderr
_ = cmd.Run()
fmt.Fprintln(req.Stdout, "\n=== 已开放端口 ===")
listCmd := exec.CommandContext(ctx, path, "--list-ports")
listCmd.Stdout = req.Stdout
listCmd.Stderr = req.Stderr
_ = listCmd.Run()
fmt.Fprintln(req.Stdout, "\n=== 已允许服务 ===")
svcCmd := exec.CommandContext(ctx, path, "--list-services")
svcCmd.Stdout = req.Stdout
svcCmd.Stderr = req.Stderr
return svcCmd.Run()
}
if path, err := exec.LookPath("iptables"); err == nil {
cmd := exec.CommandContext(ctx, path, "-L", "-n", "--line-numbers")
cmd.Stdout = req.Stdout
cmd.Stderr = req.Stderr
return cmd.Run()
}
return fmt.Errorf("未找到 ufw、firewall-cmd 或 iptables")
}
func fwStatusDarwin(ctx context.Context, req BuiltinRequest) error {
fmt.Fprintln(req.Stdout, "macOS 使用内置应用防火墙(ALF)")
cmd := exec.CommandContext(ctx, "/usr/libexec/ApplicationFirewall/socketfilterfw", "--getglobalstate")
cmd.Stdout = req.Stdout
cmd.Stderr = req.Stderr
_ = cmd.Run()
fmt.Fprintln(req.Stdout, "\n=== 已允许的应用 ===")
listCmd := exec.CommandContext(ctx, "/usr/libexec/ApplicationFirewall/socketfilterfw", "--listapps")
listCmd.Stdout = req.Stdout
listCmd.Stderr = req.Stderr
return listCmd.Run()
}
func fwStatusWindows(ctx context.Context, req BuiltinRequest) error {
cmd := exec.CommandContext(ctx, "netsh", "advfirewall", "firewall", "show", "rule", "name=all", "dir=in", "status=enabled")
cmd.Stdout = req.Stdout
cmd.Stderr = req.Stderr
return cmd.Run()
}
// runFwAllow implements `kopsc fw allow <port>/<protocol>`: open a port.
func runFwAllow(ctx context.Context, req BuiltinRequest) error {
opts, err := parseFwPortArgs(req.Args, "allow")
if err != nil {
return err
}
switch runtime.GOOS {
case "linux":
return fwAllowLinux(ctx, opts, req)
case "darwin":
return fmt.Errorf("macOS 不支持通过命令行开放端口,请在系统偏好设置中配置")
case "windows":
return fwAllowWindows(ctx, opts, req)
default:
return fmt.Errorf("不支持的操作系统: %s", runtime.GOOS)
}
}
func fwAllowLinux(ctx context.Context, opts fwPortOptions, req BuiltinRequest) error {
portProto := fmt.Sprintf("%d/%s", opts.port, opts.protocol)
if path, err := exec.LookPath("ufw"); err == nil {
fmt.Fprintf(req.Stdout, "开放端口: %s (ufw)\n", portProto)
return runCommand(ctx, path, []string{"allow", portProto}, req)
}
if path, err := exec.LookPath("firewall-cmd"); err == nil {
fmt.Fprintf(req.Stdout, "开放端口: %s (firewalld)\n", portProto)
if err := runCommand(ctx, path, []string{"--add-port=" + portProto, "--permanent"}, req); err != nil {
return err
}
return runCommand(ctx, path, []string{"--reload"}, req)
}
return fmt.Errorf("未找到 ufw 或 firewall-cmd")
}
func fwAllowWindows(ctx context.Context, opts fwPortOptions, req BuiltinRequest) error {
name := fmt.Sprintf("kopsc-allow-%d-%s", opts.port, opts.protocol)
fmt.Fprintf(req.Stdout, "开放端口: %d/%s (Windows Firewall)\n", opts.port, opts.protocol)
return runCommand(ctx, "netsh", []string{
"advfirewall", "firewall", "add", "rule",
"name=" + name, "dir=in", "action=allow",
"protocol=" + opts.protocol, "localport=" + fmt.Sprintf("%d", opts.port),
}, req)
}
// runFwDeny implements `kopsc fw deny <port>/<protocol>`: close a port.
func runFwDeny(ctx context.Context, req BuiltinRequest) error {
opts, err := parseFwPortArgs(req.Args, "deny")
if err != nil {
return err
}
switch runtime.GOOS {
case "linux":
return fwDenyLinux(ctx, opts, req)
case "darwin":
return fmt.Errorf("macOS 不支持通过命令行关闭端口,请在系统偏好设置中配置")
case "windows":
return fwDenyWindows(ctx, opts, req)
default:
return fmt.Errorf("不支持的操作系统: %s", runtime.GOOS)
}
}
func fwDenyLinux(ctx context.Context, opts fwPortOptions, req BuiltinRequest) error {
portProto := fmt.Sprintf("%d/%s", opts.port, opts.protocol)
if path, err := exec.LookPath("ufw"); err == nil {
fmt.Fprintf(req.Stdout, "关闭端口: %s (ufw)\n", portProto)
return runCommand(ctx, path, []string{"deny", portProto}, req)
}
if path, err := exec.LookPath("firewall-cmd"); err == nil {
fmt.Fprintf(req.Stdout, "关闭端口: %s (firewalld)\n", portProto)
if err := runCommand(ctx, path, []string{"--remove-port=" + portProto, "--permanent"}, req); err != nil {
return err
}
return runCommand(ctx, path, []string{"--reload"}, req)
}
return fmt.Errorf("未找到 ufw 或 firewall-cmd")
}
func fwDenyWindows(ctx context.Context, opts fwPortOptions, req BuiltinRequest) error {
name := fmt.Sprintf("kopsc-allow-%d-%s", opts.port, opts.protocol)
fmt.Fprintf(req.Stdout, "关闭端口: %d/%s (Windows Firewall)\n", opts.port, opts.protocol)
return runCommand(ctx, "netsh", []string{
"advfirewall", "firewall", "delete", "rule",
"name=" + name,
}, req)
}
type fwPortOptions struct {
port int
protocol string
}
func parseFwPortArgs(args []string, action string) (fwPortOptions, error) {
opts := fwPortOptions{protocol: "tcp"}
var positional []string
for i := 0; i < len(args); i++ {
arg := args[i]
switch {
case arg == "--":
positional = append(positional, args[i+1:]...)
i = len(args)
case arg == "-p" || arg == "--protocol":
if i+1 >= len(args) {
return opts, fmt.Errorf("%s 缺少协议", arg)
}
i++
proto := strings.ToLower(args[i])
if proto != "tcp" && proto != "udp" {
return opts, fmt.Errorf("协议必须是 tcp 或 udp,收到 %q", args[i])
}
opts.protocol = proto
case strings.HasPrefix(arg, "--protocol="):
proto := strings.ToLower(strings.TrimPrefix(arg, "--protocol="))
if proto != "tcp" && proto != "udp" {
return opts, fmt.Errorf("协议必须是 tcp 或 udp,收到 %q", strings.TrimPrefix(arg, "--protocol="))
}
opts.protocol = proto
case strings.HasPrefix(arg, "-") && arg != "-":
return opts, fmt.Errorf("未知选项 %q", arg)
default:
positional = append(positional, arg)
}
}
if len(positional) == 0 {
return opts, fmt.Errorf("缺少端口号;用法: kopsc fw %s <端口>[/<协议>]", action)
}
portStr := positional[0]
if idx := strings.Index(portStr, "/"); idx >= 0 {
proto := strings.ToLower(portStr[idx+1:])
if proto != "tcp" && proto != "udp" {
return opts, fmt.Errorf("协议必须是 tcp 或 udp,收到 %q", proto)
}
opts.protocol = proto
portStr = portStr[:idx]
}
n, err := parseInt(portStr)
if err != nil || n < 1 || n > 65535 {
return opts, fmt.Errorf("端口必须是 1-65535 的整数,收到 %q", positional[0])
}
opts.port = n
return opts, nil
}
func parseInt(s string) (int, error) {
n := 0
for _, c := range s {
if c < '0' || c > '9' {
return 0, fmt.Errorf("不是整数: %q", s)
}
n = n*10 + int(c-'0')
}
return n, nil
}