mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-09-22 08:00:53 +00:00
chore: Complete system security upgrade (#11961)
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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,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))
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"})
|
||||
}
|
||||
|
||||
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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"`
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
@@ -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
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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>
|
||||
Reference in New Issue
Block a user