feat(system): Fix issue with certificate failure in settings panel (#8063)

This commit is contained in:
zhengkunwang 2025-03-04 22:37:20 +08:00 committed by GitHub
parent 6092e768ef
commit 7a6e5376c4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 173 additions and 11 deletions

View file

@ -113,11 +113,12 @@ func (b *BaseApi) ObtainWebsiteCA(c *gin.Context) {
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if _, err := websiteCAService.ObtainSSL(req); err != nil {
res, err := websiteCAService.ObtainSSL(req)
if err != nil {
helper.InternalServer(c, err)
return
}
helper.SuccessWithOutData(c)
helper.SuccessWithData(c, res)
}
// @Tags Website CA

47
core/app/model/agent.go Normal file
View file

@ -0,0 +1,47 @@
package model
import (
"time"
)
type WebsiteSSL struct {
BaseModel
PrimaryDomain string `json:"primaryDomain"`
PrivateKey string `json:"privateKey"`
Pem string `json:"pem"`
Domains string `json:"domains"`
CertURL string `json:"certURL"`
Type string `json:"type"`
Provider string `json:"provider"`
Organization string `json:"organization"`
DnsAccountID uint `json:"dnsAccountId"`
AcmeAccountID uint `gorm:"column:acme_account_id" json:"acmeAccountId" `
CaID uint `json:"caId"`
AutoRenew bool `json:"autoRenew"`
ExpireDate time.Time `json:"expireDate"`
StartDate time.Time `json:"startDate"`
Status string `json:"status"`
Message string `json:"message"`
KeyType string `json:"keyType"`
PushDir bool `json:"pushDir"`
Dir string `json:"dir"`
Description string `json:"description"`
SkipDNS bool `json:"skipDNS"`
Nameserver1 string `json:"nameserver1"`
Nameserver2 string `json:"nameserver2"`
DisableCNAME bool `json:"disableCNAME"`
ExecShell bool `json:"execShell"`
Shell string `json:"shell"`
}
func (w WebsiteSSL) TableName() string {
return "website_ssls"
}
type WebsiteCA struct {
BaseModel
CSR string `gorm:"not null;" json:"csr"`
Name string `gorm:"not null;" json:"name"`
PrivateKey string `gorm:"not null" json:"privateKey"`
KeyType string `gorm:"not null;default:2048" json:"keyType"`
}

37
core/app/repo/agent.go Normal file
View file

@ -0,0 +1,37 @@
package repo
import (
"github.com/1Panel-dev/1Panel/core/app/model"
"github.com/1Panel-dev/1Panel/core/global"
)
type AgentRepo struct{}
type IAgentRepo interface {
GetWebsiteSSL(opts ...global.DBOption) (model.WebsiteSSL, error)
GetCA(opts ...global.DBOption) (model.WebsiteCA, error)
}
func NewIAgentRepo() IAgentRepo {
return &AgentRepo{}
}
func (a *AgentRepo) GetWebsiteSSL(opts ...global.DBOption) (model.WebsiteSSL, error) {
var ssl model.WebsiteSSL
db := global.AgentDB
for _, opt := range opts {
db = opt(db)
}
err := db.First(&ssl).Error
return ssl, err
}
func (a *AgentRepo) GetCA(opts ...global.DBOption) (model.WebsiteCA, error) {
var ca model.WebsiteCA
db := global.AgentDB
for _, opt := range opts {
db = opt(db)
}
err := db.First(&ca).Error
return ca, err
}

View file

@ -13,4 +13,6 @@ var (
upgradeLogRepo = repo.NewIUpgradeLogRepo()
taskRepo = repo.NewITaskRepo()
agentRepo = repo.NewIAgentRepo()
)

View file

@ -1,6 +1,7 @@
package service
import (
"bytes"
"crypto/rand"
"crypto/rsa"
"crypto/tls"
@ -8,7 +9,10 @@ import (
"encoding/json"
"encoding/pem"
"fmt"
"github.com/1Panel-dev/1Panel/core/app/model"
"github.com/1Panel-dev/1Panel/core/utils/req_helper"
"net"
"net/http"
"os"
"path"
"strconv"
@ -265,6 +269,48 @@ func (u *SettingService) UpdateSSL(c *gin.Context, req dto.SSLUpdate) error {
return err
}
secret = string(certFile)
case "select":
ssl, err := agentRepo.GetWebsiteSSL(repo.WithByID(req.SSLID))
if err != nil {
return err
}
secret = ssl.Pem
key = ssl.PrivateKey
if err := settingRepo.Update("SSLID", strconv.Itoa(int(req.SSLID))); err != nil {
return err
}
case "self":
ca, err := agentRepo.GetCA(repo.WithByName("1Panel"))
if err != nil {
return err
}
params := make(map[string]interface{})
params["domains"] = req.Domain
params["time"] = 10
params["unit"] = "year"
params["keyType"] = "P256"
params["id"] = ca.ID
jsonData, err := json.Marshal(params)
if err != nil {
return err
}
res, err := req_helper.NewLocalClient("/api/v2/websites/ca/obtain", http.MethodPost, bytes.NewReader(jsonData))
if err != nil {
return err
}
jsonBytes, err := json.Marshal(res)
if err != nil {
return err
}
var ssl model.WebsiteSSL
if err := json.Unmarshal(jsonBytes, &ssl); err != nil {
return err
}
secret = ssl.Pem
key = ssl.PrivateKey
if err := settingRepo.Update("SSLID", strconv.Itoa(int(ssl.ID))); err != nil {
return err
}
}
if err := os.WriteFile(path.Join(secretDir, "server.crt.tmp"), []byte(secret), 0600); err != nil {
@ -325,7 +371,18 @@ func (u *SettingService) LoadFromCert() (*dto.SSLInfo, error) {
keyFile, _ := os.ReadFile(path.Join(global.CONF.Base.InstallDir, "1panel/secret/server.key"))
data.Key = string(keyFile)
case "select":
// TODO select ssl from website
sslID, err := settingRepo.Get(repo.WithByKey("SSLID"))
if err != nil {
return nil, err
}
id, _ := strconv.Atoi(sslID.Value)
ssl, err := agentRepo.GetWebsiteSSL(repo.WithByID(uint(id)))
if err != nil {
return nil, err
}
data.Domain = ssl.PrimaryDomain
data.SSLID = uint(id)
data.Timeout = ssl.ExpireDate.Format(constant.DateTimeLayout)
}
return &data, nil
}

View file

@ -13,6 +13,7 @@ import (
var (
DB *gorm.DB
TaskDB *gorm.DB
AgentDB *gorm.DB
LOG *logrus.Logger
CONF ServerConfig
Api ApiInterface

View file

@ -10,4 +10,5 @@ import (
func Init() {
global.DB = common.LoadDBConnByPath(path.Join(global.CONF.Base.InstallDir, "1panel/db/core.db"), "core")
global.TaskDB = common.LoadDBConnByPath(path.Join(global.CONF.Base.InstallDir, "1panel/db/task.db"), "task")
global.AgentDB = common.LoadDBConnByPath(path.Join(global.CONF.Base.InstallDir, "1panel/db/agent.db"), "agent")
}

View file

@ -27,7 +27,9 @@ class RequestHttp {
'Accept-Language': language,
...config.headers,
};
config.headers.CurrentNode = globalStore.currentNode;
if (config.headers.CurrentNode == undefined) {
config.headers.CurrentNode = globalStore.currentNode;
}
if (config.url === '/core/auth/login' || config.url === '/core/auth/mfalogin') {
let entrance = Base64.encode(globalStore.entrance);
config.headers.EntranceCode = entrance;
@ -125,6 +127,16 @@ class RequestHttp {
withCredentials: true,
});
}
postLocalNode<T>(url: string, params?: object, timeout?: number): Promise<ResultData<T>> {
return this.service.post(url, params, {
baseURL: import.meta.env.VITE_API_URL as string,
timeout: timeout ? timeout : (ResultEnum.TIMEOUT as number),
withCredentials: true,
headers: {
CurrentNode: 'local',
},
});
}
put<T>(url: string, params?: object, _object = {}): Promise<ResultData<T>> {
return this.service.put(url, params, _object);
}

View file

@ -110,6 +110,10 @@ export const listSSL = (req: Website.SSLReq) => {
return http.post<Website.SSLDTO[]>(`/websites/ssl/search`, req);
};
export const listLocalNodeSSL = (req: Website.SSLReq) => {
return http.postLocalNode<Website.SSLDTO[]>(`/websites/ssl/search`, req);
};
export const createSSL = (req: Website.SSLCreate) => {
return http.post<Website.SSLCreate>(`/websites/ssl`, req, TimeoutEnum.T_10M);
};

View file

@ -27,10 +27,10 @@
<el-form-item v-if="form.timeout">
<el-tag>{{ $t('setting.domainOrIP') }} {{ form.domain }}</el-tag>
<el-tag style="margin-left: 5px">{{ $t('setting.timeOut') }} {{ form.timeout }}</el-tag>
<el-tag class="p-ml-5">{{ $t('setting.timeOut') }} {{ form.timeout }}</el-tag>
<el-button
@click="onDownload"
style="margin-left: 5px"
class="p-ml-5"
v-if="form.sslType === 'self'"
type="primary"
link
@ -116,7 +116,7 @@
<script lang="ts" setup>
import { Website } from '@/api/interface/website';
import { dateFormatSimple, getProvider } from '@/utils/util';
import { listSSL } from '@/api/modules/website';
import { listLocalNodeSSL } from '@/api/modules/website';
import { reactive, ref } from 'vue';
import i18n from '@/lang';
import { MsgSuccess } from '@/utils/message';
@ -131,7 +131,7 @@ const loading = ref();
const drawerVisible = ref();
const form = reactive({
ssl: 'enable',
ssl: 'Enable',
domain: '',
sslType: 'self',
itemSSLType: 'paste',
@ -172,7 +172,7 @@ const acceptParams = async (params: DialogProps): Promise<void> => {
if (params.sslInfo?.sslID) {
form.sslID = params.sslInfo.sslID;
const ssls = await listSSL({});
const ssls = await listLocalNodeSSL({});
sslList.value = ssls.data || [];
changeSSl(params.sslInfo?.sslID);
} else {
@ -183,7 +183,7 @@ const acceptParams = async (params: DialogProps): Promise<void> => {
const emit = defineEmits<{ (e: 'search'): void }>();
const loadSSLs = async () => {
const res = await listSSL({});
const res = await listLocalNodeSSL({});
sslList.value = res.data || [];
};
@ -228,7 +228,7 @@ const onSaveSSL = async (formEl: FormInstance | undefined) => {
itemType = form.itemSSLType === 'paste' ? 'import-paste' : 'import-local';
}
let param = {
ssl: 'enable',
ssl: 'Enable',
sslType: itemType,
domain: '',
sslID: form.sslID,