mirror of
https://github.com/acepanel/panel.git
synced 2026-02-04 10:17:17 +08:00
feat: 添加GoogleCN证书
This commit is contained in:
2
go.mod
2
go.mod
@@ -130,3 +130,5 @@ require (
|
||||
modernc.org/memory v1.8.0 // indirect
|
||||
modernc.org/sqlite v1.32.0 // indirect
|
||||
)
|
||||
|
||||
replace github.com/mholt/acmez/v2 => github.com/TheTNB/acmez/v2 v2.0.0-20241012154227-1911c2ed4ae4
|
||||
|
||||
4
go.sum
4
go.sum
@@ -23,6 +23,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU=
|
||||
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
||||
github.com/TheTNB/acmez/v2 v2.0.0-20241012154227-1911c2ed4ae4 h1:pQyd4C3Y8MuvvJ0B3d4hDHQpLe2Bb/dW7/GwcIoyCSY=
|
||||
github.com/TheTNB/acmez/v2 v2.0.0-20241012154227-1911c2ed4ae4/go.mod h1:pQ1ysaDeGrIMvJ9dfJMk5kJNkn7L2sb3UhyrX6Q91cw=
|
||||
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
|
||||
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||
github.com/beevik/ntp v1.4.3 h1:PlbTvE5NNy4QHmA4Mg57n7mcFTmr1W1j3gcK7L1lqho=
|
||||
@@ -227,8 +229,6 @@ github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mholt/acmez/v2 v2.0.3 h1:CgDBlEwg3QBp6s45tPQmFIBrkRIkBT4rW4orMM6p4sw=
|
||||
github.com/mholt/acmez/v2 v2.0.3/go.mod h1:pQ1ysaDeGrIMvJ9dfJMk5kJNkn7L2sb3UhyrX6Q91cw=
|
||||
github.com/mholt/archiver/v4 v4.0.0-alpha.8 h1:tRGQuDVPh66WCOelqe6LIGh0gwmfwxUrSSDunscGsRM=
|
||||
github.com/mholt/archiver/v4 v4.0.0-alpha.8/go.mod h1:5f7FUYGXdJWUjESffJaYR4R60VhnHxb2X3T1teMyv5A=
|
||||
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
|
||||
|
||||
@@ -254,6 +254,12 @@ func (r *certRepo) getClient(cert *biz.Cert) (*acme.Client, error) {
|
||||
var ca string
|
||||
var eab *acme.EAB
|
||||
switch cert.Account.CA {
|
||||
case "googlecn":
|
||||
ca = acme.CAGoogleCN
|
||||
eab = &acme.EAB{KeyID: cert.Account.Kid, MACKey: cert.Account.HmacEncoded}
|
||||
case "google":
|
||||
ca = acme.CAGoogle
|
||||
eab = &acme.EAB{KeyID: cert.Account.Kid, MACKey: cert.Account.HmacEncoded}
|
||||
case "letsencrypt":
|
||||
ca = acme.CALetsEncrypt
|
||||
case "buypass":
|
||||
@@ -264,9 +270,6 @@ func (r *certRepo) getClient(cert *biz.Cert) (*acme.Client, error) {
|
||||
case "sslcom":
|
||||
ca = acme.CASSLcom
|
||||
eab = &acme.EAB{KeyID: cert.Account.Kid, MACKey: cert.Account.HmacEncoded}
|
||||
case "google":
|
||||
ca = acme.CAGoogle
|
||||
eab = &acme.EAB{KeyID: cert.Account.Kid, MACKey: cert.Account.HmacEncoded}
|
||||
}
|
||||
|
||||
return acme.NewPrivateKeyAccount(cert.Account.Email, cert.Account.PrivateKey, ca, eab)
|
||||
|
||||
@@ -3,6 +3,7 @@ package data
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/go-resty/resty/v2"
|
||||
@@ -44,6 +45,14 @@ func (r certAccountRepo) Create(req *request.CertAccountCreate) (*biz.CertAccoun
|
||||
var err error
|
||||
var client *acme.Client
|
||||
switch account.CA {
|
||||
case "googlecn":
|
||||
eab, eabErr := r.getGoogleEAB()
|
||||
if eabErr != nil {
|
||||
return nil, eabErr
|
||||
}
|
||||
client, err = acme.NewRegisterAccount(context.Background(), account.Email, acme.CAGoogleCN, eab, acme.KeyType(account.KeyType))
|
||||
case "google":
|
||||
client, err = acme.NewRegisterAccount(context.Background(), account.Email, acme.CAGoogle, &acme.EAB{KeyID: account.Kid, MACKey: account.HmacEncoded}, acme.KeyType(account.KeyType))
|
||||
case "letsencrypt":
|
||||
client, err = acme.NewRegisterAccount(context.Background(), account.Email, acme.CALetsEncrypt, nil, acme.KeyType(account.KeyType))
|
||||
case "buypass":
|
||||
@@ -56,14 +65,12 @@ func (r certAccountRepo) Create(req *request.CertAccountCreate) (*biz.CertAccoun
|
||||
client, err = acme.NewRegisterAccount(context.Background(), account.Email, acme.CAZeroSSL, eab, acme.KeyType(account.KeyType))
|
||||
case "sslcom":
|
||||
client, err = acme.NewRegisterAccount(context.Background(), account.Email, acme.CASSLcom, &acme.EAB{KeyID: account.Kid, MACKey: account.HmacEncoded}, acme.KeyType(account.KeyType))
|
||||
case "google":
|
||||
client, err = acme.NewRegisterAccount(context.Background(), account.Email, acme.CAGoogle, &acme.EAB{KeyID: account.Kid, MACKey: account.HmacEncoded}, acme.KeyType(account.KeyType))
|
||||
default:
|
||||
return nil, errors.New("CA 提供商不支持")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, errors.New("向 CA 注册账号失败,请检查参数是否正确")
|
||||
return nil, fmt.Errorf("注册账号失败:%v", err)
|
||||
}
|
||||
|
||||
privateKey, err := cert.EncodeKey(client.Account.PrivateKey)
|
||||
@@ -93,6 +100,14 @@ func (r certAccountRepo) Update(req *request.CertAccountUpdate) error {
|
||||
|
||||
var client *acme.Client
|
||||
switch account.CA {
|
||||
case "googlecn":
|
||||
eab, eabErr := r.getGoogleEAB()
|
||||
if eabErr != nil {
|
||||
return eabErr
|
||||
}
|
||||
client, err = acme.NewRegisterAccount(context.Background(), account.Email, acme.CAGoogleCN, eab, acme.KeyType(account.KeyType))
|
||||
case "google":
|
||||
client, err = acme.NewRegisterAccount(context.Background(), account.Email, acme.CAGoogle, &acme.EAB{KeyID: account.Kid, MACKey: account.HmacEncoded}, acme.KeyType(account.KeyType))
|
||||
case "letsencrypt":
|
||||
client, err = acme.NewRegisterAccount(context.Background(), account.Email, acme.CALetsEncrypt, nil, acme.KeyType(account.KeyType))
|
||||
case "buypass":
|
||||
@@ -105,8 +120,6 @@ func (r certAccountRepo) Update(req *request.CertAccountUpdate) error {
|
||||
client, err = acme.NewRegisterAccount(context.Background(), account.Email, acme.CAZeroSSL, eab, acme.KeyType(account.KeyType))
|
||||
case "sslcom":
|
||||
client, err = acme.NewRegisterAccount(context.Background(), account.Email, acme.CASSLcom, &acme.EAB{KeyID: account.Kid, MACKey: account.HmacEncoded}, acme.KeyType(account.KeyType))
|
||||
case "google":
|
||||
client, err = acme.NewRegisterAccount(context.Background(), account.Email, acme.CAGoogle, &acme.EAB{KeyID: account.Kid, MACKey: account.HmacEncoded}, acme.KeyType(account.KeyType))
|
||||
default:
|
||||
return errors.New("CA 提供商不支持")
|
||||
}
|
||||
@@ -128,6 +141,31 @@ func (r certAccountRepo) Delete(id uint) error {
|
||||
return app.Orm.Model(&biz.CertAccount{}).Where("id = ?", id).Delete(&biz.CertAccount{}).Error
|
||||
}
|
||||
|
||||
// getGoogleEAB 获取 Google EAB
|
||||
func (r certAccountRepo) getGoogleEAB() (*acme.EAB, error) {
|
||||
type data struct {
|
||||
Message string `json:"message"`
|
||||
Data struct {
|
||||
KeyId string `json:"key_id"`
|
||||
MacKey string `json:"mac_key"`
|
||||
} `json:"data"`
|
||||
}
|
||||
client := resty.New()
|
||||
client.SetTimeout(5 * time.Second)
|
||||
client.SetRetryCount(2)
|
||||
|
||||
resp, err := client.R().SetResult(&data{}).Get("https://panel.haozi.net/api/acme/googleEAB")
|
||||
if err != nil || !resp.IsSuccess() {
|
||||
return &acme.EAB{}, errors.New("获取Google EAB失败")
|
||||
}
|
||||
eab := resp.Result().(*data)
|
||||
if eab.Message != "success" {
|
||||
return &acme.EAB{}, errors.New("获取Google EAB失败")
|
||||
}
|
||||
|
||||
return &acme.EAB{KeyID: eab.Data.KeyId, MACKey: eab.Data.MacKey}, nil
|
||||
}
|
||||
|
||||
// getZeroSSLEAB 获取 ZeroSSL EAB
|
||||
func (r certAccountRepo) getZeroSSLEAB(email string) (*acme.EAB, error) {
|
||||
type data struct {
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/TheTNB/panel/internal/data"
|
||||
"github.com/TheTNB/panel/internal/http/request"
|
||||
"github.com/TheTNB/panel/pkg/acme"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
)
|
||||
|
||||
type CertService struct {
|
||||
@@ -22,70 +23,74 @@ func NewCertService() *CertService {
|
||||
}
|
||||
|
||||
func (s *CertService) CAProviders(w http.ResponseWriter, r *http.Request) {
|
||||
Success(w, []map[string]string{
|
||||
Success(w, []types.LV{
|
||||
{
|
||||
"name": "Let's Encrypt",
|
||||
"ca": "letsencrypt",
|
||||
Label: "GoogleCN(推荐)",
|
||||
Value: "googlecn",
|
||||
},
|
||||
{
|
||||
"name": "ZeroSSL",
|
||||
"ca": "zerossl",
|
||||
Label: "Let's Encrypt",
|
||||
Value: "letsencrypt",
|
||||
},
|
||||
{
|
||||
"name": "SSL.com",
|
||||
"ca": "sslcom",
|
||||
Label: "ZeroSSL",
|
||||
Value: "zerossl",
|
||||
},
|
||||
{
|
||||
"name": "Google",
|
||||
"ca": "google",
|
||||
Label: "SSL.com",
|
||||
Value: "sslcom",
|
||||
},
|
||||
{
|
||||
"name": "Buypass",
|
||||
"ca": "buypass",
|
||||
Label: "Google",
|
||||
Value: "google",
|
||||
},
|
||||
{
|
||||
Label: "Buypass",
|
||||
Value: "buypass",
|
||||
},
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func (s *CertService) DNSProviders(w http.ResponseWriter, r *http.Request) {
|
||||
Success(w, []map[string]any{
|
||||
Success(w, []types.LV{
|
||||
{
|
||||
"name": "DNSPod",
|
||||
"dns": acme.DnsPod,
|
||||
Label: "DNSPod",
|
||||
Value: string(acme.DnsPod),
|
||||
},
|
||||
{
|
||||
"name": "腾讯云",
|
||||
"dns": acme.Tencent,
|
||||
Label: "腾讯云",
|
||||
Value: string(acme.Tencent),
|
||||
},
|
||||
{
|
||||
"name": "阿里云",
|
||||
"dns": acme.AliYun,
|
||||
Label: "阿里云",
|
||||
Value: string(acme.AliYun),
|
||||
},
|
||||
{
|
||||
"name": "CloudFlare",
|
||||
"dns": acme.CloudFlare,
|
||||
Label: "CloudFlare",
|
||||
Value: string(acme.CloudFlare),
|
||||
},
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func (s *CertService) Algorithms(w http.ResponseWriter, r *http.Request) {
|
||||
Success(w, []map[string]any{
|
||||
Success(w, []types.LV{
|
||||
{
|
||||
"name": "EC256",
|
||||
"key": acme.KeyEC256,
|
||||
Label: "EC256",
|
||||
Value: string(acme.KeyEC256),
|
||||
},
|
||||
{
|
||||
"name": "EC384",
|
||||
"key": acme.KeyEC384,
|
||||
Label: "EC384",
|
||||
Value: string(acme.KeyEC384),
|
||||
},
|
||||
{
|
||||
"name": "RSA2048",
|
||||
"key": acme.KeyRSA2048,
|
||||
Label: "RSA2048",
|
||||
Value: string(acme.KeyRSA2048),
|
||||
},
|
||||
{
|
||||
"name": "RSA4096",
|
||||
"key": acme.KeyRSA4096,
|
||||
Label: "RSA4096",
|
||||
Value: string(acme.KeyRSA4096),
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
@@ -17,10 +17,11 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
CAGoogleCN = "https://panel.haozi.net/api/acme/google/directory"
|
||||
CAGoogle = "https://dv.acme-v02.api.pki.goog/directory"
|
||||
CALetsEncryptStaging = "https://acme-staging-v02.api.letsencrypt.org/directory"
|
||||
CALetsEncrypt = "https://acme-v02.api.letsencrypt.org/directory"
|
||||
CAZeroSSL = "https://acme.zerossl.com/v2/DV90"
|
||||
CAGoogle = "https://dv.acme-v02.api.pki.goog/directory"
|
||||
CABuypass = "https://api.buypass.com/acme/directory"
|
||||
CASSLcom = "https://acme.ssl.com/sslcom-dv-rsa"
|
||||
)
|
||||
|
||||
@@ -18,19 +18,23 @@ const addAccountModel = ref<any>({
|
||||
email: '',
|
||||
kid: '',
|
||||
key_type: 'P256',
|
||||
ca: 'letsencrypt'
|
||||
ca: 'googlecn'
|
||||
})
|
||||
const updateAccountModel = ref<any>({
|
||||
hmac_encoded: '',
|
||||
email: '',
|
||||
kid: '',
|
||||
key_type: 'P256',
|
||||
ca: 'letsencrypt'
|
||||
ca: 'googlecn'
|
||||
})
|
||||
const addAccountModal = ref(false)
|
||||
const updateAccountModal = ref(false)
|
||||
const updateAccount = ref<any>()
|
||||
|
||||
const showEAB = computed(() => {
|
||||
return addAccountModel.value.ca === 'google' || addAccountModel.value.ca === 'sslcom'
|
||||
})
|
||||
|
||||
const caProviders = ref<any>([])
|
||||
const algorithms = ref<any>([])
|
||||
|
||||
@@ -51,20 +55,7 @@ const accountColumns: any = [
|
||||
},
|
||||
{
|
||||
default: () => {
|
||||
switch (row.ca) {
|
||||
case 'letsencrypt':
|
||||
return "Let's Encrypt"
|
||||
case 'zerossl':
|
||||
return 'ZeroSSL'
|
||||
case 'sslcom':
|
||||
return 'SSL.com'
|
||||
case 'buypass':
|
||||
return 'Buypass'
|
||||
case 'google':
|
||||
return 'Google'
|
||||
default:
|
||||
return '未知'
|
||||
}
|
||||
return caProviders.value.find((item: any) => item.value === row.ca)?.label
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -192,20 +183,10 @@ const handleUpdateAccount = async () => {
|
||||
|
||||
onMounted(() => {
|
||||
cert.caProviders().then((res) => {
|
||||
for (const item of res.data) {
|
||||
caProviders.value.push({
|
||||
label: item.name,
|
||||
value: item.ca
|
||||
})
|
||||
}
|
||||
caProviders.value = res.data
|
||||
})
|
||||
cert.algorithms().then((res) => {
|
||||
for (const item of res.data) {
|
||||
algorithms.value.push({
|
||||
label: item.name,
|
||||
value: item.key
|
||||
})
|
||||
}
|
||||
algorithms.value = res.data
|
||||
})
|
||||
onAccountPageChange(1)
|
||||
})
|
||||
@@ -243,7 +224,7 @@ onMounted(() => {
|
||||
<n-space vertical>
|
||||
<n-alert type="info"> Google 和 SSL.com 需要先去官网获得 KID 和 HMAC 并填入 </n-alert>
|
||||
<n-alert type="warning">
|
||||
境内无法使用 Google CA,其他 CA 视网络情况而定,建议使用 Let's Encrypt
|
||||
境内无法使用 Google,其他 CA 视网络情况而定,建议使用 GoogleCN 或 Let's Encrypt
|
||||
</n-alert>
|
||||
<n-form :model="addAccountModel">
|
||||
<n-form-item path="ca" label="CA">
|
||||
@@ -270,7 +251,7 @@ onMounted(() => {
|
||||
placeholder="输入邮箱地址"
|
||||
/>
|
||||
</n-form-item>
|
||||
<n-form-item path="kid" label="KID">
|
||||
<n-form-item v-if="showEAB" path="kid" label="KID">
|
||||
<n-input
|
||||
v-model:value="addAccountModel.kid"
|
||||
type="text"
|
||||
@@ -278,7 +259,7 @@ onMounted(() => {
|
||||
placeholder="输入 KID"
|
||||
/>
|
||||
</n-form-item>
|
||||
<n-form-item path="hmac_encoded" label="HMAC">
|
||||
<n-form-item v-if="showEAB" path="hmac_encoded" label="HMAC">
|
||||
<n-input
|
||||
v-model:value="addAccountModel.hmac_encoded"
|
||||
type="text"
|
||||
@@ -302,7 +283,7 @@ onMounted(() => {
|
||||
<n-space vertical>
|
||||
<n-alert type="info"> Google 和 SSL.com 需要先去官网获得 KID 和 HMAC 并填入 </n-alert>
|
||||
<n-alert type="warning">
|
||||
境内无法使用 Google CA,其他 CA 视网络情况而定,建议使用 Let's Encrypt
|
||||
境内无法使用 Google,其他 CA 视网络情况而定,建议使用 GoogleCN 或 Let's Encrypt
|
||||
</n-alert>
|
||||
<n-form :model="updateAccountModel">
|
||||
<n-form-item path="ca" label="CA">
|
||||
|
||||
@@ -426,12 +426,7 @@ const handleDeployCert = async () => {
|
||||
|
||||
const getAsyncData = async () => {
|
||||
const { data: algorithmData } = await cert.algorithms()
|
||||
for (const item of algorithmData) {
|
||||
algorithms.value.push({
|
||||
label: item.name,
|
||||
value: item.key
|
||||
})
|
||||
}
|
||||
algorithms.value = algorithmData
|
||||
|
||||
const { data: websiteData } = await website.list(1, 10000)
|
||||
websites.value = []
|
||||
|
||||
@@ -187,12 +187,7 @@ const handleUpdateDNS = async () => {
|
||||
|
||||
onMounted(async () => {
|
||||
cert.dnsProviders().then((res) => {
|
||||
for (const item of res.data) {
|
||||
dnsProviders.value.push({
|
||||
label: item.name,
|
||||
value: item.dns
|
||||
})
|
||||
}
|
||||
dnsProviders.value = res.data
|
||||
})
|
||||
onDnsPageChange(1)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user