mirror of https://github.com/1Panel-dev/1Panel
appstorecrontabdatabasedockerdocker-composedocker-containerdocker-imagedocker-uifilemanagerlamplnmppanel
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
332 lines
8.8 KiB
332 lines
8.8 KiB
package service |
|
|
|
import ( |
|
"crypto/tls" |
|
"crypto/x509" |
|
"encoding/json" |
|
"encoding/pem" |
|
"fmt" |
|
"os" |
|
"strconv" |
|
"strings" |
|
"time" |
|
|
|
"github.com/1Panel-dev/1Panel/backend/app/dto" |
|
"github.com/1Panel-dev/1Panel/backend/buserr" |
|
"github.com/1Panel-dev/1Panel/backend/constant" |
|
"github.com/1Panel-dev/1Panel/backend/global" |
|
"github.com/1Panel-dev/1Panel/backend/utils/cmd" |
|
"github.com/1Panel-dev/1Panel/backend/utils/common" |
|
"github.com/1Panel-dev/1Panel/backend/utils/encrypt" |
|
"github.com/1Panel-dev/1Panel/backend/utils/ssl" |
|
"github.com/gin-gonic/gin" |
|
"github.com/pkg/errors" |
|
) |
|
|
|
type SettingService struct{} |
|
|
|
type ISettingService interface { |
|
GetSettingInfo() (*dto.SettingInfo, error) |
|
Update(key, value string) error |
|
UpdatePassword(c *gin.Context, old, new string) error |
|
UpdatePort(port uint) error |
|
UpdateSSL(c *gin.Context, req dto.SSLUpdate) error |
|
LoadFromCert() (*dto.SSLInfo, error) |
|
HandlePasswordExpired(c *gin.Context, old, new string) error |
|
} |
|
|
|
func NewISettingService() ISettingService { |
|
return &SettingService{} |
|
} |
|
|
|
func (u *SettingService) GetSettingInfo() (*dto.SettingInfo, error) { |
|
setting, err := settingRepo.GetList() |
|
if err != nil { |
|
return nil, constant.ErrRecordNotFound |
|
} |
|
settingMap := make(map[string]string) |
|
for _, set := range setting { |
|
settingMap[set.Key] = set.Value |
|
} |
|
var info dto.SettingInfo |
|
arr, err := json.Marshal(settingMap) |
|
if err != nil { |
|
return nil, err |
|
} |
|
if err := json.Unmarshal(arr, &info); err != nil { |
|
return nil, err |
|
} |
|
info.LocalTime = time.Now().Format("2006-01-02 15:04:05 MST -0700") |
|
return &info, err |
|
} |
|
|
|
func (u *SettingService) Update(key, value string) error { |
|
if key == "ExpirationDays" { |
|
timeout, _ := strconv.Atoi(value) |
|
if err := settingRepo.Update("ExpirationTime", time.Now().AddDate(0, 0, timeout).Format("2006-01-02 15:04:05")); err != nil { |
|
return err |
|
} |
|
} |
|
if err := settingRepo.Update(key, value); err != nil { |
|
return err |
|
} |
|
if key == "UserName" { |
|
_ = global.SESSION.Clean() |
|
} |
|
return nil |
|
} |
|
|
|
func (u *SettingService) UpdatePort(port uint) error { |
|
if common.ScanPort(int(port)) { |
|
return buserr.WithDetail(constant.ErrPortInUsed, port, nil) |
|
} |
|
serverPort, err := settingRepo.Get(settingRepo.WithByKey("ServerPort")) |
|
if err != nil { |
|
return err |
|
} |
|
portValue, _ := strconv.Atoi(serverPort.Value) |
|
if err := OperateFirewallPort([]int{portValue}, []int{int(port)}); err != nil { |
|
global.LOG.Errorf("set system firewall ports failed, err: %v", err) |
|
} |
|
if err := settingRepo.Update("ServerPort", strconv.Itoa(int(port))); err != nil { |
|
return err |
|
} |
|
go func() { |
|
_, err := cmd.Exec("systemctl restart 1panel.service") |
|
if err != nil { |
|
global.LOG.Errorf("restart system port failed, err: %v", err) |
|
} |
|
}() |
|
return nil |
|
} |
|
|
|
func (u *SettingService) UpdateSSL(c *gin.Context, req dto.SSLUpdate) error { |
|
if req.SSL == "disable" { |
|
if err := settingRepo.Update("SSL", "disable"); err != nil { |
|
return err |
|
} |
|
if err := settingRepo.Update("SSLType", "self"); err != nil { |
|
return err |
|
} |
|
_ = os.Remove(global.CONF.System.BaseDir + "/1panel/secret/server.crt") |
|
_ = os.Remove(global.CONF.System.BaseDir + "/1panel/secret/server.key") |
|
go func() { |
|
_, err := cmd.Exec("systemctl restart 1panel.service") |
|
if err != nil { |
|
global.LOG.Errorf("restart system failed, err: %v", err) |
|
} |
|
}() |
|
return nil |
|
} |
|
|
|
if _, err := os.Stat(global.CONF.System.BaseDir + "/1panel/secret"); err != nil && os.IsNotExist(err) { |
|
if err = os.MkdirAll(global.CONF.System.BaseDir+"/1panel/secret", os.ModePerm); err != nil { |
|
return err |
|
} |
|
} |
|
if err := settingRepo.Update("SSLType", req.SSLType); err != nil { |
|
return err |
|
} |
|
if req.SSLType == "self" { |
|
if len(req.Domain) == 0 { |
|
return fmt.Errorf("load domain failed") |
|
} |
|
if err := ssl.GenerateSSL(req.Domain); err != nil { |
|
return err |
|
} |
|
} |
|
if req.SSLType == "select" { |
|
sslInfo, err := websiteSSLRepo.GetFirst(commonRepo.WithByID(req.SSLID)) |
|
if err != nil { |
|
return err |
|
} |
|
req.Cert = sslInfo.Pem |
|
req.Key = sslInfo.PrivateKey |
|
req.SSLType = "import" |
|
if err := settingRepo.Update("SSLID", strconv.Itoa(int(req.SSLID))); err != nil { |
|
return err |
|
} |
|
} |
|
if req.SSLType == "import" { |
|
cert, err := os.OpenFile("/opt/1panel/secret/server.crt", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) |
|
if err != nil { |
|
return err |
|
} |
|
defer cert.Close() |
|
if _, err := cert.WriteString(req.Cert); err != nil { |
|
return err |
|
} |
|
key, err := os.OpenFile("/opt/1panel/secret/server.key", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) |
|
if err != nil { |
|
return err |
|
} |
|
if _, err := key.WriteString(req.Key); err != nil { |
|
return err |
|
} |
|
defer key.Close() |
|
} |
|
if err := checkCertValid(req.Domain); err != nil { |
|
return err |
|
} |
|
if err := settingRepo.Update("SSL", req.SSL); err != nil { |
|
return err |
|
} |
|
go func() { |
|
_, err := cmd.Exec("systemctl restart 1panel.service") |
|
if err != nil { |
|
global.LOG.Errorf("restart system failed, err: %v", err) |
|
} |
|
}() |
|
return nil |
|
} |
|
|
|
func (u *SettingService) LoadFromCert() (*dto.SSLInfo, error) { |
|
ssl, err := settingRepo.Get(settingRepo.WithByKey("SSL")) |
|
if err != nil { |
|
return nil, err |
|
} |
|
if ssl.Value == "disable" { |
|
return &dto.SSLInfo{}, nil |
|
} |
|
sslType, err := settingRepo.Get(settingRepo.WithByKey("SSLType")) |
|
if err != nil { |
|
return nil, err |
|
} |
|
data, err := loadInfoFromCert() |
|
if err != nil { |
|
return nil, err |
|
} |
|
switch sslType.Value { |
|
case "import": |
|
if _, err := os.Stat(global.CONF.System.BaseDir + "/1panel/secret/server.crt"); err != nil { |
|
return nil, fmt.Errorf("load server.crt file failed, err: %v", err) |
|
} |
|
certFile, _ := os.ReadFile(global.CONF.System.BaseDir + "/1panel/secret/server.crt") |
|
data.Cert = string(certFile) |
|
|
|
if _, err := os.Stat(global.CONF.System.BaseDir + "/1panel/secret/server.key"); err != nil { |
|
return nil, fmt.Errorf("load server.key file failed, err: %v", err) |
|
} |
|
keyFile, _ := os.ReadFile(global.CONF.System.BaseDir + "/1panel/secret/server.key") |
|
data.Key = string(keyFile) |
|
case "select": |
|
sslID, err := settingRepo.Get(settingRepo.WithByKey("SSLID")) |
|
if err != nil { |
|
return nil, err |
|
} |
|
id, _ := strconv.Atoi(sslID.Value) |
|
data.SSLID = uint(id) |
|
} |
|
return data, nil |
|
} |
|
|
|
func (u *SettingService) HandlePasswordExpired(c *gin.Context, old, new string) error { |
|
setting, err := settingRepo.Get(settingRepo.WithByKey("Password")) |
|
if err != nil { |
|
return err |
|
} |
|
passwordFromDB, err := encrypt.StringDecrypt(setting.Value) |
|
if err != nil { |
|
return err |
|
} |
|
if passwordFromDB == old { |
|
newPassword, err := encrypt.StringEncrypt(new) |
|
if err != nil { |
|
return err |
|
} |
|
if err := settingRepo.Update("Password", newPassword); err != nil { |
|
return err |
|
} |
|
|
|
expiredSetting, err := settingRepo.Get(settingRepo.WithByKey("ExpirationDays")) |
|
if err != nil { |
|
return err |
|
} |
|
timeout, _ := strconv.Atoi(expiredSetting.Value) |
|
if err := settingRepo.Update("ExpirationTime", time.Now().AddDate(0, 0, timeout).Format("2006-01-02 15:04:05")); err != nil { |
|
return err |
|
} |
|
return nil |
|
} |
|
return constant.ErrInitialPassword |
|
} |
|
|
|
func (u *SettingService) UpdatePassword(c *gin.Context, old, new string) error { |
|
if err := u.HandlePasswordExpired(c, old, new); err != nil { |
|
return err |
|
} |
|
_ = global.SESSION.Clean() |
|
return nil |
|
} |
|
|
|
func loadInfoFromCert() (*dto.SSLInfo, error) { |
|
var info dto.SSLInfo |
|
certFile := global.CONF.System.BaseDir + "/1panel/secret/server.crt" |
|
if _, err := os.Stat(certFile); err != nil { |
|
return &info, err |
|
} |
|
certData, err := os.ReadFile(certFile) |
|
if err != nil { |
|
return &info, err |
|
} |
|
certBlock, _ := pem.Decode(certData) |
|
if certBlock == nil { |
|
return &info, err |
|
} |
|
certObj, err := x509.ParseCertificate(certBlock.Bytes) |
|
if err != nil { |
|
return &info, err |
|
} |
|
var domains []string |
|
if len(certObj.IPAddresses) != 0 { |
|
for _, ip := range certObj.IPAddresses { |
|
domains = append(domains, ip.String()) |
|
} |
|
} |
|
if len(certObj.DNSNames) != 0 { |
|
domains = append(domains, certObj.DNSNames...) |
|
} |
|
return &dto.SSLInfo{ |
|
Domain: strings.Join(domains, ","), |
|
Timeout: certObj.NotAfter.Format("2006-01-02 15:04:05"), |
|
RootPath: global.CONF.System.BaseDir + "/1panel/secret/server.crt", |
|
}, nil |
|
} |
|
|
|
func checkCertValid(domain string) error { |
|
certificate, err := os.ReadFile(global.CONF.System.BaseDir + "/1panel/secret/server.crt") |
|
if err != nil { |
|
return err |
|
} |
|
key, err := os.ReadFile(global.CONF.System.BaseDir + "/1panel/secret/server.key") |
|
if err != nil { |
|
return err |
|
} |
|
if _, err = tls.X509KeyPair(certificate, key); err != nil { |
|
return err |
|
} |
|
certBlock, _ := pem.Decode(certificate) |
|
if certBlock == nil { |
|
return err |
|
} |
|
certObj, err := x509.ParseCertificate(certBlock.Bytes) |
|
if err != nil { |
|
return err |
|
} |
|
|
|
if len(certObj.IPAddresses) != 0 { |
|
for _, ip := range certObj.IPAddresses { |
|
if ip.String() == domain { |
|
return nil |
|
} |
|
} |
|
} |
|
if len(certObj.DNSNames) != 0 { |
|
for _, ip := range certObj.DNSNames { |
|
if ip == domain { |
|
return nil |
|
} |
|
} |
|
} |
|
return errors.New("The domain name or ip address does not match") |
|
}
|
|
|