1
0
mirror of https://github.com/1Panel-dev/1Panel.git synced 2025-01-19 08:19:15 +08:00

feat: 证书申请增加配置,提高申请成功率 (#4903)

This commit is contained in:
zhengkunwang 2024-05-07 18:26:34 +08:00 committed by GitHub
parent 43ebf6eef9
commit b489f5cf2f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 133 additions and 20 deletions

View File

@ -20,6 +20,10 @@ type WebsiteSSLCreate struct {
Dir string `json:"dir"`
ID uint `json:"id"`
Description string `json:"description"`
DisableCNAME bool `json:"disableCNAME"`
SkipDNS bool `json:"skipDNS"`
Nameserver1 string `json:"nameserver1"`
Nameserver2 string `json:"nameserver2"`
}
type WebsiteDNSReq struct {
@ -79,6 +83,10 @@ type WebsiteSSLUpdate struct {
Apply bool `json:"apply"`
PushDir bool `json:"pushDir"`
Dir string `json:"dir"`
DisableCNAME bool `json:"disableCNAME"`
SkipDNS bool `json:"skipDNS"`
Nameserver1 string `json:"nameserver1"`
Nameserver2 string `json:"nameserver2"`
}
type WebsiteSSLUpload struct {

View File

@ -29,6 +29,10 @@ type WebsiteSSL struct {
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"`
AcmeAccount WebsiteAcmeAccount `json:"acmeAccount" gorm:"-:migration"`
DnsAccount WebsiteDnsAccount `json:"dnsAccount" gorm:"-:migration"`

View File

@ -102,6 +102,12 @@ func (w WebsiteSSLService) Search(search request.WebsiteSSLSearch) ([]response.W
}
func (w WebsiteSSLService) Create(create request.WebsiteSSLCreate) (request.WebsiteSSLCreate, error) {
if create.Nameserver1 != "" && !common.IsValidIP(create.Nameserver1) {
return create, buserr.New("ErrParseIP")
}
if create.Nameserver2 != "" && !common.IsValidIP(create.Nameserver2) {
return create, buserr.New("ErrParseIP")
}
var res request.WebsiteSSLCreate
acmeAccount, err := websiteAcmeRepo.GetFirst(commonRepo.WithByID(create.AcmeAccountID))
if err != nil {
@ -116,6 +122,10 @@ func (w WebsiteSSLService) Create(create request.WebsiteSSLCreate) (request.Webs
KeyType: create.KeyType,
PushDir: create.PushDir,
Description: create.Description,
Nameserver1: create.Nameserver1,
Nameserver2: create.Nameserver2,
SkipDNS: create.SkipDNS,
DisableCNAME: create.DisableCNAME,
}
if create.PushDir {
if !files.NewFileOp().Stat(create.Dir) {
@ -191,7 +201,7 @@ func (w WebsiteSSLService) ObtainSSL(apply request.WebsiteSSLApply) error {
if err != nil {
return err
}
if err = client.UseDns(ssl.DnsType(dnsAccount.Type), dnsAccount.Authorization, apply.SkipDNSCheck, apply.Nameservers); err != nil {
if err = client.UseDns(ssl.DnsType(dnsAccount.Type), dnsAccount.Authorization, *websiteSSL); err != nil {
return err
}
case constant.Http:
@ -374,6 +384,10 @@ func (w WebsiteSSLService) Update(update request.WebsiteSSLUpdate) error {
updateParams["provider"] = update.Provider
updateParams["key_type"] = update.KeyType
updateParams["push_dir"] = update.PushDir
updateParams["disable_cname"] = update.DisableCNAME
updateParams["skip_dns"] = update.SkipDNS
updateParams["nameserver1"] = update.Nameserver1
updateParams["nameserver2"] = update.Nameserver2
acmeAccount, err := websiteAcmeRepo.GetFirst(commonRepo.WithByID(update.AcmeAccountID))
if err != nil {

View File

@ -82,6 +82,8 @@ func Init() {
migrations.UpdateXpackHideMenu,
migrations.AddMenuTabsSetting,
migrations.AddDeveloperSetting,
migrations.AddWebsiteSSLColumn,
})
if err := m.Migrate(); err != nil {
global.LOG.Error(err)

View File

@ -154,3 +154,13 @@ var AddDeveloperSetting = &gormigrate.Migration{
return nil
},
}
var AddWebsiteSSLColumn = &gormigrate.Migration{
ID: "20240508-update-website-ssl",
Migrate: func(tx *gorm.DB) error {
if err := tx.AutoMigrate(&model.WebsiteSSL{}); err != nil {
return err
}
return nil
},
}

View File

@ -317,3 +317,7 @@ func SplitStr(str string, spi ...string) []string {
}
return results
}
func IsValidIP(ip string) bool {
return net.ParseIP(ip) != nil
}

View File

@ -3,6 +3,7 @@ package ssl
import (
"crypto"
"encoding/json"
"os"
"time"
"github.com/1Panel-dev/1Panel/backend/app/model"
@ -84,7 +85,7 @@ type DNSParam struct {
SecretID string `json:"secretID"`
}
func (c *AcmeClient) UseDns(dnsType DnsType, params string, skipDNSCheck bool, nameservers []string) error {
func (c *AcmeClient) UseDns(dnsType DnsType, params string, websiteSSL model.WebsiteSSL) error {
var (
param DNSParam
p challenge.Provider
@ -162,11 +163,23 @@ func (c *AcmeClient) UseDns(dnsType DnsType, params string, skipDNSCheck bool, n
if err != nil {
return err
}
var nameservers []string
if websiteSSL.Nameserver1 != "" {
nameservers = append(nameservers, websiteSSL.Nameserver1)
}
if websiteSSL.Nameserver2 != "" {
nameservers = append(nameservers, websiteSSL.Nameserver2)
}
if websiteSSL.DisableCNAME {
_ = os.Setenv("LEGO_DISABLE_CNAME_SUPPORT", "true")
} else {
_ = os.Setenv("LEGO_DISABLE_CNAME_SUPPORT", "false")
}
return c.Client.Challenge.SetDNS01Provider(p,
dns01.CondOption(len(nameservers) > 0,
dns01.AddRecursiveNameservers(nameservers)),
dns01.CondOption(skipDNSCheck,
dns01.CondOption(websiteSSL.SkipDNS,
dns01.DisableCompletePropagationRequirement()),
dns01.AddDNSTimeout(10*time.Minute),
)

View File

@ -173,6 +173,10 @@ export namespace Website {
pushDir: boolean;
dir: string;
keyType: string;
nameserver1: string;
nameserver2: string;
disableCNAME: boolean;
skipDNS: boolean;
}
export interface SSLDTO extends SSL {
@ -452,7 +456,6 @@ export namespace Website {
export interface SSLObtain {
ID: number;
skipDNSCheck: boolean;
}
export interface CA extends CommonModel {

View File

@ -22,6 +22,20 @@ const checkIp = (rule: any, value: any, callback: any) => {
}
};
const checkIpv4 = (rule: any, value: any, callback: any) => {
if (value === '' || typeof value === 'undefined' || value == null) {
callback();
} else {
const reg =
/^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/;
if (!reg.test(value) && value !== '') {
callback(new Error(i18n.global.t('commons.rule.ip')));
} else {
callback();
}
}
};
const checkIpV6 = (rule: any, value: any, callback: any) => {
if (value === '' || typeof value === 'undefined' || value == null) {
callback(new Error(i18n.global.t('commons.rule.requiredInput')));
@ -533,6 +547,7 @@ interface CommonRule {
floatNumber: FormItemRule;
ip: FormItemRule;
ipV6: FormItemRule;
ipv4: FormItemRule;
ipV4V6OrDomain: FormItemRule;
host: FormItemRule;
illegal: FormItemRule;
@ -768,4 +783,8 @@ export const Rules: CommonRule = {
validator: checkHttpOrHttps,
trigger: 'blur',
},
ipv4: {
validator: checkIpv4,
trigger: 'blur',
},
};

View File

@ -2062,6 +2062,10 @@ const message = {
deprecated: 'will be deprecated',
deprecatedHelper:
'Maintenance has been stopped and may be abandoned in a future version. Please use Tencent Cloud method for analysis',
disableCNAME: 'Disable CNAME',
disableCNAMEHelper: 'Domain name with CNAME configuration, if the application fails, you can check here',
nameserver: 'DNS server',
nameserverHelper: 'Use a custom DNS server to verify domain names',
},
firewall: {
create: 'Create rule',

View File

@ -1929,6 +1929,10 @@ const message = {
cfHelper: '請勿使用 Global API Key',
deprecated: '即將廢棄',
deprecatedHelper: '已經停止維護可能會在以後的某個版本廢棄請使用騰訊雲方式解析',
disableCNAME: '停用 CNAME',
disableCNAMEHelper: ' CNAME 配置的域名如果申請失敗可以勾選此處',
nameserver: 'DNS 伺服器',
nameserverHelper: '使用自訂的 DNS 伺服器來校驗網域名稱',
},
firewall: {
create: '創建規則',

View File

@ -1929,6 +1929,10 @@ const message = {
cfHelper: '请勿使用 Global API Key',
deprecated: '即将废弃',
deprecatedHelper: '已经停止维护可能会在以后的某个版本废弃请使用腾讯云方式解析',
disableCNAME: '禁用 CNAME',
disableCNAMEHelper: ' CNAME 配置的域名如果申请失败可以勾选此处',
nameserver: 'DNS 服务器',
nameserverHelper: '使用自定义的 DNS 服务器来校验域名',
},
firewall: {
create: '创建规则',

View File

@ -31,11 +31,6 @@
</el-table>
</div>
<div class="mt-3">
<el-checkbox v-model="skipDNSCheck">{{ $t('ssl.skipDNSCheck') }}</el-checkbox>
<span class="input-help">{{ $t('ssl.skipDNSCheckHelper') }}</span>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="handleClose" :disabled="loading">{{ $t('commons.button.cancel') }}</el-button>
@ -67,13 +62,11 @@ const handleClose = () => {
open.value = false;
em('close', false);
};
const skipDNSCheck = ref(false);
const acceptParams = async (props: RenewProps) => {
open.value = true;
dnsResolve.value = [];
sslID.value = props.ssl.id;
skipDNSCheck.value = false;
getDnsResolve(props.ssl);
};
@ -96,7 +89,7 @@ const getDnsResolve = async (row: Website.SSL) => {
};
const submit = () => {
ObtainSSL({ ID: sslID.value, skipDNSCheck: skipDNSCheck.value })
ObtainSSL({ ID: sslID.value })
.then(() => {
MsgSuccess(i18n.global.t('ssl.applyStart'));
handleClose();

View File

@ -119,6 +119,30 @@
{{ $t('ssl.pushDirHelper') }}
</span>
</el-form-item>
<el-form-item :label="''" prop="disableCNAME">
<el-checkbox v-model="ssl.disableCNAME" :label="$t('ssl.disableCNAME')" />
<span class="input-help">
{{ $t('ssl.disableCNAMEHelper') }}
</span>
</el-form-item>
<el-form-item :label="''" prop="skipDNS">
<el-checkbox v-model="ssl.skipDNS" :label="$t('ssl.skipDNSCheck')" />
<span class="input-help">
{{ $t('ssl.skipDNSCheckHelper') }}
</span>
</el-form-item>
<el-form-item :label="$t('ssl.nameserver') + '1'" prop="nameserver1">
<el-input v-model.trim="ssl.nameserver1"></el-input>
<span class="input-help">
{{ $t('ssl.nameserverHelper') }}
</span>
</el-form-item>
<el-form-item :label="$t('ssl.nameserver') + '2'" prop="nameserver1">
<el-input v-model.trim="ssl.nameserver2"></el-input>
<span class="input-help">
{{ $t('ssl.nameserverHelper') }}
</span>
</el-form-item>
</el-form>
</el-col>
</el-row>
@ -178,6 +202,8 @@ const rules = ref({
autoRenew: [Rules.requiredInput],
keyType: [Rules.requiredInput],
dir: [Rules.requiredInput],
nameserver1: [Rules.ipv4],
nameserver2: [Rules.ipv4],
});
const websiteID = ref();
@ -194,6 +220,10 @@ const initData = () => ({
pushDir: false,
dir: '',
description: '',
disableCNAME: false,
skipDNS: false,
nameserver1: '',
nameserver2: '',
});
const ssl = ref(initData());
@ -231,6 +261,10 @@ const acceptParams = (op: string, websiteSSL: Website.SSLDTO) => {
ssl.value.description = websiteSSL.description;
ssl.value.id = websiteSSL.id;
ssl.value.provider = websiteSSL.provider;
ssl.value.skipDNS = websiteSSL.skipDNS;
ssl.value.disableCNAME = websiteSSL.disableCNAME;
ssl.value.nameserver1 = websiteSSL.nameserver1;
ssl.value.nameserver2 = websiteSSL.nameserver2;
}
ssl.value.websiteId = Number(id.value);
getAcmeAccounts();
@ -318,6 +352,10 @@ const submit = async (formEl: FormInstance | undefined) => {
dir: ssl.value.dir,
description: ssl.value.description,
provider: ssl.value.provider,
disableCNAME: ssl.value.disableCNAME,
skipDNS: ssl.value.skipDNS,
nameserver1: ssl.value.nameserver1,
nameserver2: ssl.value.nameserver2,
};
UpdateSSL(sslUpdate)
.then(() => {

View File

@ -127,7 +127,6 @@ const rules = ref<any>({
secretKey: [Rules.requiredInput],
id: [Rules.requiredInput],
token: [Rules.requiredInput],
email: [Rules.requiredInput],
apiKey: [Rules.requiredInput],
apiUser: [Rules.requiredInput],
secretID: [Rules.requiredInput],

View File

@ -21,10 +21,6 @@
<br />
</div>
<span>{{ $t('ssl.renewConfirm', [ssl.primaryDomain]) }}</span>
<div class="mt-3">
<el-checkbox v-model="skipDNSCheck">{{ $t('ssl.skipDNSCheck') }}</el-checkbox>
<span class="input-help">{{ $t('ssl.skipDNSCheckHelper') }}</span>
</div>
</div>
<template #footer>
<span class="dialog-footer">
@ -56,12 +52,10 @@ const handleClose = () => {
em('close', false);
};
const ssl = ref();
const skipDNSCheck = ref(false);
const acceptParams = async (props: RenewProps) => {
ssl.value = props.ssl;
open.value = true;
skipDNSCheck.value = false;
};
const submit = async () => {
@ -70,7 +64,7 @@ const submit = async () => {
if (ssl.value.provider == 'selfSigned') {
await RenewSSLByCA({ SSLID: ssl.value.id });
} else {
await ObtainSSL({ ID: ssl.value.id, skipDNSCheck: skipDNSCheck.value });
await ObtainSSL({ ID: ssl.value.id });
}
handleClose();
MsgSuccess(i18n.global.t('ssl.applyStart'));