Skip to content

Commit ea3705b

Browse files
feat(system-security): Support Hot Reloading of System Certificates
1 parent 7fdb0a5 commit ea3705b

File tree

13 files changed

+80
-114
lines changed

13 files changed

+80
-114
lines changed

backend/app/service/website_ca.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,9 @@ func (w WebsiteCAService) ObtainSSL(req request.WebsiteCAObtain) (*model.Website
381381
logger.Println(i18n.GetMsgByKey("ExecShellSuccess"))
382382
}
383383
}
384+
385+
reloadSystemSSL(websiteSSL, logger)
386+
384387
return websiteSSL, nil
385388
}
386389

backend/app/service/website_ssl.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package service
33
import (
44
"context"
55
"crypto"
6+
"crypto/tls"
67
"crypto/x509"
78
"encoding/pem"
89
"fmt"
@@ -188,6 +189,31 @@ func printSSLLog(logger *log.Logger, msgKey string, params map[string]interface{
188189
logger.Println(i18n.GetMsgWithMap(msgKey, params))
189190
}
190191

192+
func reloadSystemSSL(websiteSSL *model.WebsiteSSL, logger *log.Logger) {
193+
systemSSLEnable, sslID := GetSystemSSL()
194+
if systemSSLEnable && sslID == websiteSSL.ID {
195+
fileOp := files.NewFileOp()
196+
certPath := path.Join(global.CONF.System.BaseDir, "1panel/secret/server.crt")
197+
keyPath := path.Join(global.CONF.System.BaseDir, "1panel/secret/server.key")
198+
printSSLLog(logger, "StartUpdateSystemSSL", nil, logger == nil)
199+
if err := fileOp.WriteFile(certPath, strings.NewReader(websiteSSL.Pem), 0600); err != nil {
200+
logger.Printf("Failed to update the SSL certificate File for 1Panel System domain [%s] , err:%s", websiteSSL.PrimaryDomain, err.Error())
201+
return
202+
}
203+
if err := fileOp.WriteFile(keyPath, strings.NewReader(websiteSSL.PrivateKey), 0600); err != nil {
204+
logger.Printf("Failed to update the SSL certificate for 1Panel System domain [%s] , err:%s", websiteSSL.PrimaryDomain, err.Error())
205+
return
206+
}
207+
newCert, err := tls.X509KeyPair([]byte(websiteSSL.Pem), []byte(websiteSSL.PrivateKey))
208+
if err != nil {
209+
logger.Printf("Failed to update the SSL certificate for 1Panel System domain [%s] , err:%s", websiteSSL.PrimaryDomain, err.Error())
210+
return
211+
}
212+
printSSLLog(logger, "UpdateSystemSSLSuccess", nil, logger == nil)
213+
constant.CertStore.Store(&newCert)
214+
}
215+
}
216+
191217
func (w WebsiteSSLService) ObtainSSL(apply request.WebsiteSSLApply) error {
192218
var (
193219
err error
@@ -344,6 +370,8 @@ func (w WebsiteSSLService) ObtainSSL(apply request.WebsiteSSLApply) error {
344370
}
345371
printSSLLog(logger, "ApplyWebSiteSSLSuccess", nil, apply.DisableLog)
346372
}
373+
374+
reloadSystemSSL(websiteSSL, logger)
347375
}()
348376

349377
return nil

backend/app/service/website_utils.go

Lines changed: 6 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1002,23 +1002,22 @@ func saveCertificateFile(websiteSSL *model.WebsiteSSL, logger *log.Logger) {
10021002
}
10031003
}
10041004

1005-
func GetSystemSSL() (bool, bool, uint) {
1005+
func GetSystemSSL() (bool, uint) {
10061006
sslSetting, err := settingRepo.Get(settingRepo.WithByKey("SSL"))
10071007
if err != nil {
10081008
global.LOG.Errorf("load service ssl from setting failed, err: %v", err)
1009-
return false, false, 0
1009+
return false, 0
10101010
}
10111011
if sslSetting.Value == "enable" {
10121012
sslID, _ := settingRepo.Get(settingRepo.WithByKey("SSLID"))
10131013
idValue, _ := strconv.Atoi(sslID.Value)
10141014
if idValue <= 0 {
1015-
return false, false, 0
1015+
return false, 0
10161016
}
10171017

1018-
auto, _ := settingRepo.Get(settingRepo.WithByKey("AutoRestart"))
1019-
return true, auto.Value == "enable", uint(idValue)
1018+
return true, uint(idValue)
10201019
}
1021-
return false, false, 0
1020+
return false, 0
10221021
}
10231022

10241023
func UpdateSSLConfig(websiteSSL model.WebsiteSSL) error {
@@ -1037,22 +1036,7 @@ func UpdateSSLConfig(websiteSSL model.WebsiteSSL) error {
10371036
return buserr.WithErr(constant.ErrSSLApply, err)
10381037
}
10391038
}
1040-
enable, auto, sslID := GetSystemSSL()
1041-
if enable && sslID == websiteSSL.ID {
1042-
fileOp := files.NewFileOp()
1043-
secretDir := path.Join(global.CONF.System.BaseDir, "1panel/secret")
1044-
if err := fileOp.WriteFile(path.Join(secretDir, "server.crt"), strings.NewReader(websiteSSL.Pem), 0600); err != nil {
1045-
global.LOG.Errorf("Failed to update the SSL certificate File for 1Panel System domain [%s] , err:%s", websiteSSL.PrimaryDomain, err.Error())
1046-
return err
1047-
}
1048-
if err := fileOp.WriteFile(path.Join(secretDir, "server.key"), strings.NewReader(websiteSSL.PrivateKey), 0600); err != nil {
1049-
global.LOG.Errorf("Failed to update the SSL certificate for 1Panel System domain [%s] , err:%s", websiteSSL.PrimaryDomain, err.Error())
1050-
return err
1051-
}
1052-
if auto {
1053-
_, _ = cmd.Exec("systemctl restart 1panel.service")
1054-
}
1055-
}
1039+
reloadSystemSSL(&websiteSSL, nil)
10561040
return nil
10571041
}
10581042

backend/constant/common.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package constant
22

3+
import "sync/atomic"
4+
35
type DBContext string
46

57
const (
@@ -123,3 +125,5 @@ var DynamicRoutes = []string{
123125
`^/databases/postgresql/setting/[^/]+/[^/]+$`,
124126
`^/websites/[^/]+/config/[^/]+$`,
125127
}
128+
129+
var CertStore atomic.Value

backend/cron/job/ssl.go

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,14 @@
11
package job
22

33
import (
4-
"path"
5-
"strings"
64
"time"
75

86
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
97
"github.com/1Panel-dev/1Panel/backend/app/repo"
108
"github.com/1Panel-dev/1Panel/backend/app/service"
119
"github.com/1Panel-dev/1Panel/backend/constant"
1210
"github.com/1Panel-dev/1Panel/backend/global"
13-
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
1411
"github.com/1Panel-dev/1Panel/backend/utils/common"
15-
"github.com/1Panel-dev/1Panel/backend/utils/files"
1612
)
1713

1814
type ssl struct {
@@ -23,7 +19,6 @@ func NewSSLJob() *ssl {
2319
}
2420

2521
func (ssl *ssl) Run() {
26-
systemSSLEnable, auto, sslID := service.GetSystemSSL()
2722
sslRepo := repo.NewISSLRepo()
2823
sslService := service.NewIWebsiteSSLService()
2924
sslList, _ := sslRepo.List()
@@ -59,22 +54,6 @@ func (ssl *ssl) Run() {
5954
continue
6055
}
6156
}
62-
if systemSSLEnable && sslID == s.ID {
63-
websiteSSL, _ := sslRepo.GetFirst(repo.NewCommonRepo().WithByID(s.ID))
64-
fileOp := files.NewFileOp()
65-
secretDir := path.Join(global.CONF.System.BaseDir, "1panel/secret")
66-
if err := fileOp.WriteFile(path.Join(secretDir, "server.crt"), strings.NewReader(websiteSSL.Pem), 0600); err != nil {
67-
global.LOG.Errorf("Failed to update the SSL certificate File for 1Panel System domain [%s] , err:%s", s.PrimaryDomain, err.Error())
68-
continue
69-
}
70-
if err := fileOp.WriteFile(path.Join(secretDir, "server.key"), strings.NewReader(websiteSSL.PrivateKey), 0600); err != nil {
71-
global.LOG.Errorf("Failed to update the SSL certificate for 1Panel System domain [%s] , err:%s", s.PrimaryDomain, err.Error())
72-
continue
73-
}
74-
if auto {
75-
_, _ = cmd.Exec("systemctl restart 1panel.service")
76-
}
77-
}
7857
global.LOG.Infof("The SSL certificate for the [%s] domain has been successfully updated", s.PrimaryDomain)
7958
}
8059
}

backend/server/server.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@ import (
44
"crypto/tls"
55
"encoding/gob"
66
"fmt"
7+
"github.com/1Panel-dev/1Panel/backend/constant"
8+
"github.com/1Panel-dev/1Panel/backend/i18n"
79
"net"
810
"net/http"
911
"os"
1012
"path"
1113

12-
"github.com/1Panel-dev/1Panel/backend/i18n"
13-
1414
"github.com/1Panel-dev/1Panel/backend/init/app"
1515
"github.com/1Panel-dev/1Panel/backend/init/business"
1616

@@ -81,12 +81,16 @@ func Start() {
8181
if err != nil {
8282
panic(err)
8383
}
84+
constant.CertStore.Store(&cert)
85+
8486
server.TLSConfig = &tls.Config{
85-
Certificates: []tls.Certificate{cert},
87+
GetCertificate: func(info *tls.ClientHelloInfo) (*tls.Certificate, error) {
88+
return constant.CertStore.Load().(*tls.Certificate), nil
89+
},
8690
}
8791
global.LOG.Infof("listen at https://%s:%s [%s]", global.CONF.System.BindAddress, global.CONF.System.Port, tcpItem)
8892

89-
if err := server.ServeTLS(tcpKeepAliveListener{ln.(*net.TCPListener)}, certPath, keyPath); err != nil {
93+
if err := server.ServeTLS(tcpKeepAliveListener{ln.(*net.TCPListener)}, "", ""); err != nil {
9094
panic(err)
9195
}
9296
} else {

frontend/src/api/interface/setting.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ export namespace Setting {
2929
bindAddress: string;
3030
ssl: string;
3131
sslType: string;
32-
autoRestart: string;
3332
allowIPs: string;
3433
bindDomain: string;
3534
securityEntrance: string;

frontend/src/lang/modules/en.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1551,11 +1551,6 @@ const message = {
15511551
bindDomain: 'Bind Domain',
15521552
unBindDomain: 'Unbind domain',
15531553
panelSSL: 'Panel SSL',
1554-
sslAutoRestart: 'Restart 1Panel service after certificate auto-renewal',
1555-
sslChangeHelper1:
1556-
'Currently, automatic restart of 1Panel service is not selected. The certificate auto-renewal will not take effect immediately and will still require a manual restart of 1Panel.',
1557-
sslChangeHelper2:
1558-
'The 1Panel service will automatically restart after setting the panel SSL. Do you want to continue?',
15591554
unBindDomainHelper:
15601555
'The action of unbinding a domain name may cause system insecurity. Do you want to continue?',
15611556
bindDomainHelper:

frontend/src/lang/modules/tw.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1494,9 +1494,6 @@ const message = {
14941494
bindDomain: '域名綁定',
14951495
unBindDomain: '域名解綁',
14961496
panelSSL: '面板 SSL',
1497-
sslAutoRestart: '證書自動續期後重啟 1Panel 服務',
1498-
sslChangeHelper1: '當前未勾選自動重啟 1Panel 服務,證書自動續期後不會立即生效,仍需手動重啟 1Panel。',
1499-
sslChangeHelper2: '設置面板 SSL 後將自動重啟 1Panel 服務,是否繼續?',
15001497
unBindDomainHelper: '解除域名綁定可能造成系統不安全,是否繼續?',
15011498
bindDomainHelper: '設置域名綁定後,僅能通過設置中域名訪問 1Panel 服務',
15021499
bindDomainHelper1: '綁定域名為空時,則取消域名綁定',

frontend/src/lang/modules/zh.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1496,9 +1496,6 @@ const message = {
14961496
bindDomain: '域名绑定',
14971497
unBindDomain: '域名解绑',
14981498
panelSSL: '面板 SSL',
1499-
sslAutoRestart: '证书自动续期后重启 1Panel 服务',
1500-
sslChangeHelper1: '当前未勾选自动重启 1Panel 服务,证书自动续期后不会立即生效,仍需手动重启 1Panel。',
1501-
sslChangeHelper2: '设置面板 SSL 后将自动重启 1Panel 服务,是否继续?',
15021499
unBindDomainHelper: '解除域名绑定可能造成系统不安全,是否继续?',
15031500
bindDomainHelper: '设置域名绑定后,仅能通过设置中域名访问 1Panel 服务',
15041501
bindDomainHelper1: '绑定域名为空时,则取消域名绑定',

0 commit comments

Comments
 (0)