1
0
mirror of https://github.com/1Panel-dev/1Panel.git synced 2025-01-31 14:08:06 +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"` Dir string `json:"dir"`
ID uint `json:"id"` ID uint `json:"id"`
Description string `json:"description"` Description string `json:"description"`
DisableCNAME bool `json:"disableCNAME"`
SkipDNS bool `json:"skipDNS"`
Nameserver1 string `json:"nameserver1"`
Nameserver2 string `json:"nameserver2"`
} }
type WebsiteDNSReq struct { type WebsiteDNSReq struct {
@ -79,6 +83,10 @@ type WebsiteSSLUpdate struct {
Apply bool `json:"apply"` Apply bool `json:"apply"`
PushDir bool `json:"pushDir"` PushDir bool `json:"pushDir"`
Dir string `json:"dir"` Dir string `json:"dir"`
DisableCNAME bool `json:"disableCNAME"`
SkipDNS bool `json:"skipDNS"`
Nameserver1 string `json:"nameserver1"`
Nameserver2 string `json:"nameserver2"`
} }
type WebsiteSSLUpload struct { type WebsiteSSLUpload struct {

View File

@ -29,6 +29,10 @@ type WebsiteSSL struct {
PushDir bool `json:"pushDir"` PushDir bool `json:"pushDir"`
Dir string `json:"dir"` Dir string `json:"dir"`
Description string `json:"description"` 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"` AcmeAccount WebsiteAcmeAccount `json:"acmeAccount" gorm:"-:migration"`
DnsAccount WebsiteDnsAccount `json:"dnsAccount" 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) { 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 var res request.WebsiteSSLCreate
acmeAccount, err := websiteAcmeRepo.GetFirst(commonRepo.WithByID(create.AcmeAccountID)) acmeAccount, err := websiteAcmeRepo.GetFirst(commonRepo.WithByID(create.AcmeAccountID))
if err != nil { if err != nil {
@ -116,6 +122,10 @@ func (w WebsiteSSLService) Create(create request.WebsiteSSLCreate) (request.Webs
KeyType: create.KeyType, KeyType: create.KeyType,
PushDir: create.PushDir, PushDir: create.PushDir,
Description: create.Description, Description: create.Description,
Nameserver1: create.Nameserver1,
Nameserver2: create.Nameserver2,
SkipDNS: create.SkipDNS,
DisableCNAME: create.DisableCNAME,
} }
if create.PushDir { if create.PushDir {
if !files.NewFileOp().Stat(create.Dir) { if !files.NewFileOp().Stat(create.Dir) {
@ -191,7 +201,7 @@ func (w WebsiteSSLService) ObtainSSL(apply request.WebsiteSSLApply) error {
if err != nil { if err != nil {
return err 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 return err
} }
case constant.Http: case constant.Http:
@ -374,6 +384,10 @@ func (w WebsiteSSLService) Update(update request.WebsiteSSLUpdate) error {
updateParams["provider"] = update.Provider updateParams["provider"] = update.Provider
updateParams["key_type"] = update.KeyType updateParams["key_type"] = update.KeyType
updateParams["push_dir"] = update.PushDir 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)) acmeAccount, err := websiteAcmeRepo.GetFirst(commonRepo.WithByID(update.AcmeAccountID))
if err != nil { if err != nil {

View File

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

View File

@ -154,3 +154,13 @@ var AddDeveloperSetting = &gormigrate.Migration{
return nil 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 return results
} }
func IsValidIP(ip string) bool {
return net.ParseIP(ip) != nil
}

View File

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

View File

@ -173,6 +173,10 @@ export namespace Website {
pushDir: boolean; pushDir: boolean;
dir: string; dir: string;
keyType: string; keyType: string;
nameserver1: string;
nameserver2: string;
disableCNAME: boolean;
skipDNS: boolean;
} }
export interface SSLDTO extends SSL { export interface SSLDTO extends SSL {
@ -452,7 +456,6 @@ export namespace Website {
export interface SSLObtain { export interface SSLObtain {
ID: number; ID: number;
skipDNSCheck: boolean;
} }
export interface CA extends CommonModel { 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) => { const checkIpV6 = (rule: any, value: any, callback: any) => {
if (value === '' || typeof value === 'undefined' || value == null) { if (value === '' || typeof value === 'undefined' || value == null) {
callback(new Error(i18n.global.t('commons.rule.requiredInput'))); callback(new Error(i18n.global.t('commons.rule.requiredInput')));
@ -533,6 +547,7 @@ interface CommonRule {
floatNumber: FormItemRule; floatNumber: FormItemRule;
ip: FormItemRule; ip: FormItemRule;
ipV6: FormItemRule; ipV6: FormItemRule;
ipv4: FormItemRule;
ipV4V6OrDomain: FormItemRule; ipV4V6OrDomain: FormItemRule;
host: FormItemRule; host: FormItemRule;
illegal: FormItemRule; illegal: FormItemRule;
@ -768,4 +783,8 @@ export const Rules: CommonRule = {
validator: checkHttpOrHttps, validator: checkHttpOrHttps,
trigger: 'blur', trigger: 'blur',
}, },
ipv4: {
validator: checkIpv4,
trigger: 'blur',
},
}; };

View File

@ -2062,6 +2062,10 @@ const message = {
deprecated: 'will be deprecated', deprecated: 'will be deprecated',
deprecatedHelper: deprecatedHelper:
'Maintenance has been stopped and may be abandoned in a future version. Please use Tencent Cloud method for analysis', '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: { firewall: {
create: 'Create rule', create: 'Create rule',

View File

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

View File

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

View File

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

View File

@ -119,6 +119,30 @@
{{ $t('ssl.pushDirHelper') }} {{ $t('ssl.pushDirHelper') }}
</span> </span>
</el-form-item> </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-form>
</el-col> </el-col>
</el-row> </el-row>
@ -178,6 +202,8 @@ const rules = ref({
autoRenew: [Rules.requiredInput], autoRenew: [Rules.requiredInput],
keyType: [Rules.requiredInput], keyType: [Rules.requiredInput],
dir: [Rules.requiredInput], dir: [Rules.requiredInput],
nameserver1: [Rules.ipv4],
nameserver2: [Rules.ipv4],
}); });
const websiteID = ref(); const websiteID = ref();
@ -194,6 +220,10 @@ const initData = () => ({
pushDir: false, pushDir: false,
dir: '', dir: '',
description: '', description: '',
disableCNAME: false,
skipDNS: false,
nameserver1: '',
nameserver2: '',
}); });
const ssl = ref(initData()); const ssl = ref(initData());
@ -231,6 +261,10 @@ const acceptParams = (op: string, websiteSSL: Website.SSLDTO) => {
ssl.value.description = websiteSSL.description; ssl.value.description = websiteSSL.description;
ssl.value.id = websiteSSL.id; ssl.value.id = websiteSSL.id;
ssl.value.provider = websiteSSL.provider; 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); ssl.value.websiteId = Number(id.value);
getAcmeAccounts(); getAcmeAccounts();
@ -318,6 +352,10 @@ const submit = async (formEl: FormInstance | undefined) => {
dir: ssl.value.dir, dir: ssl.value.dir,
description: ssl.value.description, description: ssl.value.description,
provider: ssl.value.provider, provider: ssl.value.provider,
disableCNAME: ssl.value.disableCNAME,
skipDNS: ssl.value.skipDNS,
nameserver1: ssl.value.nameserver1,
nameserver2: ssl.value.nameserver2,
}; };
UpdateSSL(sslUpdate) UpdateSSL(sslUpdate)
.then(() => { .then(() => {

View File

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

View File

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