From 37932d7fb2d8ec50eb2bea1ebf8269fb0531c404 Mon Sep 17 00:00:00 2001
From: ssongliu <73214554+ssongliu@users.noreply.github.com>
Date: Thu, 26 Feb 2026 18:01:47 +0800
Subject: [PATCH] chore: Complete system security upgrade (#11961)
---
agent/app/api/v2/container.go | 21 ----
agent/app/service/container.go | 40 +------
agent/app/service/database_common.go | 5 +
agent/app/service/device.go | 16 +--
agent/app/service/disk.go | 19 +--
agent/app/service/file.go | 15 ++-
agent/app/service/host_tool.go | 4 +
agent/app/service/mcp_server.go | 21 +++-
agent/app/service/runtime.go | 6 +-
agent/app/service/ssh.go | 62 ++++++++--
agent/app/service/website.go | 26 +++-
agent/app/service/website_auth_basic.go | 21 +++-
agent/app/service/website_lb.go | 23 +++-
agent/app/service/website_proxy.go | 30 ++++-
agent/app/service/website_rewrite.go | 15 ++-
agent/cron/cron.go | 15 ++-
agent/router/ro_container.go | 1 -
agent/utils/common/common.go | 33 +++---
agent/utils/compose/compose.go | 18 +--
agent/utils/encrypt/encrypt.go | 26 +++-
agent/utils/nginx/validate.go | 102 ++++++++++++++++
agent/utils/re/re.go | 4 +
core/app/dto/setting.go | 1 -
core/app/service/script_library.go | 15 ++-
core/cmd/server/cmd/update.go | 2 +-
core/middleware/api_auth.go | 23 ++--
core/utils/common/common.go | 26 ++--
core/utils/encrypt/encrypt.go | 25 +++-
frontend/src/api/modules/container.ts | 3 -
.../container/container/command/index.vue | 111 ------------------
30 files changed, 435 insertions(+), 294 deletions(-)
create mode 100644 agent/utils/nginx/validate.go
delete mode 100644 frontend/src/views/container/container/command/index.vue
diff --git a/agent/app/api/v2/container.go b/agent/app/api/v2/container.go
index 9d8d26e0e..c031b2952 100644
--- a/agent/app/api/v2/container.go
+++ b/agent/app/api/v2/container.go
@@ -306,27 +306,6 @@ func (b *BaseApi) ContainerCreate(c *gin.Context) {
helper.Success(c)
}
-// @Tags Container
-// @Summary Create container by command
-// @Accept json
-// @Param request body dto.ContainerCreateByCommand true "request"
-// @Success 200
-// @Security ApiKeyAuth
-// @Security Timestamp
-// @Router /containers/command [post]
-func (b *BaseApi) ContainerCreateByCommand(c *gin.Context) {
- var req dto.ContainerCreateByCommand
- if err := helper.CheckBindAndValidate(&req, c); err != nil {
- return
- }
-
- if err := containerService.ContainerCreateByCommand(req); err != nil {
- helper.InternalServer(c, err)
- return
- }
- helper.Success(c)
-}
-
// @Tags Container
// @Summary Upgrade container
// @Accept json
diff --git a/agent/app/service/container.go b/agent/app/service/container.go
index d7f4789ca..3856b6a4b 100644
--- a/agent/app/service/container.go
+++ b/agent/app/service/container.go
@@ -43,7 +43,6 @@ import (
"github.com/docker/go-connections/nat"
"github.com/gin-gonic/gin"
v1 "github.com/opencontainers/image-spec/specs-go/v1"
- "github.com/pkg/errors"
"github.com/shirou/gopsutil/v4/cpu"
"github.com/shirou/gopsutil/v4/mem"
)
@@ -69,7 +68,6 @@ type IContainerService interface {
ComposeLogClean(req dto.ComposeLogClean) error
ContainerCreate(req dto.ContainerOperate, inThread bool) error
- ContainerCreateByCommand(req dto.ContainerCreateByCommand) error
ContainerUpdate(req dto.ContainerOperate) error
ContainerUpgrade(req dto.ContainerUpgrade) error
ContainerInfo(req dto.OperationWithName) (*dto.ContainerOperate, error)
@@ -104,7 +102,7 @@ func (u *ContainerService) Page(req dto.PageContainer) (int64, interface{}, erro
if err != nil {
return 0, nil, err
}
- defer client.Close()
+ defer func() { _ = client.Close() }()
options := container.ListOptions{All: true}
if len(req.Filters) != 0 {
options.Filters = filters.NewArgs()
@@ -302,42 +300,6 @@ func (u *ContainerService) ContainerListStats() ([]dto.ContainerListStats, error
return datas, nil
}
-func (u *ContainerService) ContainerCreateByCommand(req dto.ContainerCreateByCommand) error {
- if cmd.CheckIllegal(req.Command) {
- return buserr.New("ErrCmdIllegal")
- }
- if !strings.HasPrefix(strings.TrimSpace(req.Command), "docker run ") {
- return errors.New("error command format")
- }
- containerName := ""
- commands := strings.Split(req.Command, " ")
- for index, val := range commands {
- if val == "--name" && len(commands) > index+1 {
- containerName = commands[index+1]
- }
- }
- if !strings.Contains(req.Command, " -d ") {
- req.Command = strings.ReplaceAll(req.Command, "docker run", "docker run -d")
- }
- if len(containerName) == 0 {
- containerName = fmt.Sprintf("1Panel-%s-%s", common.RandStr(5), common.RandStrAndNum(4))
- req.Command += fmt.Sprintf(" --name %s", containerName)
- }
- taskItem, err := task.NewTaskWithOps(containerName, task.TaskCreate, task.TaskScopeContainer, req.TaskID, 1)
- if err != nil {
- global.LOG.Errorf("new task for create container failed, err: %v", err)
- return err
- }
- go func() {
- taskItem.AddSubTask(i18n.GetWithName("ContainerCreate", containerName), func(t *task.Task) error {
- cmdMgr := cmd.NewCommandMgr(cmd.WithTask(*taskItem), cmd.WithTimeout(5*time.Minute))
- return cmdMgr.RunBashC(req.Command)
- }, nil)
- _ = taskItem.Execute()
- }()
- return nil
-}
-
func (u *ContainerService) Inspect(req dto.InspectReq) (string, error) {
client, err := docker.NewDockerClient()
if err != nil {
diff --git a/agent/app/service/database_common.go b/agent/app/service/database_common.go
index a303434ec..4d5fc9007 100644
--- a/agent/app/service/database_common.go
+++ b/agent/app/service/database_common.go
@@ -5,6 +5,7 @@ import (
"fmt"
"os"
"path"
+ "path/filepath"
"strings"
"github.com/1Panel-dev/1Panel/agent/app/dto"
@@ -43,6 +44,10 @@ func (u *DBCommonService) LoadBaseInfo(req dto.OperationWithNameAndType) (*dto.D
func (u *DBCommonService) LoadDatabaseFile(req dto.OperationWithNameAndType) (string, error) {
filePath := ""
+ safeName := filepath.Base(req.Name)
+ if safeName != req.Name || strings.Contains(safeName, "..") {
+ return "", buserr.New("ErrInvalidParams")
+ }
switch req.Type {
case "mysql-cluster-conf":
filePath = path.Join(global.Dir.DataDir, fmt.Sprintf("apps/mysql-cluster/%s/conf/my.cnf", req.Name))
diff --git a/agent/app/service/device.go b/agent/app/service/device.go
index 579509817..8702c4063 100644
--- a/agent/app/service/device.go
+++ b/agent/app/service/device.go
@@ -109,7 +109,7 @@ func (u *DeviceService) CheckDNS(key, value string) (bool, error) {
if err != nil {
return false, err
}
- defer conn.Close()
+ defer func() { _ = conn.Close() }()
return true, nil
}
@@ -132,7 +132,7 @@ func (u *DeviceService) Update(key, value string) error {
if cmd.CheckIllegal(value) {
return buserr.New("ErrCmdIllegal")
}
- if err := cmd.RunDefaultBashCf("%s hostnamectl set-hostname %s", cmd.SudoHandleCmd(), value); err != nil {
+ if err := cmd.NewCommandMgr(cmd.WithTimeout(20*time.Second)).Run("hostnamectl", "set-hostname", value); err != nil {
return err
}
_, _ = psutil.HOST.GetHostInfo(true)
@@ -245,7 +245,7 @@ func (u *DeviceService) UpdateSwap(req dto.SwapHelper) error {
}
cmdMgr := cmd.NewCommandMgr(cmd.WithTask(*taskItem))
if !req.IsNew {
- if err := cmdMgr.RunBashCf("%s swapoff %s", cmd.SudoHandleCmd(), req.Path); err != nil {
+ if err := cmdMgr.Run("swapoff", req.Path); err != nil {
return fmt.Errorf("handle swapoff %s failed, %v", req.Path, err)
}
}
@@ -256,22 +256,22 @@ func (u *DeviceService) UpdateSwap(req dto.SwapHelper) error {
return operateSwapWithFile(true, req)
}
taskItem.LogStart(i18n.GetMsgByKey("CreateSwap"))
- if err := cmdMgr.RunBashCf("%s dd if=/dev/zero of=%s bs=1024 count=%d", cmd.SudoHandleCmd(), req.Path, req.Size); err != nil {
+ if err := cmdMgr.Run("dd", "if=/dev/zero", fmt.Sprintf("of=%s", req.Path), "bs=1024", fmt.Sprintf("count=%d", req.Size)); err != nil {
return fmt.Errorf("handle dd %s failed, %v", req.Path, err)
}
taskItem.Log("chmod 0600 " + req.Path)
- if err := cmdMgr.RunBashCf("%s chmod 0600 %s", cmd.SudoHandleCmd(), req.Path); err != nil {
+ if err := cmdMgr.Run("chmod", "0600", req.Path); err != nil {
return fmt.Errorf("handle chmod 0600 %s failed, %v", req.Path, err)
}
taskItem.LogStart(i18n.GetMsgByKey("FormatSwap"))
- if err := cmdMgr.RunBashCf("%s mkswap -f %s", cmd.SudoHandleCmd(), req.Path); err != nil {
+ if err := cmdMgr.Run("mkswap", "-f", req.Path); err != nil {
return fmt.Errorf("handle mkswap -f %s failed, %v", req.Path, err)
}
taskItem.LogStart(i18n.GetMsgByKey("EnableSwap"))
- if err := cmdMgr.RunBashCf("%s swapon %s", cmd.SudoHandleCmd(), req.Path); err != nil {
- _ = cmdMgr.RunBashCf("%s swapoff %s", cmd.SudoHandleCmd(), req.Path)
+ if err := cmdMgr.Run("swapon", req.Path); err != nil {
+ _ = cmdMgr.Run("swapoff", req.Path)
return fmt.Errorf("handle swapoff %s failed, %v", req.Path, err)
}
return operateSwapWithFile(false, req)
diff --git a/agent/app/service/disk.go b/agent/app/service/disk.go
index 71ceed4d5..30563343a 100644
--- a/agent/app/service/disk.go
+++ b/agent/app/service/disk.go
@@ -2,13 +2,14 @@ package service
import (
"fmt"
+ "os"
+ "strings"
+ "time"
+
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
- "os"
- "strings"
- "time"
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
"github.com/1Panel-dev/1Panel/agent/app/dto/response"
@@ -65,19 +66,19 @@ func (s *DiskService) PartitionDisk(req request.DiskPartitionRequest) (string, e
}
cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(10 * time.Second))
- if err := cmdMgr.RunBashC(fmt.Sprintf("partprobe %s", req.Device)); err != nil {
+ if err := cmdMgr.Run("partprobe", req.Device); err != nil {
return "", buserr.WithErr("PartitionDiskErr", err)
}
- if err := cmdMgr.RunBashC(fmt.Sprintf("parted -s %s mklabel gpt", req.Device)); err != nil {
+ if err := cmdMgr.Run("parted", "-s", req.Device, "mklabel", "gpt"); err != nil {
return "", buserr.WithErr("PartitionDiskErr", err)
}
- if err := cmdMgr.RunBashC(fmt.Sprintf("parted -s %s mkpart primary 1MiB 100%%", req.Device)); err != nil {
+ if err := cmdMgr.Run("parted", "-s", req.Device, "mkpart", "primary", "1MiB", "100%"); err != nil {
return "", buserr.WithErr("PartitionDiskErr", err)
}
- if err := cmdMgr.RunBashC(fmt.Sprintf("partprobe %s", req.Device)); err != nil {
+ if err := cmdMgr.Run("partprobe", req.Device); err != nil {
return "", buserr.WithErr("PartitionDiskErr", err)
}
partition := req.Device + "1"
@@ -133,7 +134,7 @@ func (s *DiskService) MountDisk(req request.DiskMountRequest) error {
}
cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(1 * time.Minute))
- if err := cmdMgr.RunBashC(fmt.Sprintf("mount -t %s %s %s", req.Filesystem, req.Device, req.MountPoint)); err != nil {
+ if err := cmdMgr.Run("mount", "-t", req.Filesystem, req.Device, req.MountPoint); err != nil {
return buserr.WithErr("MountDiskErr", err)
}
if req.AutoMount {
@@ -156,7 +157,7 @@ func (s *DiskService) UnmountDisk(req request.DiskUnmountRequest) error {
if !isPointMounted(req.MountPoint) {
return buserr.New("MountDiskErr")
}
- if err := cmd.RunDefaultBashC(fmt.Sprintf("umount -f %s", req.MountPoint)); err != nil {
+ if err := cmd.NewCommandMgr(cmd.WithTimeout(20*time.Second)).Run("umount", "-f", req.MountPoint); err != nil {
return buserr.WithErr("MountDiskErr", err)
}
if err := removeFromFstab(req.MountPoint); err != nil {
diff --git a/agent/app/service/file.go b/agent/app/service/file.go
index a8a154d77..1c48e72e8 100644
--- a/agent/app/service/file.go
+++ b/agent/app/service/file.go
@@ -263,7 +263,7 @@ func (f *FileService) Create(op request.FileCreate) error {
func (f *FileService) Delete(op request.FileDelete) error {
if op.IsDir {
excludeDir := global.Dir.DataDir
- if filepath.Base(op.Path) == ".1panel_clash" || op.Path == excludeDir {
+ if path.Base(op.Path) == ".1panel_clash" || op.Path == excludeDir {
return buserr.New("ErrPathNotDelete")
}
}
@@ -592,6 +592,12 @@ func (f *FileService) DepthDirSize(req request.DirSizeReq) ([]response.DepthDirS
func (f *FileService) ReadLogByLine(req request.FileReadByLineReq) (*response.FileLineContent, error) {
logFilePath := ""
taskStatus := ""
+ if len(req.Name) != 0 {
+ safeName := path.Base(req.Name)
+ if safeName != req.Name || strings.Contains(safeName, "..") {
+ return nil, buserr.New("ErrInvalidParams")
+ }
+ }
switch req.Type {
case constant.TypeWebsite:
website, err := websiteRepo.GetFirst(repo.WithByID(req.ID))
@@ -649,9 +655,9 @@ func (f *FileService) ReadLogByLine(req request.FileReadByLineReq) (*response.Fi
logFilePath = taskModel.LogFile
taskStatus = taskModel.Status
case "mysql-slow-logs":
- logFilePath = path.Join(global.Dir.DataDir, fmt.Sprintf("apps/mysql/%s/data/1Panel-slow.log", req.Name))
+ logFilePath = path.Join(global.Dir.DataDir, "apps", "mysql", req.Name, "data", "1Panel-slow.log")
case "mariadb-slow-logs":
- logFilePath = path.Join(global.Dir.DataDir, fmt.Sprintf("apps/mariadb/%s/db/data/1Panel-slow.log", req.Name))
+ logFilePath = path.Join(global.Dir.DataDir, "apps", "mariadb", req.Name, "db", "data", "1Panel-slow.log")
case "php-fpm-slow-logs":
php, err := runtimeRepo.GetFirst(context.Background(), repo.WithByID(req.ID))
if err != nil {
@@ -666,8 +672,7 @@ func (f *FileService) ReadLogByLine(req request.FileReadByLineReq) (*response.Fi
}
logFilePath, _ = ini_conf.GetIniValue(configPath, "supervisord", "logfile")
case constant.Supervisor:
- logDir := path.Join(global.Dir.DataDir, "tools", "supervisord", "log")
- logFilePath = path.Join(logDir, req.Name)
+ logFilePath = path.Join(global.Dir.DataDir, "tools", "supervisord", "log", req.Name)
}
file, err := os.Open(logFilePath)
diff --git a/agent/app/service/host_tool.go b/agent/app/service/host_tool.go
index d9ba89623..cf91d8528 100644
--- a/agent/app/service/host_tool.go
+++ b/agent/app/service/host_tool.go
@@ -278,6 +278,10 @@ func (h *HostToolService) OperateSupervisorProcess(req request.SupervisorProcess
}
func handleProcess(supervisordDir string, req request.SupervisorProcessConfig, containerName string) error {
+ safeName := path.Base(req.Name)
+ if safeName != req.Name || strings.Contains(safeName, "..") {
+ return buserr.New("ErrInvalidParams")
+ }
var (
fileOp = files.NewFileOp()
logDir = path.Join(supervisordDir, "log")
diff --git a/agent/app/service/mcp_server.go b/agent/app/service/mcp_server.go
index efb6cf1a4..a6ee67b86 100644
--- a/agent/app/service/mcp_server.go
+++ b/agent/app/service/mcp_server.go
@@ -4,6 +4,10 @@ import (
"context"
"errors"
"fmt"
+ "path"
+ "strconv"
+ "strings"
+
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
"github.com/1Panel-dev/1Panel/agent/app/dto/response"
@@ -23,9 +27,6 @@ import (
"github.com/1Panel-dev/1Panel/agent/utils/nginx/parser"
"github.com/subosito/gotenv"
"gopkg.in/yaml.v3"
- "path"
- "strconv"
- "strings"
)
type McpServerService struct{}
@@ -458,7 +459,12 @@ func addProxy(server *model.McpServer) {
} else {
proxyPath = server.StreamableHttpPath
}
- location.UpdateDirective("proxy_pass", []string{fmt.Sprintf("http://127.0.0.1:%d%s", server.Port, proxyPath)})
+ safePass, err := nginx.NginxSafeString(fmt.Sprintf("http://127.0.0.1:%d%s", server.Port, proxyPath), nginx.ModeURL)
+ if err != nil {
+ global.LOG.Errorf("mcp add proxy failed, err: %v", err)
+ return
+ }
+ location.UpdateDirective("proxy_pass", []string{safePass})
location.ChangePath("^~", proxyPath)
if err = nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
global.LOG.Errorf("write config failed, err: %v", buserr.WithErr("ErrUpdateBuWebsite", err))
@@ -516,7 +522,12 @@ func addMCPProxy(websiteID uint) error {
} else {
proxyPath = server.StreamableHttpPath
}
- location.UpdateDirective("proxy_pass", []string{fmt.Sprintf("http://127.0.0.1:%d%s", server.Port, proxyPath)})
+ safePass, err := nginx.NginxSafeString(fmt.Sprintf("http://127.0.0.1:%d%s", server.Port, proxyPath), nginx.ModeURL)
+ if err != nil {
+ global.LOG.Errorf("mcp add proxy failed, err: %v", err)
+ return err
+ }
+ location.UpdateDirective("proxy_pass", []string{safePass})
location.ChangePath("^~", proxyPath)
if err = nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
return buserr.WithErr("ErrUpdateBuWebsite", err)
diff --git a/agent/app/service/runtime.go b/agent/app/service/runtime.go
index 57d861f9e..b37e9faab 100644
--- a/agent/app/service/runtime.go
+++ b/agent/app/service/runtime.go
@@ -677,7 +677,7 @@ func (r *RuntimeService) GetPHPExtensions(runtimeID uint) (response.PHPExtension
return res, err
}
cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(20 * time.Second))
- out, err := cmdMgr.RunWithStdoutBashCf("docker exec -i %s php -m", runtime.ContainerName)
+ out, err := cmdMgr.RunWithStdout("docker", "exec", "-i", runtime.ContainerName, "php", "-m")
if err != nil {
return res, err
}
@@ -722,7 +722,7 @@ func (r *RuntimeService) InstallPHPExtension(req request.PHPExtensionInstallReq)
}
installTask.AddSubTask("", func(t *task.Task) error {
err = cmd.NewCommandMgr(cmd.WithTask(*installTask), cmd.WithTimeout(20*time.Minute)).
- RunBashCf("docker exec -i %s install-ext %s", runtime.ContainerName, req.Name)
+ Run("docker", "exec", "-i", runtime.ContainerName, "install-ext", req.Name)
if err != nil {
return err
}
@@ -736,7 +736,7 @@ func (r *RuntimeService) InstallPHPExtension(req request.PHPExtensionInstallReq)
return err
}
err = cmd.NewCommandMgr(cmd.WithTask(*installTask), cmd.WithTimeout(15*time.Minute)).
- RunBashCf("docker commit %s %s", runtime.ContainerName, runtime.Image)
+ Run("docker", "commit", runtime.ContainerName, runtime.Image)
if err != nil {
return err
}
diff --git a/agent/app/service/ssh.go b/agent/app/service/ssh.go
index 49dc40665..e89b87807 100644
--- a/agent/app/service/ssh.go
+++ b/agent/app/service/ssh.go
@@ -269,16 +269,26 @@ func (u *SSHService) CreateRootCert(req dto.RootCertOperate) error {
publicPath := fmt.Sprintf("%s/.ssh/%s.pub", currentUser.HomeDir, req.Name)
authFilePath := currentUser.HomeDir + "/.ssh/authorized_keys"
- authFileItem, _ := os.ReadFile(authFilePath)
- authFile := string(authFileItem)
- if authFile != "" && !strings.HasSuffix(authFile, "\n") {
- file, err := os.OpenFile(authFilePath, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
+ if info, err := os.Stat(authFilePath); err == nil && info.Size() > 0 {
+ f, err := os.Open(authFilePath)
if err != nil {
return err
}
- defer file.Close()
- if _, err := file.WriteString("\n"); err != nil {
- return err
+ defer func() { _ = f.Close() }()
+
+ if _, err := f.Seek(-1, 2); err == nil {
+ buf := make([]byte, 1)
+ if _, err := f.Read(buf); err == nil && buf[0] != '\n' {
+ appendFile, err := os.OpenFile(authFilePath, os.O_APPEND|os.O_WRONLY, 0600)
+ if err != nil {
+ return err
+ }
+ if _, err := appendFile.Write([]byte("\n")); err != nil {
+ _ = appendFile.Close()
+ return err
+ }
+ _ = appendFile.Close()
+ }
}
}
@@ -290,17 +300,43 @@ func (u *SSHService) CreateRootCert(req dto.RootCertOperate) error {
return err
}
} else {
- command := fmt.Sprintf("ssh-keygen -t %s -f %s/.ssh/%s -N ''", req.EncryptionMode, currentUser.HomeDir, req.Name)
- if len(req.PassPhrase) != 0 {
- command = fmt.Sprintf("ssh-keygen -t %s -P %s -f %s/.ssh/%s | echo y", req.EncryptionMode, req.PassPhrase, currentUser.HomeDir, req.Name)
+ tmpPrivatePath := privatePath + ".tmp"
+ tmpPublicPath := privatePath + ".tmp.pub"
+ cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(2 * time.Minute))
+ args := []string{
+ "-t", req.EncryptionMode,
+ "-f", tmpPrivatePath,
+ "-N", req.PassPhrase,
+ "-q",
}
- if err := cmd.RunDefaultBashC(command); err != nil {
+
+ if err := cmdMgr.Run("ssh-keygen", args...); err != nil {
+ _ = os.Remove(tmpPrivatePath)
+ _ = os.Remove(tmpPublicPath)
return fmt.Errorf("generate failed, %v", err)
}
+ if err := os.Rename(tmpPrivatePath, privatePath); err != nil {
+ return fmt.Errorf("replace private key failed, %v", err)
+ }
+ if err := os.Rename(tmpPublicPath, publicPath); err != nil {
+ return fmt.Errorf("replace public key failed, %v", err)
+ }
}
- if err := cmd.RunDefaultBashCf("cat %s >> %s", publicPath, authFilePath); err != nil {
- return fmt.Errorf("generate failed, %v", err)
+ publicKeyBytes, err := os.ReadFile(publicPath)
+ if err != nil {
+ return fmt.Errorf("read public key failed, %v", err)
+ }
+
+ cleanKey := strings.TrimRight(string(publicKeyBytes), "\n") + "\n"
+ authFile, err := os.OpenFile(authFilePath, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
+ if err != nil {
+ return fmt.Errorf("open authorized_keys failed, %v", err)
+ }
+ defer func() { _ = authFile.Close() }()
+
+ if _, err := authFile.Write([]byte(cleanKey)); err != nil {
+ return fmt.Errorf("append authorized_keys failed, %v", err)
}
cert.PrivateKeyPath = privatePath
diff --git a/agent/app/service/website.go b/agent/app/service/website.go
index 25a514a6a..c121e589c 100644
--- a/agent/app/service/website.go
+++ b/agent/app/service/website.go
@@ -1906,6 +1906,10 @@ func (w WebsiteService) UpdateRedirectFile(req request.NginxRedirectUpdate) (err
if err != nil {
return err
}
+ safeName := path.Base(req.Name)
+ if safeName != req.Name || strings.Contains(safeName, "..") {
+ return buserr.New("ErrInvalidParams")
+ }
absolutePath := path.Join(GetSitePath(website, SiteRedirectDir), req.Name+".conf")
fileOp := files.NewFileOp()
oldRewriteContent, err = fileOp.GetContent(absolutePath)
@@ -2333,9 +2337,25 @@ func (w WebsiteService) ExecComposer(req request.ExecComposerReq) error {
siteDir, _ := settingRepo.Get(settingRepo.WithByKey("WEBSITE_DIR"))
execDir := strings.ReplaceAll(req.Dir, siteDir.Value, "/www")
composerTask.AddSubTask("", func(t *task.Task) error {
- cmdStr := fmt.Sprintf("docker exec -u %s %s sh -c 'composer config -g repo.packagist composer %s && composer %s --working-dir=%s'", req.User, runtime.ContainerName, req.Mirror, command, execDir)
- err = cmdMgr.RunBashC(cmdStr)
- if err != nil {
+ if err := cmdMgr.Run("docker", "exec",
+ "-u", req.User,
+ runtime.ContainerName,
+ "composer",
+ "config", "-g",
+ "repo.packagist",
+ "composer",
+ req.Mirror,
+ ); err != nil {
+ return err
+ }
+
+ if err := cmdMgr.Run("docker", "exec",
+ "-u", req.User,
+ runtime.ContainerName,
+ "composer",
+ command,
+ "--working-dir="+execDir,
+ ); err != nil {
return err
}
return nil
diff --git a/agent/app/service/website_auth_basic.go b/agent/app/service/website_auth_basic.go
index 3d1c80352..7740db349 100644
--- a/agent/app/service/website_auth_basic.go
+++ b/agent/app/service/website_auth_basic.go
@@ -3,6 +3,10 @@ package service
import (
"bufio"
"fmt"
+ "os"
+ "path"
+ "strings"
+
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
"github.com/1Panel-dev/1Panel/agent/app/dto/response"
@@ -11,14 +15,12 @@ import (
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/cmd/server/nginx_conf"
"github.com/1Panel-dev/1Panel/agent/constant"
+ "github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/files"
"github.com/1Panel-dev/1Panel/agent/utils/nginx"
"github.com/1Panel-dev/1Panel/agent/utils/nginx/components"
"github.com/1Panel-dev/1Panel/agent/utils/nginx/parser"
"golang.org/x/crypto/bcrypt"
- "os"
- "path"
- "strings"
)
func (w WebsiteService) GetAuthBasics(req request.NginxAuthReq) (res response.NginxAuthRes, err error) {
@@ -260,6 +262,10 @@ func (w WebsiteService) UpdatePathAuthBasic(req request.NginxPathAuthUpdate) err
if !fileOp.Stat(passDir) {
_ = fileOp.CreateDir(passDir, constant.DirPerm)
}
+ safeName := path.Base(req.Name)
+ if safeName != req.Name || strings.Contains(safeName, "..") {
+ return buserr.New("ErrInvalidParams")
+ }
confPath := path.Join(authDir, fmt.Sprintf("%s.conf", req.Name))
passPath := path.Join(passDir, fmt.Sprintf("%s.pass", req.Name))
var config *components.Config
@@ -289,7 +295,12 @@ func (w WebsiteService) UpdatePathAuthBasic(req request.NginxPathAuthUpdate) err
config.FilePath = confPath
directives := config.Directives
location, _ := directives[0].(*components.Location)
- location.UpdateDirective("auth_basic_user_file", []string{fmt.Sprintf("/www/sites/%s/path_auth/pass/%s", website.Alias, fmt.Sprintf("%s.pass", req.Name))})
+ safePass, err := nginx.NginxSafeString(fmt.Sprintf("/www/sites/%s/path_auth/pass/%s", website.Alias, fmt.Sprintf("%s.pass", req.Name)), nginx.ModePath)
+ if err != nil {
+ global.LOG.Errorf("invalid auth_basic_user_file path, err: %v", err)
+ return err
+ }
+ location.UpdateDirective("auth_basic_user_file", []string{safePass})
location.ChangePath("~*", fmt.Sprintf("^%s", req.Path))
var passwdHash []byte
passwdHash, err = bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
@@ -306,7 +317,7 @@ func (w WebsiteService) UpdatePathAuthBasic(req request.NginxPathAuthUpdate) err
}
nginxInclude := fmt.Sprintf("/www/sites/%s/path_auth/*.conf", website.Alias)
if err = updateNginxConfig(constant.NginxScopeServer, []dto.NginxParam{{Name: "include", Params: []string{nginxInclude}}}, &website); err != nil {
- return nil
+ return err
}
return nil
}
diff --git a/agent/app/service/website_lb.go b/agent/app/service/website_lb.go
index 9094d5961..32e2d1aab 100644
--- a/agent/app/service/website_lb.go
+++ b/agent/app/service/website_lb.go
@@ -3,6 +3,10 @@ package service
import (
"bytes"
"fmt"
+ "os"
+ "path"
+ "strings"
+
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
"github.com/1Panel-dev/1Panel/agent/app/repo"
@@ -13,9 +17,6 @@ import (
"github.com/1Panel-dev/1Panel/agent/utils/nginx"
"github.com/1Panel-dev/1Panel/agent/utils/nginx/components"
"github.com/1Panel-dev/1Panel/agent/utils/nginx/parser"
- "os"
- "path"
- "strings"
)
func (w WebsiteService) GetLoadBalances(id uint) ([]dto.NginxUpstream, error) {
@@ -87,6 +88,10 @@ func (w WebsiteService) CreateLoadBalance(req request.WebsiteLBCreate) error {
if !fileOp.Stat(includeDir) {
_ = fileOp.CreateDir(includeDir, constant.DirPerm)
}
+ safeName := path.Base(req.Name)
+ if safeName != req.Name || strings.Contains(safeName, "..") {
+ return buserr.New("ErrInvalidParams")
+ }
filePath := path.Join(includeDir, fmt.Sprintf("%s.conf", req.Name))
if fileOp.Stat(filePath) {
return buserr.New("ErrNameIsExist")
@@ -133,6 +138,10 @@ func (w WebsiteService) UpdateLoadBalance(req request.WebsiteLBUpdate) error {
}
includeDir := GetSitePath(website, SiteUpstreamDir)
fileOp := files.NewFileOp()
+ safeName := path.Base(req.Name)
+ if safeName != req.Name || strings.Contains(safeName, "..") {
+ return buserr.New("ErrInvalidParams")
+ }
filePath := path.Join(includeDir, fmt.Sprintf("%s.conf", req.Name))
if !fileOp.Stat(filePath) {
return nil
@@ -191,6 +200,10 @@ func (w WebsiteService) DeleteLoadBalance(req request.WebsiteLBDelete) error {
includeDir := GetSitePath(website, SiteUpstreamDir)
fileOp := files.NewFileOp()
+ safeName := path.Base(req.Name)
+ if safeName != req.Name || strings.Contains(safeName, "..") {
+ return buserr.New("ErrInvalidParams")
+ }
filePath := path.Join(includeDir, fmt.Sprintf("%s.conf", req.Name))
if !fileOp.Stat(filePath) {
return nil
@@ -211,6 +224,10 @@ func (w WebsiteService) UpdateLoadBalanceFile(req request.WebsiteLBUpdateFile) e
return err
}
includeDir := GetSitePath(website, SiteUpstreamDir)
+ safeName := path.Base(req.Name)
+ if safeName != req.Name || strings.Contains(safeName, "..") {
+ return buserr.New("ErrInvalidParams")
+ }
filePath := path.Join(includeDir, fmt.Sprintf("%s.conf", req.Name))
fileOp := files.NewFileOp()
oldContent, err := fileOp.GetContent(filePath)
diff --git a/agent/app/service/website_proxy.go b/agent/app/service/website_proxy.go
index 0d79a5257..ba5bd196d 100644
--- a/agent/app/service/website_proxy.go
+++ b/agent/app/service/website_proxy.go
@@ -107,8 +107,16 @@ func (w WebsiteService) OperateProxy(req request.WebsiteProxyConfig) (err error)
err = errors.New("invalid proxy config, no location found")
return
}
- location.UpdateDirective("proxy_pass", []string{req.ProxyPass})
- location.UpdateDirective("proxy_set_header", []string{"Host", req.ProxyHost})
+ safePass, err := nginx.NginxSafeString(req.ProxyPass, nginx.ModeURL)
+ if err != nil {
+ return err
+ }
+ safeHeader, err := nginx.NginxSafeString(req.ProxyHost, nginx.ModeHost)
+ if err != nil {
+ return err
+ }
+ location.UpdateDirective("proxy_pass", []string{safePass})
+ location.UpdateDirective("proxy_set_header", []string{"Host", safeHeader})
location.ChangePath(req.Modifier, req.Match)
// Server Cache Settings
if req.Cache {
@@ -147,14 +155,26 @@ func (w WebsiteService) OperateProxy(req request.WebsiteProxyConfig) (err error)
}
// CORS Settings
if req.Cors {
- location.UpdateDirective("add_header", []string{"Access-Control-Allow-Origin", req.AllowOrigins, "always"})
+ safeAllowOrigins, err := nginx.NginxSafeString(req.AllowOrigins, nginx.ModeGeneric)
+ if err != nil {
+ return err
+ }
+ location.UpdateDirective("add_header", []string{"Access-Control-Allow-Origin", safeAllowOrigins, "always"})
if req.AllowMethods != "" {
- location.UpdateDirective("add_header", []string{"Access-Control-Allow-Methods", req.AllowMethods, "always"})
+ safeAllowMethods, err := nginx.NginxSafeString(req.AllowMethods, nginx.ModeAllowMethods)
+ if err != nil {
+ return err
+ }
+ location.UpdateDirective("add_header", []string{"Access-Control-Allow-Methods", safeAllowMethods, "always"})
} else {
location.RemoveDirective("add_header", []string{"Access-Control-Allow-Methods"})
}
if req.AllowHeaders != "" {
- location.UpdateDirective("add_header", []string{"Access-Control-Allow-Headers", req.AllowHeaders, "always"})
+ safeAllowHeaders, err := nginx.NginxSafeString(req.AllowHeaders, nginx.ModeGeneric)
+ if err != nil {
+ return err
+ }
+ location.UpdateDirective("add_header", []string{"Access-Control-Allow-Headers", safeAllowHeaders, "always"})
} else {
location.RemoveDirective("add_header", []string{"Access-Control-Allow-Headers"})
}
diff --git a/agent/app/service/website_rewrite.go b/agent/app/service/website_rewrite.go
index 04b22e1af..b2263c490 100644
--- a/agent/app/service/website_rewrite.go
+++ b/agent/app/service/website_rewrite.go
@@ -4,6 +4,10 @@ import (
"bytes"
"context"
"fmt"
+ "os"
+ "path"
+ "strings"
+
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
"github.com/1Panel-dev/1Panel/agent/app/dto/response"
@@ -12,9 +16,6 @@ import (
"github.com/1Panel-dev/1Panel/agent/cmd/server/nginx_conf"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/utils/files"
- "os"
- "path"
- "strings"
)
func (w WebsiteService) UpdateRewriteConfig(req request.NginxRewriteUpdate) error {
@@ -73,6 +74,10 @@ func (w WebsiteService) GetRewriteConfig(req request.NginxRewriteReq) (*response
contentByte, _ = nginx_conf.Rewrites.ReadFile(rewriteFile)
if contentByte == nil {
customRewriteDir := GetOpenrestyDir(DefaultRewriteDir)
+ safeName := path.Base(req.Name)
+ if safeName != req.Name || strings.Contains(safeName, "..") {
+ return nil, buserr.New("ErrInvalidParams")
+ }
customRewriteFile := path.Join(customRewriteDir, fmt.Sprintf("%s.conf", strings.ToLower(req.Name)))
contentByte, err = files.NewFileOp().GetContent(customRewriteFile)
}
@@ -90,6 +95,10 @@ func (w WebsiteService) OperateCustomRewrite(req request.CustomRewriteOperate) e
return err
}
}
+ safeName := path.Base(req.Name)
+ if safeName != req.Name || strings.Contains(safeName, "..") {
+ return buserr.New("ErrInvalidParams")
+ }
rewriteFile := path.Join(rewriteDir, fmt.Sprintf("%s.conf", req.Name))
switch req.Operate {
case "create":
diff --git a/agent/cron/cron.go b/agent/cron/cron.go
index 70ed24d0b..78c3c887b 100644
--- a/agent/cron/cron.go
+++ b/agent/cron/cron.go
@@ -1,8 +1,9 @@
package cron
import (
+ "crypto/rand"
"fmt"
- mathRand "math/rand"
+ "math/big"
"time"
"github.com/1Panel-dev/1Panel/agent/app/model"
@@ -43,7 +44,17 @@ func Run() {
if _, err := global.Cron.AddJob("@daily", job.NewSSLJob()); err != nil {
global.LOG.Errorf("can not add ssl corn job: %s", err.Error())
}
- if _, err := global.Cron.AddJob(fmt.Sprintf("%v %v * * *", mathRand.Intn(60), mathRand.Intn(3)), job.NewAppStoreJob()); err != nil {
+ minuteRand, err := rand.Int(rand.Reader, big.NewInt(60))
+ if err != nil {
+ global.LOG.Errorf("generate random minute failed: %v", err)
+ minuteRand = big.NewInt(0)
+ }
+ hourRand, err := rand.Int(rand.Reader, big.NewInt(3))
+ if err != nil {
+ global.LOG.Errorf("generate random hour failed: %v", err)
+ hourRand = big.NewInt(0)
+ }
+ if _, err := global.Cron.AddJob(fmt.Sprintf("%v %v * * *", minuteRand.Int64(), hourRand.Int64()), job.NewAppStoreJob()); err != nil {
global.LOG.Errorf("can not add appstore corn job: %s", err.Error())
}
if _, err := global.Cron.AddJob("0 3 */31 * *", job.NewBackupJob()); err != nil {
diff --git a/agent/router/ro_container.go b/agent/router/ro_container.go
index 112d64be0..b997b551b 100644
--- a/agent/router/ro_container.go
+++ b/agent/router/ro_container.go
@@ -15,7 +15,6 @@ func (s *ContainerRouter) InitRouter(Router *gin.RouterGroup) {
baRouter.GET("/stats/:id", baseApi.ContainerStats)
baRouter.POST("", baseApi.ContainerCreate)
- baRouter.POST("command", baseApi.ContainerCreateByCommand)
baRouter.POST("/update", baseApi.ContainerUpdate)
baRouter.POST("/upgrade", baseApi.ContainerUpgrade)
baRouter.POST("/info", baseApi.ContainerInfo)
diff --git a/agent/utils/common/common.go b/agent/utils/common/common.go
index 4f2d61537..a49dda605 100644
--- a/agent/utils/common/common.go
+++ b/agent/utils/common/common.go
@@ -4,7 +4,7 @@ import (
"crypto/rand"
"fmt"
"io"
- mathRand "math/rand"
+ "math/big"
"net"
"os/exec"
"reflect"
@@ -135,13 +135,6 @@ func isDigit(r rune) bool {
return r >= '0' && r <= '9'
}
-func max(x, y int) int {
- if x > y {
- return x
- }
- return y
-}
-
func GetSortedVersions(versions []string) []string {
sort.Slice(versions, func(i, j int) bool {
return CompareVersion(versions[i], versions[j])
@@ -169,21 +162,29 @@ var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456
func RandStr(n int) string {
b := make([]rune, n)
+ max := big.NewInt(int64(len(letters)))
for i := range b {
- b[i] = letters[mathRand.Intn(len(letters))]
+ num, err := rand.Int(rand.Reader, max)
+ if err != nil {
+ return ""
+ }
+ b[i] = letters[num.Int64()]
}
return string(b)
}
func RandStrAndNum(n int) string {
- source := mathRand.NewSource(time.Now().UnixNano())
- randGen := mathRand.New(source)
const charset = "abcdefghijklmnopqrstuvwxyz0123456789"
b := make([]byte, n)
+ max := big.NewInt(int64(len(charset)))
for i := range b {
- b[i] = charset[randGen.Intn(len(charset)-1)]
+ num, err := rand.Int(rand.Reader, max)
+ if err != nil {
+ return ""
+ }
+ b[i] = charset[num.Int64()]
}
- return (string(b))
+ return string(b)
}
func ScanPort(port int) bool {
@@ -191,7 +192,7 @@ func ScanPort(port int) bool {
if err != nil {
return true
}
- defer ln.Close()
+ defer func() { _ = ln.Close() }()
return false
}
@@ -200,7 +201,7 @@ func ScanUDPPort(port int) bool {
if err != nil {
return true
}
- defer ln.Close()
+ defer func() { _ = ln.Close() }()
return false
}
@@ -225,7 +226,7 @@ func ScanPortWithIP(ip string, port int) bool {
}
return false
}
- defer conn.Close()
+ defer func() { _ = conn.Close() }()
return true
}
diff --git a/agent/utils/compose/compose.go b/agent/utils/compose/compose.go
index fc9c2e876..b370600f9 100644
--- a/agent/utils/compose/compose.go
+++ b/agent/utils/compose/compose.go
@@ -32,14 +32,14 @@ func Up(filePath string) (string, error) {
if err := checkCmd(); err != nil {
return "", err
}
- return cmd.NewCommandMgr(cmd.WithTimeout(20*time.Minute)).RunWithStdoutBashCf("%s %s up -d", global.CONF.DockerConfig.Command, loadFiles(filePath))
+ return cmd.NewCommandMgr(cmd.WithTimeout(20*time.Minute)).RunWithStdout(global.CONF.DockerConfig.Command, loadFiles(filePath), "up", "-d")
}
func UpWithTask(filePath string, task *task.Task, forcePull bool) error {
if err := pullComposeImages(filePath, forcePull, task); err != nil {
return err
}
- return cmd.NewCommandMgr(cmd.WithTask(*task)).RunBashCf("%s %s up -d", global.CONF.DockerConfig.Command, loadFiles(filePath))
+ return cmd.NewCommandMgr(cmd.WithTask(*task)).Run(global.CONF.DockerConfig.Command, loadFiles(filePath), "up", "-d")
}
func pullComposeImages(filePath string, forcePull bool, task *task.Task) error {
@@ -123,7 +123,7 @@ func getComposeImagesByCommand(filePath string) ([]string, error) {
return nil, err
}
stdout, err := cmd.NewCommandMgr(cmd.WithTimeout(5*time.Minute)).
- RunWithStdoutBashCf("%s %s config --images", global.CONF.DockerConfig.Command, loadFiles(filePath))
+ RunWithStdout(global.CONF.DockerConfig.Command, loadFiles(filePath), "config", "--images")
if err != nil {
return nil, fmt.Errorf("run compose config --images failed, std: %s, err: %v", stdout, err)
}
@@ -151,28 +151,28 @@ func Down(filePath string) (string, error) {
if err := checkCmd(); err != nil {
return "", err
}
- return cmd.NewCommandMgr(cmd.WithTimeout(20*time.Minute)).RunWithStdoutBashCf("%s %s down --remove-orphans", global.CONF.DockerConfig.Command, loadFiles(filePath))
+ return cmd.NewCommandMgr(cmd.WithTimeout(20*time.Minute)).RunWithStdout(global.CONF.DockerConfig.Command, loadFiles(filePath), "down", "--remove-orphans")
}
func Stop(filePath string) (string, error) {
if err := checkCmd(); err != nil {
return "", err
}
- return cmd.NewCommandMgr(cmd.WithTimeout(20*time.Minute)).RunWithStdoutBashCf("%s %s stop", global.CONF.DockerConfig.Command, loadFiles(filePath))
+ return cmd.NewCommandMgr(cmd.WithTimeout(20*time.Minute)).RunWithStdout(global.CONF.DockerConfig.Command, loadFiles(filePath), "stop")
}
func Restart(filePath string) (string, error) {
if err := checkCmd(); err != nil {
return "", err
}
- return cmd.NewCommandMgr(cmd.WithTimeout(20*time.Minute)).RunWithStdoutBashCf("%s %s restart", global.CONF.DockerConfig.Command, loadFiles(filePath))
+ return cmd.NewCommandMgr(cmd.WithTimeout(20*time.Minute)).RunWithStdout(global.CONF.DockerConfig.Command, loadFiles(filePath), "restart")
}
func Operate(filePath, operation string) (string, error) {
if err := checkCmd(); err != nil {
return "", err
}
- return cmd.NewCommandMgr(cmd.WithTimeout(20*time.Minute)).RunWithStdoutBashCf("%s %s %s", global.CONF.DockerConfig.Command, loadFiles(filePath), operation)
+ return cmd.NewCommandMgr(cmd.WithTimeout(20*time.Minute)).RunWithStdout(global.CONF.DockerConfig.Command, loadFiles(filePath), operation)
}
func DownAndUp(filePath string) (string, error) {
@@ -180,11 +180,11 @@ func DownAndUp(filePath string) (string, error) {
return "", err
}
cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(20 * time.Minute))
- stdout, err := cmdMgr.RunWithStdoutBashCf("%s %s down", global.CONF.DockerConfig.Command, loadFiles(filePath))
+ stdout, err := cmdMgr.RunWithStdout(global.CONF.DockerConfig.Command, loadFiles(filePath), "down")
if err != nil {
return stdout, err
}
- stdout, err = cmdMgr.RunWithStdoutBashCf("%s %s up -d", global.CONF.DockerConfig.Command, loadFiles(filePath))
+ stdout, err = cmdMgr.RunWithStdout(global.CONF.DockerConfig.Command, loadFiles(filePath), "up", "-d")
return stdout, err
}
diff --git a/agent/utils/encrypt/encrypt.go b/agent/utils/encrypt/encrypt.go
index 9a48e493f..882f3e8c4 100644
--- a/agent/utils/encrypt/encrypt.go
+++ b/agent/utils/encrypt/encrypt.go
@@ -118,10 +118,24 @@ func padding(plaintext []byte, blockSize int) []byte {
return append(plaintext, padtext...)
}
-func unPadding(origData []byte) []byte {
+func unPadding(origData []byte) ([]byte, error) {
length := len(origData)
+ if length == 0 {
+ return nil, fmt.Errorf("invalid padding size")
+ }
+
unpadding := int(origData[length-1])
- return origData[:(length - unpadding)]
+ if unpadding == 0 || unpadding > length {
+ return nil, fmt.Errorf("invalid padding")
+ }
+
+ for i := 0; i < unpadding; i++ {
+ if origData[length-1-i] != byte(unpadding) {
+ return nil, fmt.Errorf("invalid padding")
+ }
+ }
+
+ return origData[:(length - unpadding)], nil
}
func aesEncryptWithSalt(key, plaintext []byte) ([]byte, error) {
@@ -152,6 +166,10 @@ func aesDecryptWithSalt(key, ciphertext []byte) ([]byte, error) {
ciphertext = ciphertext[aes.BlockSize:]
cbc := cipher.NewCBCDecrypter(block, iv)
cbc.CryptBlocks(ciphertext, ciphertext)
- ciphertext = unPadding(ciphertext)
- return ciphertext, nil
+
+ unpadded, err := unPadding(ciphertext)
+ if err != nil {
+ return nil, err
+ }
+ return unpadded, nil
}
diff --git a/agent/utils/nginx/validate.go b/agent/utils/nginx/validate.go
new file mode 100644
index 000000000..8a56d193e
--- /dev/null
+++ b/agent/utils/nginx/validate.go
@@ -0,0 +1,102 @@
+package nginx
+
+import (
+ "errors"
+ "net/url"
+ "strings"
+ "unicode"
+
+ "github.com/1Panel-dev/1Panel/agent/utils/re"
+)
+
+type Mode int
+
+const (
+ ModeGeneric Mode = iota
+ ModeHost
+ ModeURL
+ ModePath
+ ModeAllowMethods
+)
+
+func NginxSafeString(input string, mode Mode) (string, error) {
+ if input == "" {
+ return "", errors.New("empty value not allowed")
+ }
+
+ for _, r := range input {
+ if unicode.IsControl(r) {
+ return "", errors.New("control characters not allowed")
+ }
+ }
+
+ if strings.ContainsAny(input, ";\n\r{}#`$") {
+ return "", errors.New("illegal nginx syntax characters")
+ }
+
+ switch mode {
+ case ModeHost:
+ return validateHost(input)
+ case ModeURL:
+ return validateURL(input)
+ case ModePath:
+ return validatePath(input)
+ case ModeAllowMethods:
+ return validateAllowMethods(input)
+ default:
+ return input, nil
+ }
+}
+
+func validateHost(host string) (string, error) {
+ if !re.GetRegex(re.NginxHostPattern).MatchString(host) {
+ return "", errors.New("invalid host format")
+ }
+ return host, nil
+}
+
+func validateURL(raw string) (string, error) {
+ u, err := url.Parse(raw)
+ if err != nil {
+ return "", errors.New("invalid url")
+ }
+
+ if u.Scheme != "http" && u.Scheme != "https" {
+ return "", errors.New("unsupported scheme")
+ }
+
+ if u.Host == "" {
+ return "", errors.New("missing host")
+ }
+
+ return raw, nil
+}
+
+func validatePath(p string) (string, error) {
+ if !re.GetRegex(re.NginxPathPattern).MatchString(p) {
+ return "", errors.New("invalid path")
+ }
+ return p, nil
+}
+
+var allowedMethods = map[string]bool{
+ "GET": true,
+ "POST": true,
+ "PUT": true,
+ "DELETE": true,
+ "PATCH": true,
+ "OPTIONS": true,
+ "HEAD": true,
+}
+
+func validateAllowMethods(input string) (string, error) {
+ parts := strings.Split(input, ",")
+ for i, method := range parts {
+ method = strings.TrimSpace(strings.ToUpper(method))
+ if !allowedMethods[method] {
+ return "", errors.New("invalid HTTP method: " + method)
+ }
+ parts[i] = method
+ }
+ return strings.Join(parts, ", "), nil
+}
diff --git a/agent/utils/re/re.go b/agent/utils/re/re.go
index 3c556e597..c3702e772 100644
--- a/agent/utils/re/re.go
+++ b/agent/utils/re/re.go
@@ -29,6 +29,8 @@ const (
AnsiEscapePattern = "\x1b\\[[0-9;?]*[A-Za-z]|\x1b=|\x1b>"
RecycleBinFilePattern = `_1p_file_1p_(.+)_p_(\d+)_(\d+)`
OrderByValidationPattern = `^[a-zA-Z_][a-zA-Z0-9_]*$`
+ NginxHostPattern = `^[a-zA-Z0-9.-]+(:[0-9]+)?$`
+ NginxPathPattern = `^/[a-zA-Z0-9._/\-]*$`
)
var regexMap = make(map[string]*regexp.Regexp)
@@ -58,6 +60,8 @@ func Init() {
AnsiEscapePattern,
RecycleBinFilePattern,
OrderByValidationPattern,
+ NginxHostPattern,
+ NginxPathPattern,
}
for _, pattern := range patterns {
diff --git a/core/app/dto/setting.go b/core/app/dto/setting.go
index 4b4eb4ecb..1b5621005 100644
--- a/core/app/dto/setting.go
+++ b/core/app/dto/setting.go
@@ -33,7 +33,6 @@ type SettingInfo struct {
ExpirationTime string `json:"expirationTime"`
ComplexityVerification string `json:"complexityVerification"`
MFAStatus string `json:"mfaStatus"`
- MFASecret string `json:"mfaSecret"`
MFAInterval string `json:"mfaInterval"`
AppStoreVersion string `json:"appStoreVersion"`
diff --git a/core/app/service/script_library.go b/core/app/service/script_library.go
index 93a91df3a..35fb32ebc 100644
--- a/core/app/service/script_library.go
+++ b/core/app/service/script_library.go
@@ -1,9 +1,10 @@
package service
import (
+ "crypto/rand"
"encoding/json"
"fmt"
- mathRand "math/rand"
+ "math/big"
"net/http"
"os"
"path"
@@ -172,7 +173,17 @@ func StartSync() {
service := NewIScriptService()
scriptSync, _ := repo.NewISettingRepo().GetValueByKey("ScriptSync")
if !global.CONF.Base.IsOffLine && scriptSync == constant.StatusEnable {
- id, err := global.Cron.AddJob(fmt.Sprintf("%v %v * * *", mathRand.Intn(60), mathRand.Intn(3)), service)
+ minuteRand, err := rand.Int(rand.Reader, big.NewInt(60))
+ if err != nil {
+ global.LOG.Errorf("generate random minute failed: %v", err)
+ minuteRand = big.NewInt(0)
+ }
+ hourRand, err := rand.Int(rand.Reader, big.NewInt(3))
+ if err != nil {
+ global.LOG.Errorf("generate random hour failed: %v", err)
+ hourRand = big.NewInt(0)
+ }
+ id, err := global.Cron.AddJob(fmt.Sprintf("%v %v * * *", minuteRand.Int64(), hourRand.Int64()), service)
if err != nil {
global.LOG.Errorf("[core] can not add script sync corn job: %s", err.Error())
}
diff --git a/core/cmd/server/cmd/update.go b/core/cmd/server/cmd/update.go
index 4f7802dab..afdf2c6f2 100644
--- a/core/cmd/server/cmd/update.go
+++ b/core/cmd/server/cmd/update.go
@@ -258,7 +258,7 @@ func isValidPassword(password string) bool {
}
}
- if len(password) < 8 && len(password) > 30 {
+ if len(password) < 8 || len(password) > 30 {
return false
}
if (numCount == 0 && alphaCount == 0) || (alphaCount == 0 && specialCount == 0) || (numCount == 0 && specialCount == 0) {
diff --git a/core/middleware/api_auth.go b/core/middleware/api_auth.go
index 1a1b92c95..2fbb91b27 100644
--- a/core/middleware/api_auth.go
+++ b/core/middleware/api_auth.go
@@ -1,17 +1,19 @@
package middleware
import (
- "crypto/md5"
+ "crypto/hmac"
+ "crypto/sha256"
"encoding/hex"
+ "net"
+ "strconv"
+ "strings"
+ "time"
+
"github.com/1Panel-dev/1Panel/core/app/api/v2/helper"
"github.com/1Panel-dev/1Panel/core/constant"
"github.com/1Panel-dev/1Panel/core/global"
"github.com/1Panel-dev/1Panel/core/utils/common"
"github.com/gin-gonic/gin"
- "net"
- "strconv"
- "strings"
- "time"
)
func ApiAuth() gin.HandlerFunc {
@@ -76,7 +78,8 @@ func isValid1PanelTimestamp(panelTimestamp string) bool {
func isValid1PanelToken(panelToken string, panelTimestamp string) bool {
system1PanelToken := global.Api.ApiKey
- return panelToken == GenerateMD5("1panel"+system1PanelToken+panelTimestamp)
+ expected := GenerateHMAC(system1PanelToken, panelTimestamp)
+ return hmac.Equal([]byte(panelToken), []byte(expected))
}
func isIPInWhiteList(clientIP string) bool {
@@ -111,8 +114,8 @@ func isIPInWhiteList(clientIP string) bool {
return false
}
-func GenerateMD5(param string) string {
- hash := md5.New()
- hash.Write([]byte(param))
- return hex.EncodeToString(hash.Sum(nil))
+func GenerateHMAC(systemToken, timestamp string) string {
+ mac := hmac.New(sha256.New, []byte(systemToken))
+ mac.Write([]byte("1panel" + timestamp))
+ return hex.EncodeToString(mac.Sum(nil))
}
diff --git a/core/utils/common/common.go b/core/utils/common/common.go
index a17e6324d..85aea82e5 100644
--- a/core/utils/common/common.go
+++ b/core/utils/common/common.go
@@ -2,9 +2,10 @@ package common
import (
"crypto/md5"
+ "crypto/rand"
"encoding/hex"
"fmt"
- mathRand "math/rand"
+ "math/big"
"net"
"os"
"path"
@@ -22,20 +23,29 @@ var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456
func RandStr(n int) string {
b := make([]rune, n)
+ max := big.NewInt(int64(len(letters)))
for i := range b {
- b[i] = letters[mathRand.Intn(len(letters))]
+ num, err := rand.Int(rand.Reader, max)
+ if err != nil {
+ return ""
+ }
+ b[i] = letters[num.Int64()]
}
return string(b)
}
+
func RandStrAndNum(n int) string {
- source := mathRand.NewSource(time.Now().UnixNano())
- randGen := mathRand.New(source)
const charset = "abcdefghijklmnopqrstuvwxyz0123456789"
b := make([]byte, n)
+ max := big.NewInt(int64(len(charset)))
for i := range b {
- b[i] = charset[randGen.Intn(len(charset)-1)]
+ num, err := rand.Int(rand.Reader, max)
+ if err != nil {
+ return ""
+ }
+ b[i] = charset[num.Int64()]
}
- return (string(b))
+ return string(b)
}
func Md5(val string) string {
@@ -68,7 +78,7 @@ func ScanPort(port int) bool {
if err != nil {
return true
}
- defer ln.Close()
+ defer func() { _ = ln.Close() }()
return false
}
@@ -81,7 +91,7 @@ func CheckPort(host string, port string, timeout time.Duration) bool {
}
return strings.Contains(err.Error(), "connection refused")
}
- defer conn.Close()
+ defer func() { _ = conn.Close() }()
return true
}
diff --git a/core/utils/encrypt/encrypt.go b/core/utils/encrypt/encrypt.go
index 753fdad12..7b8d979b0 100644
--- a/core/utils/encrypt/encrypt.go
+++ b/core/utils/encrypt/encrypt.go
@@ -163,14 +163,31 @@ func aesDecrypt(ciphertext, key, iv []byte) ([]byte, error) {
}
mode := cipher.NewCBCDecrypter(block, iv)
mode.CryptBlocks(ciphertext, ciphertext)
- ciphertext = pkcs7Unpad(ciphertext)
- return ciphertext, nil
+ unpadded, err := pkcs7Unpad(ciphertext)
+ if err != nil {
+ return nil, err
+ }
+ return unpadded, nil
}
-func pkcs7Unpad(data []byte) []byte {
+func pkcs7Unpad(data []byte) ([]byte, error) {
length := len(data)
+ if length == 0 {
+ return nil, errors.New("invalid padding size")
+ }
+
padLength := int(data[length-1])
- return data[:length-padLength]
+ if padLength == 0 || padLength > length {
+ return nil, errors.New("invalid padding")
+ }
+
+ for i := 0; i < padLength; i++ {
+ if data[length-1-i] != byte(padLength) {
+ return nil, errors.New("invalid padding")
+ }
+ }
+
+ return data[:length-padLength], nil
}
func DecryptPassword(encryptedData string, privateKey *rsa.PrivateKey) (string, error) {
diff --git a/frontend/src/api/modules/container.ts b/frontend/src/api/modules/container.ts
index 37b994b76..8a2c21b50 100644
--- a/frontend/src/api/modules/container.ts
+++ b/frontend/src/api/modules/container.ts
@@ -24,9 +24,6 @@ export const loadResourceLimit = () => {
export const createContainer = (params: Container.ContainerHelper) => {
return http.post(`/containers`, params, TimeoutEnum.T_10M);
};
-export const createContainerByCommand = (command: string, taskID: string) => {
- return http.post(`/containers/command`, { command: command, taskID: taskID });
-};
export const updateContainer = (params: Container.ContainerHelper) => {
return http.post(`/containers/update`, params, TimeoutEnum.T_10M);
};
diff --git a/frontend/src/views/container/container/command/index.vue b/frontend/src/views/container/container/command/index.vue
deleted file mode 100644
index 5e270c95d..000000000
--- a/frontend/src/views/container/container/command/index.vue
+++ /dev/null
@@ -1,111 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-