mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-10-13 00:00:36 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a40158e3c4 | ||
|
|
e90b201001 |
@@ -582,6 +582,18 @@ func (s *FirewallService) createRules(ctx context.Context, request dto.FirewallR
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
|
importPrioritySupported := true
|
||||||
|
if adapter, ok := runtime.(*filterfirewalld.Adapter); ok && slices.ContainsFunc(request.Items, func(item dto.FirewallRuleCreateItem) bool {
|
||||||
|
return item.SourceKind == constant.FirewallRuleSourceImported
|
||||||
|
}) {
|
||||||
|
importPrioritySupported, err = adapter.SupportsRichRulePriority(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
if !importPrioritySupported && t != nil {
|
||||||
|
t.Log("firewalld does not support rich rule priority; ignoring priority for imported rules, original rule ordering is not preserved")
|
||||||
|
}
|
||||||
|
}
|
||||||
preparedItems := make([]*createItem, 0, len(request.Items))
|
preparedItems := make([]*createItem, 0, len(request.Items))
|
||||||
var stop error
|
var stop error
|
||||||
for index, item := range request.Items {
|
for index, item := range request.Items {
|
||||||
@@ -593,6 +605,16 @@ func (s *FirewallService) createRules(ctx context.Context, request dto.FirewallR
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
item.Rule.OrderIndex = nil
|
item.Rule.OrderIndex = nil
|
||||||
|
if item.SourceKind == constant.FirewallRuleSourceImported && !importPrioritySupported {
|
||||||
|
item.Rule.Priority = nil
|
||||||
|
if item.Rule.Scope.Provider == filter.ProviderFirewalld && item.Raw != "" && item.ParseStatus != "" && item.ParseStatus != filter.ParseStatusSupported && item.Rule.NativeKind != filter.NativeKindZoneService {
|
||||||
|
item.Raw, err = filterfirewalld.RemoveRichRulePriority(item.Raw)
|
||||||
|
if err != nil {
|
||||||
|
record(createItem{index: index, request: item}, "skipped", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
rules, err := expandFirewallCreateRule(item, selected)
|
rules, err := expandFirewallCreateRule(item, selected)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
status := "failed"
|
status := "failed"
|
||||||
@@ -693,7 +715,7 @@ func (s *FirewallService) createRules(ctx context.Context, request dto.FirewallR
|
|||||||
record(*item, "skipped", buserr.New("ErrRecordExist"))
|
record(*item, "skipped", buserr.New("ErrRecordExist"))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if converted {
|
if converted && importPrioritySupported {
|
||||||
priority++
|
priority++
|
||||||
}
|
}
|
||||||
if existing[key] {
|
if existing[key] {
|
||||||
@@ -713,7 +735,7 @@ func (s *FirewallService) createRules(ctx context.Context, request dto.FirewallR
|
|||||||
}
|
}
|
||||||
ufwActions[matchKey] = map[filter.Action]bool{item.request.Rule.Action: true}
|
ufwActions[matchKey] = map[filter.Action]bool{item.request.Rule.Action: true}
|
||||||
}
|
}
|
||||||
if converted {
|
if converted && importPrioritySupported {
|
||||||
if priority > 32767 {
|
if priority > 32767 {
|
||||||
return result, fmt.Errorf("%w: firewalld import order exceeds the priority range", filter.ErrExpansionLimit)
|
return result, fmt.Errorf("%w: firewalld import order exceeds the priority range", filter.ErrExpansionLimit)
|
||||||
}
|
}
|
||||||
@@ -729,18 +751,19 @@ func (s *FirewallService) createRules(ctx context.Context, request dto.FirewallR
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
item.request = prepared
|
item.request = prepared
|
||||||
actualKey, err := firewallRuleDuplicateKey(filter.ObservedRule{Rule: prepared.Rule, ParseStatus: prepared.ParseStatus}, false)
|
}
|
||||||
|
pending[key] = true
|
||||||
|
if converted {
|
||||||
|
actualKey, err := firewallRuleDuplicateKey(filter.ObservedRule{Rule: item.request.Rule, ParseStatus: item.request.ParseStatus}, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
pending[key] = true
|
|
||||||
if existing[actualKey] || pending[actualKey] {
|
if existing[actualKey] || pending[actualKey] {
|
||||||
record(*item, "skipped", buserr.New("ErrRecordExist"))
|
record(*item, "skipped", buserr.New("ErrRecordExist"))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
pending[actualKey] = true
|
pending[actualKey] = true
|
||||||
}
|
}
|
||||||
pending[key] = true
|
|
||||||
mergedItems = append(mergedItems, item)
|
mergedItems = append(mergedItems, item)
|
||||||
}
|
}
|
||||||
for _, item := range mergedItems {
|
for _, item := range mergedItems {
|
||||||
|
|||||||
@@ -526,8 +526,10 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error)
|
|||||||
website.SiteDir = create.SiteDir
|
website.SiteDir = create.SiteDir
|
||||||
}
|
}
|
||||||
|
|
||||||
createdFiles := &websiteCreateFiles{}
|
|
||||||
configNginx := func(t *task.Task) error {
|
configNginx := func(t *task.Task) error {
|
||||||
|
if err = configDefaultNginx(website, domains, appInstall, runtime, create.StreamConfig, initialSSL); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if create.Type == constant.Static && create.TemplateOutputID > 0 {
|
if create.Type == constant.Static && create.TemplateOutputID > 0 {
|
||||||
templateOutput, err := websiteTemplateOutputRepo.GetFirst(repo.WithByID(create.TemplateOutputID))
|
templateOutput, err := websiteTemplateOutputRepo.GetFirst(repo.WithByID(create.TemplateOutputID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -539,15 +541,12 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error)
|
|||||||
if _, err := os.Stat(templateOutput.OutputPath); err != nil {
|
if _, err := os.Stat(templateOutput.OutputPath); err != nil {
|
||||||
return buserr.New("ErrFileNotFound")
|
return buserr.New("ErrFileNotFound")
|
||||||
}
|
}
|
||||||
if err := createdFiles.copyTemplate(templateOutput.OutputPath, GetSitePath(*website, SiteIndexDir)); err != nil {
|
if err := copyDir(templateOutput.OutputPath, GetSitePath(*website, SiteIndexDir)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err = configDefaultNginx(website, domains, appInstall, runtime, create.StreamConfig, initialSSL, createdFiles); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if website.Type != constant.Stream {
|
if website.Type != constant.Stream {
|
||||||
if err = createWafConfig(website, domains, createdFiles); err != nil {
|
if err = createWafConfig(website, domains); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if create.Type == constant.Runtime {
|
if create.Type == constant.Runtime {
|
||||||
@@ -556,9 +555,7 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error)
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if runtime.Type == constant.RuntimePHP && runtime.Resource == constant.ResourceAppstore {
|
if runtime.Type == constant.RuntimePHP && runtime.Resource == constant.ResourceAppstore {
|
||||||
if err := createdFiles.write(path.Join(GetSitePath(*website, SiteIndexDir), ".user.ini"), []byte(fmt.Sprintf("open_basedir=/www/sites/%s/index:/tmp/", website.Alias)), 0644, true); err != nil {
|
createOpenBasedirConfig(website)
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -578,19 +575,12 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := tx.Commit().Error; err != nil {
|
tx.Commit()
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteWebsite := func(t *task.Task) {
|
deleteWebsite := func(t *task.Task) {
|
||||||
if rollbackErr := createdFiles.rollback(); rollbackErr != nil {
|
_ = deleteWebsiteFolder(website)
|
||||||
t.Log(fmt.Sprintf("website file rollback: %v", rollbackErr))
|
|
||||||
}
|
|
||||||
if reloadErr := opNginx(nginxInstall.ContainerName, constant.NginxReload); reloadErr != nil {
|
|
||||||
t.Log(fmt.Sprintf("reload OpenResty after rollback: %v", reloadErr))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
createTask.AddSubTask(i18n.GetMsgByKey("ConfigOpenresty"), configNginx, deleteWebsite)
|
createTask.AddSubTask(i18n.GetMsgByKey("ConfigOpenresty"), configNginx, deleteWebsite)
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
@@ -15,7 +14,6 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -126,255 +124,7 @@ func applyLocationProxyPass(location *components.Location, proxyPass string, sni
|
|||||||
location.UpdateDirective("proxy_ssl_name", []string{sslName})
|
location.UpdateDirective("proxy_ssl_name", []string{sslName})
|
||||||
}
|
}
|
||||||
|
|
||||||
// websiteCreateFiles owns only paths actually created by this attempt.
|
func createIndexFile(website *model.Website, runtime *model.Runtime) error {
|
||||||
// Never recursively remove a reused directory (or a newly populated one).
|
|
||||||
type websiteCreateFiles struct {
|
|
||||||
mu sync.Mutex
|
|
||||||
entries []websiteCreatedPath
|
|
||||||
rolledBack bool
|
|
||||||
restores []func() error
|
|
||||||
}
|
|
||||||
type websiteCreatedPath struct {
|
|
||||||
path string
|
|
||||||
info os.FileInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *websiteCreateFiles) mkdir(name string) error {
|
|
||||||
f.mu.Lock()
|
|
||||||
defer f.mu.Unlock()
|
|
||||||
if f.rolledBack {
|
|
||||||
return fmt.Errorf("website creation already rolled back")
|
|
||||||
}
|
|
||||||
return f.mkdirLocked(filepath.Clean(name))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *websiteCreateFiles) mkdirLocked(name string) error {
|
|
||||||
info, err := os.Lstat(name)
|
|
||||||
if err == nil {
|
|
||||||
if !info.IsDir() {
|
|
||||||
return fmt.Errorf("website path is not a directory: %s", name)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if !os.IsNotExist(err) {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err = f.mkdirLocked(filepath.Dir(name)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err = os.Mkdir(name, constant.DirPerm); err != nil {
|
|
||||||
if os.IsExist(err) {
|
|
||||||
return f.mkdirLocked(name)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
info, err = os.Lstat(name)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
f.entries = append(f.entries, websiteCreatedPath{name, info})
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// preserveExisting is for user content and logs; generated configuration must
|
|
||||||
// fail on collision. Exclusive creation also protects against concurrent writes.
|
|
||||||
func (f *websiteCreateFiles) write(name string, data []byte, mode os.FileMode, preserveExisting bool) error {
|
|
||||||
f.mu.Lock()
|
|
||||||
defer f.mu.Unlock()
|
|
||||||
if f.rolledBack {
|
|
||||||
return fmt.Errorf("website creation already rolled back")
|
|
||||||
}
|
|
||||||
if err := f.mkdirLocked(filepath.Dir(name)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
file, err := os.OpenFile(name, os.O_WRONLY|os.O_CREATE|os.O_EXCL, mode)
|
|
||||||
if os.IsExist(err) && preserveExisting {
|
|
||||||
info, statErr := os.Lstat(name)
|
|
||||||
if statErr != nil {
|
|
||||||
return statErr
|
|
||||||
}
|
|
||||||
if !info.Mode().IsRegular() {
|
|
||||||
return fmt.Errorf("website path is not a regular file: %s", name)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("create website file %s: %w", name, err)
|
|
||||||
}
|
|
||||||
info, err := file.Stat()
|
|
||||||
if err != nil {
|
|
||||||
_ = file.Close()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
f.entries = append(f.entries, websiteCreatedPath{name, info})
|
|
||||||
_, err = file.Write(data)
|
|
||||||
closeErr := file.Close()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return closeErr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *websiteCreateFiles) rollback() error {
|
|
||||||
f.mu.Lock()
|
|
||||||
defer f.mu.Unlock()
|
|
||||||
f.rolledBack = true
|
|
||||||
var firstErr error
|
|
||||||
for i := len(f.restores) - 1; i >= 0; i-- {
|
|
||||||
if err := f.restores[i](); err != nil && firstErr == nil {
|
|
||||||
firstErr = err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
f.restores = nil
|
|
||||||
for i := len(f.entries) - 1; i >= 0; i-- {
|
|
||||||
entry := f.entries[i]
|
|
||||||
info, err := os.Lstat(entry.path)
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
if firstErr == nil {
|
|
||||||
firstErr = err
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if !os.SameFile(entry.info, info) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// os.Remove refuses non-empty directories, preserving untracked content.
|
|
||||||
if err = os.Remove(entry.path); err != nil && firstErr == nil {
|
|
||||||
firstErr = err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return firstErr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *websiteCreateFiles) copyTemplate(src, dst string) error {
|
|
||||||
// Check the entire template before copying any files. Reject symlinks and
|
|
||||||
// collisions rather than truncating existing content.
|
|
||||||
err := filepath.Walk(src, func(name string, info os.FileInfo, err error) error {
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
rel, err := filepath.Rel(src, name)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
target := filepath.Join(dst, rel)
|
|
||||||
if !info.IsDir() && !info.Mode().IsRegular() {
|
|
||||||
return fmt.Errorf("unsupported template file: %s", name)
|
|
||||||
}
|
|
||||||
existing, err := os.Lstat(target)
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if info.IsDir() && existing.IsDir() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return fmt.Errorf("website template path already exists: %s", target)
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return filepath.Walk(src, func(name string, info os.FileInfo, err error) error {
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
rel, err := filepath.Rel(src, name)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
target := filepath.Join(dst, rel)
|
|
||||||
if info.IsDir() {
|
|
||||||
return f.mkdir(target)
|
|
||||||
}
|
|
||||||
if !info.Mode().IsRegular() {
|
|
||||||
return fmt.Errorf("unsupported template file: %s", name)
|
|
||||||
}
|
|
||||||
data, err := os.ReadFile(name)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return f.write(target, data, constant.FilePerm, false)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only change ownership on files created by this attempt, never recursively on
|
|
||||||
// an existing document root.
|
|
||||||
func (f *websiteCreateFiles) chownNew(root string) error {
|
|
||||||
f.mu.Lock()
|
|
||||||
defer f.mu.Unlock()
|
|
||||||
if f.rolledBack {
|
|
||||||
return fmt.Errorf("website creation already rolled back")
|
|
||||||
}
|
|
||||||
for _, entry := range f.entries {
|
|
||||||
rel, err := filepath.Rel(root, entry.path)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if rel == ".." || filepath.IsAbs(rel) || (len(rel) > 3 && rel[:3] == "../") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
info, err := os.Lstat(entry.path)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !os.SameFile(entry.info, info) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err := os.Chown(entry.path, constant.WebsiteUID, constant.WebsiteGID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shared WAF metadata is restored only while it still contains our exact write.
|
|
||||||
// Never restore a stale snapshot over changes made by another operation.
|
|
||||||
func (f *websiteCreateFiles) replaceShared(name string, before, after []byte) error {
|
|
||||||
f.mu.Lock()
|
|
||||||
defer f.mu.Unlock()
|
|
||||||
if f.rolledBack {
|
|
||||||
return fmt.Errorf("website creation already rolled back")
|
|
||||||
}
|
|
||||||
info, err := os.Lstat(name)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !info.Mode().IsRegular() {
|
|
||||||
return fmt.Errorf("website config is not a regular file: %s", name)
|
|
||||||
}
|
|
||||||
current, err := os.ReadFile(name)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !bytes.Equal(current, before) {
|
|
||||||
return fmt.Errorf("website config changed during creation: %s", name)
|
|
||||||
}
|
|
||||||
f.restores = append(f.restores, func() error {
|
|
||||||
now, err := os.Lstat(name)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !os.SameFile(info, now) {
|
|
||||||
return fmt.Errorf("website config replaced; skipping rollback: %s", name)
|
|
||||||
}
|
|
||||||
data, err := os.ReadFile(name)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !bytes.Equal(data, after) {
|
|
||||||
return fmt.Errorf("website config changed; skipping rollback: %s", name)
|
|
||||||
}
|
|
||||||
return os.WriteFile(name, before, info.Mode().Perm())
|
|
||||||
})
|
|
||||||
return os.WriteFile(name, after, info.Mode().Perm())
|
|
||||||
}
|
|
||||||
|
|
||||||
func createIndexFile(website *model.Website, runtime *model.Runtime, created *websiteCreateFiles) error {
|
|
||||||
var (
|
var (
|
||||||
indexPath string
|
indexPath string
|
||||||
indexContent string
|
indexContent string
|
||||||
@@ -395,23 +145,49 @@ func createIndexFile(website *model.Website, runtime *model.Runtime, created *we
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := created.write(indexPath, []byte(indexContent), constant.FilePerm, true); err != nil {
|
fileOp := files.NewFileOp()
|
||||||
|
if !fileOp.Stat(indexFolder) {
|
||||||
|
if err := fileOp.CreateDir(indexFolder, constant.DirPerm); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
html404, _ := websiteService.GetDefaultHtml("404")
|
}
|
||||||
if err := created.write(path.Join(indexFolder, "404.html"), []byte(html404.Content), constant.FilePerm, true); err != nil {
|
if !fileOp.Stat(indexPath) {
|
||||||
|
if err := fileOp.CreateFile(indexPath); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if website.Type == constant.Runtime && runtime.Resource == constant.ResourceAppstore {
|
if website.Type == constant.Runtime && runtime.Resource == constant.ResourceAppstore {
|
||||||
return created.chownNew(indexFolder)
|
if err := chownRootDir(indexFolder); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := fileOp.WriteFile(indexPath, strings.NewReader(indexContent), constant.DirPerm); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
html404, _ := websiteService.GetDefaultHtml("404")
|
||||||
|
path404 := path.Join(indexFolder, "404.html")
|
||||||
|
if err := fileOp.WriteFile(path404, strings.NewReader(html404.Content), constant.DirPerm); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createProxyFile(website *model.Website, created *websiteCreateFiles) error {
|
func createProxyFile(website *model.Website) error {
|
||||||
proxyFolder := GetSitePath(*website, SiteProxyDir)
|
proxyFolder := GetSitePath(*website, SiteProxyDir)
|
||||||
filePath := path.Join(proxyFolder, "root.conf")
|
filePath := path.Join(proxyFolder, "root.conf")
|
||||||
|
fileOp := files.NewFileOp()
|
||||||
|
if !fileOp.Stat(proxyFolder) {
|
||||||
|
if err := fileOp.CreateDir(proxyFolder, constant.DirPerm); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !fileOp.Stat(filePath) {
|
||||||
|
if err := fileOp.CreateFile(filePath); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
config, err := parser.NewStringParser(string(nginx_conf.Proxy)).Parse()
|
config, err := parser.NewStringParser(string(nginx_conf.Proxy)).Parse()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -425,40 +201,57 @@ func createProxyFile(website *model.Website, created *websiteCreateFiles) error
|
|||||||
location.ChangePath("^~", "/")
|
location.ChangePath("^~", "/")
|
||||||
applyLocationProxyPass(location, website.Proxy, nil, "")
|
applyLocationProxyPass(location, website.Proxy, nil, "")
|
||||||
location.UpdateDirective("proxy_set_header", []string{"Host", "$host"})
|
location.UpdateDirective("proxy_set_header", []string{"Host", "$host"})
|
||||||
if err := created.write(filePath, []byte(nginx.DumpConfig(config, nginx.IndentedStyle)), constant.FilePerm, false); err != nil {
|
if err := nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
|
||||||
return buserr.WithErr("ErrUpdateBuWebsite", err)
|
return buserr.WithErr("ErrUpdateBuWebsite", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createWebsiteFolder(website *model.Website, runtime *model.Runtime, created *websiteCreateFiles) error {
|
func createWebsiteFolder(website *model.Website, runtime *model.Runtime) error {
|
||||||
siteFolder := GetSiteDir(website.Alias)
|
siteFolder := GetSiteDir(website.Alias)
|
||||||
dirs := []string{siteFolder, path.Join(siteFolder, "log")}
|
fileOp := files.NewFileOp()
|
||||||
|
if !fileOp.Stat(siteFolder) {
|
||||||
|
if err := fileOp.CreateDir(siteFolder, constant.DirPerm); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := fileOp.CreateDir(path.Join(siteFolder, "log"), constant.DirPerm); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := fileOp.CreateFile(path.Join(siteFolder, "log", "access.log")); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := fileOp.CreateFile(path.Join(siteFolder, "log", "error.log")); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if website.Type != constant.Stream {
|
if website.Type != constant.Stream {
|
||||||
dirs = append(dirs, path.Join(siteFolder, "index"), path.Join(siteFolder, "ssl"))
|
if err := fileOp.CreateDir(path.Join(siteFolder, "index"), constant.DirPerm); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
for _, dir := range dirs {
|
if err := fileOp.CreateDir(path.Join(siteFolder, "ssl"), constant.DirPerm); err != nil {
|
||||||
if err := created.mkdir(dir); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, name := range []string{"access.log", "error.log"} {
|
if website.Type == constant.Runtime {
|
||||||
if err := created.write(path.Join(siteFolder, "log", name), nil, constant.FilePerm, true); err != nil {
|
if runtime.Type == constant.RuntimePHP && runtime.Resource == constant.ResourceLocal {
|
||||||
|
phpPoolDir := path.Join(siteFolder, "php-pool")
|
||||||
|
if err := fileOp.CreateDir(phpPoolDir, constant.DirPerm); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
if err := fileOp.CreateFile(path.Join(phpPoolDir, "php-fpm.sock")); err != nil {
|
||||||
if website.Type == constant.Runtime && runtime.Type == constant.RuntimePHP && runtime.Resource == constant.ResourceLocal {
|
|
||||||
if err := created.write(path.Join(siteFolder, "php-pool", "php-fpm.sock"), nil, constant.FilePerm, true); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if website.Type == constant.Static || (website.Type == constant.Runtime && runtime.Type == constant.RuntimePHP) {
|
if website.Type == constant.Static || (website.Type == constant.Runtime && runtime.Type == constant.RuntimePHP) {
|
||||||
if err := createIndexFile(website, runtime, created); err != nil {
|
if err := createIndexFile(website, runtime); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if website.Type == constant.Proxy {
|
if website.Type == constant.Proxy {
|
||||||
return createProxyFile(website, created)
|
if err := createProxyFile(website); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -468,12 +261,12 @@ type websiteInitialSSL struct {
|
|||||||
request request.WebsiteHTTPSOp
|
request request.WebsiteHTTPSOp
|
||||||
}
|
}
|
||||||
|
|
||||||
func configDefaultNginx(website *model.Website, domains []model.WebsiteDomain, appInstall *model.AppInstall, runtime *model.Runtime, streamConfig request.StreamConfig, initialSSL *websiteInitialSSL, created *websiteCreateFiles) error {
|
func configDefaultNginx(website *model.Website, domains []model.WebsiteDomain, appInstall *model.AppInstall, runtime *model.Runtime, streamConfig request.StreamConfig, initialSSL *websiteInitialSSL) error {
|
||||||
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
|
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err = createWebsiteFolder(website, runtime, created); err != nil {
|
if err = createWebsiteFolder(website, runtime); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
@@ -541,10 +334,7 @@ func configDefaultNginx(website *model.Website, domains []model.WebsiteDomain, a
|
|||||||
if initialSSL != nil {
|
if initialSSL != nil {
|
||||||
plan := buildWebsiteTLSPlan(domains, nginxInstall.HttpPort, nginxInstall.HttpsPort)
|
plan := buildWebsiteTLSPlan(domains, nginxInstall.HttpPort, nginxInstall.HttpsPort)
|
||||||
applyWebsiteSSLConfig(server, *website, plan, initialSSL.request)
|
applyWebsiteSSLConfig(server, *website, plan, initialSSL.request)
|
||||||
if err = created.write(path.Join(GetSitePath(*website, SiteSSLDir), "fullchain.pem"), []byte(initialSSL.certificate.Pem), constant.FilePerm, false); err != nil {
|
if err = createPemFile(*website, initialSSL.certificate); err != nil {
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err = created.write(path.Join(GetSitePath(*website, SiteSSLDir), "privkey.pem"), []byte(initialSSL.certificate.PrivateKey), 0600, false); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -614,7 +404,7 @@ func configDefaultNginx(website *model.Website, domains []model.WebsiteDomain, a
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
config.FilePath = configPath
|
config.FilePath = configPath
|
||||||
if err = created.write(configPath, []byte(nginx.DumpConfig(config, nginx.IndentedStyle)), constant.FilePerm, false); err != nil {
|
if err = nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err = opNginx(nginxInstall.ContainerName, constant.NginxCheck); err != nil {
|
if err = opNginx(nginxInstall.ContainerName, constant.NginxCheck); err != nil {
|
||||||
@@ -709,7 +499,7 @@ func createOpenBasedirConfig(website *model.Website) {
|
|||||||
_ = fileOp.SaveFile(userIniPath, fmt.Sprintf("open_basedir=/www/sites/%s/index:/tmp/", website.Alias), 0644)
|
_ = fileOp.SaveFile(userIniPath, fmt.Sprintf("open_basedir=/www/sites/%s/index:/tmp/", website.Alias), 0644)
|
||||||
}
|
}
|
||||||
|
|
||||||
func createWafConfig(website *model.Website, domains []model.WebsiteDomain, created *websiteCreateFiles) error {
|
func createWafConfig(website *model.Website, domains []model.WebsiteDomain) error {
|
||||||
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
|
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -730,11 +520,6 @@ func createWafConfig(website *model.Website, domains []model.WebsiteDomain, crea
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, existing := range websitesArray {
|
|
||||||
if existing.Key == website.Alias {
|
|
||||||
return fmt.Errorf("WAF website already exists: %s", website.Alias)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
wafWebsite := request.WafWebsite{
|
wafWebsite := request.WafWebsite{
|
||||||
Key: website.Alias,
|
Key: website.Alias,
|
||||||
Domains: make([]string, 0),
|
Domains: make([]string, 0),
|
||||||
@@ -750,6 +535,9 @@ func createWafConfig(website *model.Website, domains []model.WebsiteDomain, crea
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err := fileOp.SaveFileWithByte(websitesConfigPath, websitesContent, constant.DirPerm); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
sitesDir = path.Join(wafDataPath, "sites")
|
sitesDir = path.Join(wafDataPath, "sites")
|
||||||
@@ -763,25 +551,13 @@ func createWafConfig(website *model.Website, domains []model.WebsiteDomain, crea
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := created.write(path.Join(websiteDir, "config.json"), defaultConfigContent, constant.FilePerm, false); err != nil {
|
defer func() {
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := created.mkdir(path.Join(websiteDir, "rules")); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, name := range []string{"acl", "args", "cookie", "defaultUaBlack", "defaultUrlBlack", "fileExt", "header", "methodWhite", "cdn"} {
|
|
||||||
data, err := os.ReadFile(path.Join(defaultRuleDir, name+".json"))
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
_ = fileOp.DeleteDir(websiteDir)
|
||||||
}
|
}
|
||||||
if err := created.write(path.Join(websiteDir, "rules", name+".json"), data, constant.FilePerm, false); err != nil {
|
}()
|
||||||
return err
|
|
||||||
}
|
if err := moveDefaultWafConfig(websiteDir, defaultConfigContent, defaultRuleDir, fileOp); err != nil {
|
||||||
}
|
|
||||||
if err := created.replaceShared(websitesConfigPath, content, websitesContent); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -92,6 +92,9 @@ func dockerPolicyRuleKey(tokens []string, backend string) string {
|
|||||||
if address, err := netip.ParseAddr(value); err == nil {
|
if address, err := netip.ParseAddr(value); err == nil {
|
||||||
value = netip.PrefixFrom(address, address.BitLen()).String()
|
value = netip.PrefixFrom(address, address.BitLen()).String()
|
||||||
} else if prefix, err := netip.ParsePrefix(value); err == nil {
|
} else if prefix, err := netip.ParsePrefix(value); err == nil {
|
||||||
|
if prefix.Bits() == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
value = prefix.Masked().String()
|
value = prefix.Masked().String()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -156,7 +159,13 @@ func parseDockerGuardPolicies(output, family string) (PolicyInventory, error) {
|
|||||||
group.acceptedSource = append(group.acceptedSource, source)
|
group.acceptedSource = append(group.acceptedSource, source)
|
||||||
case action == "accept":
|
case action == "accept":
|
||||||
group.acceptAll = true
|
group.acceptAll = true
|
||||||
case action == "return" && source != "":
|
case action == "return":
|
||||||
|
if source == "" {
|
||||||
|
source = "0.0.0.0/0"
|
||||||
|
if family == FamilyIPv6 {
|
||||||
|
source = "::/0"
|
||||||
|
}
|
||||||
|
}
|
||||||
group.allowedSource = append(group.allowedSource, source)
|
group.allowedSource = append(group.allowedSource, source)
|
||||||
case action == "drop" && source != "":
|
case action == "drop" && source != "":
|
||||||
group.droppedSource = append(group.droppedSource, source)
|
group.droppedSource = append(group.droppedSource, source)
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ func (a *Adapter) CheckRule(ctx context.Context, rule filter.FirewallRule) error
|
|||||||
if rule.Priority == nil || *rule.Priority == 0 {
|
if rule.Priority == nil || *rule.Priority == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
supported, err := a.supportsRichRulePriority(ctx)
|
supported, err := a.SupportsRichRulePriority(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -60,7 +60,7 @@ func (a *Adapter) CheckRule(ctx context.Context, rule filter.FirewallRule) error
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Adapter) supportsRichRulePriority(ctx context.Context) (bool, error) {
|
func (a *Adapter) SupportsRichRulePriority(ctx context.Context) (bool, error) {
|
||||||
a.prioritySupportMu.Lock()
|
a.prioritySupportMu.Lock()
|
||||||
defer a.prioritySupportMu.Unlock()
|
defer a.prioritySupportMu.Unlock()
|
||||||
if a.richRulePrioritySupported != nil {
|
if a.richRulePrioritySupported != nil {
|
||||||
@@ -82,6 +82,52 @@ func (a *Adapter) supportsRichRulePriority(ctx context.Context) (bool, error) {
|
|||||||
return supported, nil
|
return supported, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func RemoveRichRulePriority(raw string) (string, error) {
|
||||||
|
if strings.ContainsAny(raw, "\r\n\x00") {
|
||||||
|
return "", filter.ErrInvalidRule
|
||||||
|
}
|
||||||
|
parts := make([]string, 0)
|
||||||
|
removed := false
|
||||||
|
for start := 0; start < len(raw); {
|
||||||
|
if raw[start] == ' ' || raw[start] == '\t' {
|
||||||
|
start++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
end, quote := start, byte(0)
|
||||||
|
for end < len(raw) {
|
||||||
|
char := raw[end]
|
||||||
|
if char == '\\' && quote != '\'' {
|
||||||
|
end += 2
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if quote != 0 {
|
||||||
|
if char == quote {
|
||||||
|
quote = 0
|
||||||
|
}
|
||||||
|
} else if char == '\'' || char == '"' {
|
||||||
|
quote = char
|
||||||
|
} else if char == ' ' || char == '\t' {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
end++
|
||||||
|
}
|
||||||
|
if quote != 0 || end > len(raw) {
|
||||||
|
return "", filter.ErrInvalidRule
|
||||||
|
}
|
||||||
|
token := raw[start:end]
|
||||||
|
if strings.HasPrefix(token, "priority=") {
|
||||||
|
removed = true
|
||||||
|
} else {
|
||||||
|
parts = append(parts, token)
|
||||||
|
}
|
||||||
|
start = end
|
||||||
|
}
|
||||||
|
if !removed || len(parts) == 0 || parts[0] != "rule" {
|
||||||
|
return raw, nil
|
||||||
|
}
|
||||||
|
return strings.Join(parts, " "), nil
|
||||||
|
}
|
||||||
|
|
||||||
func parseFirewalldVersion(output string) (int, int, error) {
|
func parseFirewalldVersion(output string) (int, int, error) {
|
||||||
version := strings.TrimSpace(output)
|
version := strings.TrimSpace(output)
|
||||||
parts := strings.Split(version, ".")
|
parts := strings.Split(version, ".")
|
||||||
|
|||||||
@@ -32,13 +32,8 @@
|
|||||||
@user-change="currentUser = $event"
|
@user-change="currentUser = $event"
|
||||||
/>
|
/>
|
||||||
<div class="user-actions" :class="{ 'is-collapsed': isCollapse }">
|
<div class="user-actions" :class="{ 'is-collapsed': isCollapse }">
|
||||||
<el-tooltip
|
|
||||||
v-if="currentUser"
|
|
||||||
:content="$t('xpack.user.userInfo')"
|
|
||||||
placement="top"
|
|
||||||
popper-class="sidebar-user-tooltip"
|
|
||||||
>
|
|
||||||
<button
|
<button
|
||||||
|
v-if="currentUser"
|
||||||
type="button"
|
type="button"
|
||||||
class="user-entry"
|
class="user-entry"
|
||||||
:aria-label="$t('xpack.user.userInfo') + ' ' + currentUser.name"
|
:aria-label="$t('xpack.user.userInfo') + ' ' + currentUser.name"
|
||||||
@@ -47,8 +42,6 @@
|
|||||||
<SvgIcon class="icon" iconName="p-gerenzhongxin1" />
|
<SvgIcon class="icon" iconName="p-gerenzhongxin1" />
|
||||||
<span v-if="!isCollapse" class="user-name">{{ currentUser.name }}</span>
|
<span v-if="!isCollapse" class="user-name">{{ currentUser.name }}</span>
|
||||||
</button>
|
</button>
|
||||||
</el-tooltip>
|
|
||||||
<el-tooltip :content="$t('header.exitSystem')" placement="top" popper-class="sidebar-user-tooltip">
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="user-entry logout-entry"
|
class="user-entry logout-entry"
|
||||||
@@ -57,7 +50,6 @@
|
|||||||
>
|
>
|
||||||
<SvgIcon class="icon" iconName="p-tuichudenglu3" />
|
<SvgIcon class="icon" iconName="p-tuichudenglu3" />
|
||||||
</button>
|
</button>
|
||||||
</el-tooltip>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -441,16 +433,3 @@ watch(
|
|||||||
border-left: 1px solid var(--panel-footer-border);
|
border-left: 1px solid var(--panel-footer-border);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
.el-popper.sidebar-user-tooltip {
|
|
||||||
background: var(--el-menu-item-bg-color);
|
|
||||||
color: var(--el-color-primary);
|
|
||||||
border-color: var(--panel-footer-border);
|
|
||||||
|
|
||||||
.el-popper__arrow::before {
|
|
||||||
background: var(--el-menu-item-bg-color);
|
|
||||||
border-color: var(--panel-footer-border);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
Reference in New Issue
Block a user