chore: Complete system security upgrade (#11961)

This commit is contained in:
ssongliu
2026-02-26 10:01:47 +00:00
committed by GitHub
parent f0f2087f54
commit 37932d7fb2
30 changed files with 435 additions and 294 deletions
-21
View File
@@ -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
+1 -39
View File
@@ -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 {
+5
View File
@@ -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))
+8 -8
View File
@@ -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)
+10 -9
View File
@@ -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 {
+10 -5
View File
@@ -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)
+4
View File
@@ -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")
+16 -5
View File
@@ -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)
+3 -3
View File
@@ -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
}
+49 -13
View File
@@ -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
+23 -3
View File
@@ -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
+16 -5
View File
@@ -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
}
+20 -3
View File
@@ -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)
+25 -5
View File
@@ -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"})
}
+12 -3
View File
@@ -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":
+13 -2
View File
@@ -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 {
-1
View File
@@ -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)
+17 -16
View File
@@ -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
}
+9 -9
View File
@@ -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
}
+22 -4
View File
@@ -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
}
+102
View File
@@ -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
}
+4
View File
@@ -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 {
-1
View File
@@ -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"`
+13 -2
View File
@@ -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())
}
+1 -1
View File
@@ -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) {
+13 -10
View File
@@ -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))
}
+18 -8
View File
@@ -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
}
+21 -4
View File
@@ -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) {
-3
View File
@@ -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);
};
@@ -1,111 +0,0 @@
<template>
<div>
<DialogPro v-model="open" :title="$t('container.createByCommand')" @close="handleClose" size="w-70">
<el-form
@submit.prevent
ref="formRef"
:rules="rules"
:model="form"
label-position="top"
v-loading="loading"
>
<el-form-item prop="command">
<CodemirrorPro
:lineWrapping="true"
v-model="form.command"
:height="300"
:minHeight="50"
mode="shell"
placeholder="e.g. docker run -p 80:80 --name my-nginx nginx"
/>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button :disabled="loading" @click="open = false">
{{ $t('commons.button.cancel') }}
</el-button>
<el-button :disabled="loading" type="primary" @click="onSubmit(formRef)">
{{ $t('commons.button.confirm') }}
</el-button>
</span>
</template>
</DialogPro>
<TaskLog ref="taskLogRef" width="70%" />
</div>
</template>
<script setup lang="ts">
import { reactive, ref } from 'vue';
import { ElForm } from 'element-plus';
import i18n from '@/lang';
import TaskLog from '@/components/log/task/index.vue';
import { createContainerByCommand } from '@/api/modules/container';
import { MsgSuccess } from '@/utils/message';
import { newUUID } from '@/utils/util';
const open = ref<boolean>(false);
const emit = defineEmits<{ (e: 'search'): void }>();
const loading = ref(false);
const form = reactive({
command: '',
});
const taskLogRef = ref();
const acceptParams = (): void => {
form.command = '';
open.value = true;
};
const formRef = ref<FormInstance>();
type FormInstance = InstanceType<typeof ElForm>;
const verifyCommand = (rule: any, value: any, callback: any) => {
if (!form.command || !form.command.startsWith('docker run')) {
callback(new Error(i18n.global.t('container.commandRule')));
return;
}
callback();
};
const rules = reactive({
command: [{ validator: verifyCommand, trigger: 'blur', required: true }],
});
const onSubmit = async (formEl: FormInstance | undefined) => {
if (!formEl) return;
formEl.validate(async (valid) => {
if (!valid) return;
ElMessageBox.confirm(i18n.global.t('container.commandHelper'), i18n.global.t('container.createByCommand'), {
confirmButtonText: i18n.global.t('commons.button.confirm'),
cancelButtonText: i18n.global.t('commons.button.cancel'),
type: 'info',
}).then(async () => {
loading.value = true;
let taskID = newUUID();
await createContainerByCommand(form.command, taskID)
.then(() => {
loading.value = false;
emit('search');
openTaskLog(taskID);
open.value = false;
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
})
.catch(() => {
loading.value = false;
});
});
});
};
const openTaskLog = (taskID: string) => {
taskLogRef.value.openWithTaskID(taskID);
};
const handleClose = async () => {
open.value = false;
emit('search');
};
defineExpose({
acceptParams,
});
</script>