mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-03-18 21:29:26 +08:00
feat(system-security): Add API for Synchronizing System Certificates (#7533)
This commit is contained in:
parent
ddd8b6b63d
commit
d103813311
@ -488,7 +488,9 @@ func (a AppService) Install(req request.AppInstallCreate) (appInstall *model.App
|
|||||||
if err = runScript(t, appInstall, "init"); err != nil {
|
if err = runScript(t, appInstall, "init"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
upApp(t, appInstall, req.PullImage)
|
if err = upApp(t, appInstall, req.PullImage); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
updateToolApp(appInstall)
|
updateToolApp(appInstall)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -1022,7 +1022,7 @@ func checkContainerNameIsExist(containerName, appDir string) (bool, error) {
|
|||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func upApp(task *task.Task, appInstall *model.AppInstall, pullImages bool) {
|
func upApp(task *task.Task, appInstall *model.AppInstall, pullImages bool) error {
|
||||||
upProject := func(appInstall *model.AppInstall) (err error) {
|
upProject := func(appInstall *model.AppInstall) (err error) {
|
||||||
var (
|
var (
|
||||||
out string
|
out string
|
||||||
@ -1079,14 +1079,6 @@ func upApp(task *task.Task, appInstall *model.AppInstall, pullImages bool) {
|
|||||||
task.LogSuccess(logStr)
|
task.LogSuccess(logStr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err := upProject(appInstall); err != nil {
|
|
||||||
if appInstall.Message == "" {
|
|
||||||
appInstall.Message = err.Error()
|
|
||||||
}
|
|
||||||
appInstall.Status = constant.UpErr
|
|
||||||
} else {
|
|
||||||
appInstall.Status = constant.Running
|
|
||||||
}
|
|
||||||
exist, _ := appInstallRepo.GetFirst(commonRepo.WithByID(appInstall.ID))
|
exist, _ := appInstallRepo.GetFirst(commonRepo.WithByID(appInstall.ID))
|
||||||
if exist.ID > 0 {
|
if exist.ID > 0 {
|
||||||
containerNames, err := getContainerNames(*appInstall)
|
containerNames, err := getContainerNames(*appInstall)
|
||||||
@ -1097,6 +1089,16 @@ func upApp(task *task.Task, appInstall *model.AppInstall, pullImages bool) {
|
|||||||
_ = appInstallRepo.Save(context.Background(), appInstall)
|
_ = appInstallRepo.Save(context.Background(), appInstall)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if err := upProject(appInstall); err != nil {
|
||||||
|
if appInstall.Message == "" {
|
||||||
|
appInstall.Message = err.Error()
|
||||||
|
}
|
||||||
|
appInstall.Status = constant.UpErr
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
appInstall.Status = constant.Running
|
||||||
|
return nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func rebuildApp(appInstall model.AppInstall) error {
|
func rebuildApp(appInstall model.AppInstall) error {
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
http2 "github.com/1Panel-dev/1Panel/agent/utils/http"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
@ -204,6 +205,10 @@ func reloadSystemSSL(websiteSSL *model.WebsiteSSL, logger *log.Logger) {
|
|||||||
logger.Printf("Failed to update the SSL certificate for 1Panel System domain [%s] , err:%s", websiteSSL.PrimaryDomain, err.Error())
|
logger.Printf("Failed to update the SSL certificate for 1Panel System domain [%s] , err:%s", websiteSSL.PrimaryDomain, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if err := http2.PostLocalCore("/core/settings/ssl/reload"); err != nil {
|
||||||
|
logger.Printf("Failed to update the SSL certificate for 1Panel System domain [%s] , err:%s", websiteSSL.PrimaryDomain, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
printSSLLog(logger, "UpdateSystemSSLSuccess", nil, logger == nil)
|
printSSLLog(logger, "UpdateSystemSSLSuccess", nil, logger == nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -125,4 +125,5 @@ func Init() {
|
|||||||
_, _ = bundle.LoadMessageFileFS(fs, "lang/zh.yaml")
|
_, _ = bundle.LoadMessageFileFS(fs, "lang/zh.yaml")
|
||||||
_, _ = bundle.LoadMessageFileFS(fs, "lang/en.yaml")
|
_, _ = bundle.LoadMessageFileFS(fs, "lang/en.yaml")
|
||||||
_, _ = bundle.LoadMessageFileFS(fs, "lang/zh-Hant.yaml")
|
_, _ = bundle.LoadMessageFileFS(fs, "lang/zh-Hant.yaml")
|
||||||
|
global.I18n = i18n.NewLocalizer(bundle, "en")
|
||||||
}
|
}
|
||||||
|
@ -7,8 +7,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func Init() {
|
func Init() {
|
||||||
//TODO 国际化处理
|
go syncApp()
|
||||||
//go syncApp()
|
|
||||||
go syncInstalledApp()
|
go syncInstalledApp()
|
||||||
go syncRuntime()
|
go syncRuntime()
|
||||||
go syncSSL()
|
go syncSSL()
|
||||||
|
38
agent/utils/http/core.go
Normal file
38
agent/utils/http/core.go
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package http
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/app/repo"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func PostLocalCore(url string) error {
|
||||||
|
settingRepo := repo.NewISettingRepo()
|
||||||
|
port, err := settingRepo.GetValueByKey("ServerPort")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
sslStatus, err := settingRepo.GetValueByKey("SSL")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var prefix string
|
||||||
|
if sslStatus == "Disable" {
|
||||||
|
prefix = "http://"
|
||||||
|
} else {
|
||||||
|
prefix = "https://"
|
||||||
|
}
|
||||||
|
reloadURL := fmt.Sprintf("%s://127.0.0.1:%s/api/v2%s", prefix, port, url)
|
||||||
|
req, err := http.NewRequest("POST", reloadURL, bytes.NewBuffer([]byte{}))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
client := &http.Client{}
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
return nil
|
||||||
|
}
|
@ -369,3 +369,16 @@ func (b *BaseApi) MFABind(c *gin.Context) {
|
|||||||
|
|
||||||
helper.SuccessWithData(c, nil)
|
helper.SuccessWithData(c, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *BaseApi) ReloadSSL(c *gin.Context) {
|
||||||
|
clientIP := c.ClientIP()
|
||||||
|
if clientIP != "127.0.0.1" {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, errors.New("only localhost can reload ssl"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := settingService.UpdateSystemSSL(); err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithOutData(c)
|
||||||
|
}
|
||||||
|
@ -40,6 +40,8 @@ type ISettingService interface {
|
|||||||
|
|
||||||
GetTerminalInfo() (*dto.TerminalInfo, error)
|
GetTerminalInfo() (*dto.TerminalInfo, error)
|
||||||
UpdateTerminal(req dto.TerminalInfo) error
|
UpdateTerminal(req dto.TerminalInfo) error
|
||||||
|
|
||||||
|
UpdateSystemSSL() error
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewISettingService() ISettingService {
|
func NewISettingService() ISettingService {
|
||||||
@ -198,15 +200,6 @@ func (u *SettingService) UpdateSSL(c *gin.Context, req dto.SSLUpdate) error {
|
|||||||
}
|
}
|
||||||
_ = os.Remove(path.Join(secretDir, "server.crt"))
|
_ = os.Remove(path.Join(secretDir, "server.crt"))
|
||||||
_ = os.Remove(path.Join(secretDir, "server.key"))
|
_ = os.Remove(path.Join(secretDir, "server.key"))
|
||||||
sID, _ := c.Cookie(constant.SessionName)
|
|
||||||
c.SetCookie(constant.SessionName, sID, 0, "", "", false, true)
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
_, err := cmd.Exec("systemctl restart 1panel.service")
|
|
||||||
if err != nil {
|
|
||||||
global.LOG.Errorf("restart system failed, err: %v", err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if _, err := os.Stat(secretDir); err != nil && os.IsNotExist(err) {
|
if _, err := os.Stat(secretDir); err != nil && os.IsNotExist(err) {
|
||||||
@ -257,17 +250,7 @@ func (u *SettingService) UpdateSSL(c *gin.Context, req dto.SSLUpdate) error {
|
|||||||
if err := settingRepo.Update("SSL", req.SSL); err != nil {
|
if err := settingRepo.Update("SSL", req.SSL); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
return u.UpdateSystemSSL()
|
||||||
sID, _ := c.Cookie(constant.SessionName)
|
|
||||||
c.SetCookie(constant.SessionName, sID, 0, "", "", true, true)
|
|
||||||
go func() {
|
|
||||||
time.Sleep(1 * time.Second)
|
|
||||||
_, 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) {
|
func (u *SettingService) LoadFromCert() (*dto.SSLInfo, error) {
|
||||||
@ -394,6 +377,25 @@ func (u *SettingService) UpdatePassword(c *gin.Context, old, new string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *SettingService) UpdateSystemSSL() error {
|
||||||
|
certPath := path.Join(global.CONF.System.BaseDir, "1panel/secret/server.crt")
|
||||||
|
keyPath := path.Join(global.CONF.System.BaseDir, "1panel/secret/server.key")
|
||||||
|
certificate, err := os.ReadFile(certPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
key, err := os.ReadFile(keyPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cert, err := tls.X509KeyPair(certificate, key)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
constant.CertStore.Store(&cert)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func loadInfoFromCert() (dto.SSLInfo, error) {
|
func loadInfoFromCert() (dto.SSLInfo, error) {
|
||||||
var info dto.SSLInfo
|
var info dto.SSLInfo
|
||||||
certFile := path.Join(global.CONF.System.BaseDir, "1panel/secret/server.crt")
|
certFile := path.Join(global.CONF.System.BaseDir, "1panel/secret/server.crt")
|
||||||
|
@ -16,6 +16,8 @@ func (s *SettingRouter) InitRouter(Router *gin.RouterGroup) {
|
|||||||
Use(middleware.JwtAuth()).
|
Use(middleware.JwtAuth()).
|
||||||
Use(middleware.SessionAuth()).
|
Use(middleware.SessionAuth()).
|
||||||
Use(middleware.PasswordExpired())
|
Use(middleware.PasswordExpired())
|
||||||
|
|
||||||
|
noAuthRouter := Router.Group("settings")
|
||||||
baseApi := v2.ApiGroupApp.BaseApi
|
baseApi := v2.ApiGroupApp.BaseApi
|
||||||
{
|
{
|
||||||
router.POST("/search", baseApi.GetSettingInfo)
|
router.POST("/search", baseApi.GetSettingInfo)
|
||||||
@ -39,5 +41,7 @@ func (s *SettingRouter) InitRouter(Router *gin.RouterGroup) {
|
|||||||
settingRouter.POST("/upgrade", baseApi.Upgrade)
|
settingRouter.POST("/upgrade", baseApi.Upgrade)
|
||||||
settingRouter.POST("/upgrade/notes", baseApi.GetNotesByVersion)
|
settingRouter.POST("/upgrade/notes", baseApi.GetNotesByVersion)
|
||||||
settingRouter.GET("/upgrade", baseApi.GetUpgradeInfo)
|
settingRouter.GET("/upgrade", baseApi.GetUpgradeInfo)
|
||||||
|
|
||||||
|
noAuthRouter.POST("/ssl/reload", baseApi.ReloadSSL)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -73,9 +73,11 @@ func Start() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
constant.CertStore.Store(&cert)
|
||||||
|
|
||||||
server.TLSConfig = &tls.Config{
|
server.TLSConfig = &tls.Config{
|
||||||
GetCertificate: func(info *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
GetCertificate: func(info *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||||
return &cert, nil
|
return constant.CertStore.Load().(*tls.Certificate), nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
global.LOG.Infof("listen at https://%s:%s [%s]", global.CONF.System.BindAddress, global.CONF.System.Port, tcpItem)
|
global.LOG.Infof("listen at https://%s:%s [%s]", global.CONF.System.BindAddress, global.CONF.System.Port, tcpItem)
|
||||||
|
@ -72,6 +72,12 @@ export const routes: RouteRecordRaw[] = [
|
|||||||
key: 'login',
|
key: 'login',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/:code?',
|
||||||
|
name: 'entrance',
|
||||||
|
component: () => import('@/views/login/entrance/index.vue'),
|
||||||
|
props: true,
|
||||||
|
},
|
||||||
...routerArray,
|
...routerArray,
|
||||||
{
|
{
|
||||||
path: '/:pathMatch(.*)',
|
path: '/:pathMatch(.*)',
|
||||||
|
165
frontend/src/views/login/entrance/index.vue
Normal file
165
frontend/src/views/login/entrance/index.vue
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div v-if="init">
|
||||||
|
<div v-if="errStatus === ''">
|
||||||
|
<div class="login-background">
|
||||||
|
<div class="login-wrapper">
|
||||||
|
<div :class="screenWidth > 1110 ? 'left inline-block' : ''">
|
||||||
|
<div class="login-title">
|
||||||
|
<span>{{ globalStore.themeConfig.title || $t('setting.description') }}</span>
|
||||||
|
</div>
|
||||||
|
<img src="@/assets/images/1panel-login.png" alt="" v-if="screenWidth > 1110" />
|
||||||
|
</div>
|
||||||
|
<div :class="screenWidth > 1110 ? 'right inline-block' : ''">
|
||||||
|
<div class="login-container">
|
||||||
|
<LoginForm ref="loginRef"></LoginForm>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else>
|
||||||
|
<div v-if="errStatus.indexOf('code-') !== -1">
|
||||||
|
<ErrCode :code="errStatus.replaceAll('code-', '')" />
|
||||||
|
</div>
|
||||||
|
<div v-if="errStatus === 'err-found'">
|
||||||
|
<ErrFound />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts" name="login">
|
||||||
|
import LoginForm from '../components/login-form.vue';
|
||||||
|
import ErrCode from '@/components/error-message/error_code.vue';
|
||||||
|
import ErrFound from '@/components/error-message/404.vue';
|
||||||
|
import { ref, onMounted } from 'vue';
|
||||||
|
import { GlobalStore } from '@/store';
|
||||||
|
import { getXpackSettingForTheme } from '@/utils/xpack';
|
||||||
|
const globalStore = GlobalStore();
|
||||||
|
|
||||||
|
const screenWidth = ref(null);
|
||||||
|
const errStatus = ref('x');
|
||||||
|
const init = ref(false);
|
||||||
|
|
||||||
|
const mySafetyCode = defineProps({
|
||||||
|
code: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const getStatus = async () => {
|
||||||
|
let code = mySafetyCode.code;
|
||||||
|
if (code != '') {
|
||||||
|
globalStore.entrance = code;
|
||||||
|
}
|
||||||
|
await getXpackSettingForTheme();
|
||||||
|
let info = globalStore.errStatus;
|
||||||
|
if (info?.startsWith('err-') || info?.startsWith('code-')) {
|
||||||
|
errStatus.value = info;
|
||||||
|
init.value = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
errStatus.value = '';
|
||||||
|
init.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
globalStore.isOnRestart = false;
|
||||||
|
getStatus();
|
||||||
|
screenWidth.value = document.body.clientWidth;
|
||||||
|
window.onresize = () => {
|
||||||
|
return (() => {
|
||||||
|
screenWidth.value = document.body.clientWidth;
|
||||||
|
})();
|
||||||
|
};
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@mixin login-center {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-background {
|
||||||
|
height: 100vh;
|
||||||
|
background: url(@/assets/images/1panel-login-bg.png) no-repeat,
|
||||||
|
radial-gradient(153.25% 257.2% at 118.99% 181.67%, rgba(50, 132, 255, 0.2) 0%, rgba(82, 120, 255, 0) 100%)
|
||||||
|
/* warning: gradient uses a rotation that is not supported by CSS and may not behave as expected */,
|
||||||
|
radial-gradient(123.54% 204.83% at 25.87% 195.17%, rgba(111, 76, 253, 0.15) 0%, rgba(122, 76, 253, 0) 78.85%)
|
||||||
|
/* warning: gradient uses a rotation that is not supported by CSS and may not behave as expected */,
|
||||||
|
linear-gradient(0deg, rgba(0, 94, 235, 0.03), rgba(0, 94, 235, 0.03)),
|
||||||
|
radial-gradient(109.58% 109.58% at 31.53% -36.58%, rgba(0, 94, 235, 0.3) 0%, rgba(0, 94, 235, 0) 100%)
|
||||||
|
/* warning: gradient uses a rotation that is not supported by CSS and may not behave as expected */,
|
||||||
|
rgba(0, 57, 142, 0.05);
|
||||||
|
|
||||||
|
.login-wrapper {
|
||||||
|
padding-top: 8%;
|
||||||
|
width: 80%;
|
||||||
|
margin: 0 auto;
|
||||||
|
// @media only screen and (max-width: 1440px) {
|
||||||
|
// width: 100%;
|
||||||
|
// padding-top: 6%;
|
||||||
|
// }
|
||||||
|
.left {
|
||||||
|
vertical-align: middle;
|
||||||
|
text-align: right;
|
||||||
|
width: 60%;
|
||||||
|
img {
|
||||||
|
object-fit: contain;
|
||||||
|
width: 100%;
|
||||||
|
@media only screen and (min-width: 1440px) {
|
||||||
|
width: 85%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.right {
|
||||||
|
vertical-align: middle;
|
||||||
|
width: 40%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-title {
|
||||||
|
text-align: right;
|
||||||
|
margin-right: 10%;
|
||||||
|
span:first-child {
|
||||||
|
color: #005eeb;
|
||||||
|
font-size: 40px;
|
||||||
|
font-family: pingFangSC-Regular;
|
||||||
|
font-weight: 600;
|
||||||
|
@media only screen and (max-width: 768px) {
|
||||||
|
font-size: 35px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media only screen and (max-width: 1110px) {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
font-size: 35px;
|
||||||
|
text-align: center;
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.login-container {
|
||||||
|
margin-top: 40px;
|
||||||
|
padding: 40px 0;
|
||||||
|
width: 390px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background-color: rgba(255, 255, 255, 0.55);
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: 2px 4px 22px rgba(0, 94, 235, 0.2);
|
||||||
|
@media only screen and (max-width: 1440px) {
|
||||||
|
margin-top: 60px;
|
||||||
|
}
|
||||||
|
@media only screen and (max-width: 1110px) {
|
||||||
|
margin: 60px auto 0;
|
||||||
|
}
|
||||||
|
@media only screen and (max-width: 768px) {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
Loading…
x
Reference in New Issue
Block a user