feat(provider): new acme dns-01 provider: rucenter

This commit is contained in:
Fu Diwei
2026-05-29 23:47:06 +08:00
committed by RHQYZ
parent 0be80d7303
commit ee2ac23ee1
12 changed files with 219 additions and 16 deletions
@@ -0,0 +1,29 @@
package certifiers
import (
"fmt"
"github.com/certimate-go/certimate/internal/domain"
"github.com/certimate-go/certimate/pkg/core"
"github.com/certimate-go/certimate/pkg/core/certifier/challengers/dns01/rucenter"
xmaps "github.com/certimate-go/certimate/pkg/utils/maps"
)
func init() {
ACMEDns01Registries.MustRegister(domain.ACMEDns01ProviderTypeRuCenter, func(options *ProviderFactoryOptions) (core.ACMEChallenger, error) {
credentials := domain.AccessConfigForRuCenter{}
if err := xmaps.Populate(options.ProviderAccessConfig, &credentials); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
provider, err := rucenter.NewChallenger(&rucenter.ChallengerConfig{
Username: credentials.Username,
Password: credentials.Password,
ApplicationId: credentials.ApplicationId,
ApplicationToken: credentials.ApplicationToken,
DnsPropagationTimeout: options.DnsPropagationTimeout,
DnsTTL: options.DnsTTL,
})
return provider, err
})
}
+7
View File
@@ -500,6 +500,13 @@ type AccessConfigForRFC2136 struct {
TsigSecret string `json:"tsigSecret,omitempty"`
}
type AccessConfigForRuCenter struct {
Username string `json:"username"`
Password string `json:"password"`
ApplicationId string `json:"applicationId"`
ApplicationToken string `json:"applicationToken"`
}
type AccessConfigForS3 struct {
Endpoint string `json:"endpoint"`
AccessKey string `json:"accessKey"`
+2
View File
@@ -106,6 +106,7 @@ const (
AccessProviderTypeRatPanel = AccessProviderType("ratpanel")
AccessProviderTypeRegru = AccessProviderType("regru")
AccessProviderTypeRFC2136 = AccessProviderType("rfc2136")
AccessProviderTypeRuCenter = AccessProviderType("rucenter")
AccessProviderTypeS3 = AccessProviderType("s3")
AccessProviderTypeSafeLine = AccessProviderType("safeline")
AccessProviderTypeSamWAF = AccessProviderType("samwaf")
@@ -246,6 +247,7 @@ const (
ACMEDns01ProviderTypeRainYun = ACMEDns01ProviderType(AccessProviderTypeRainYun)
ACMEDns01ProviderTypeRegru = ACMEDns01ProviderType(AccessProviderTypeRegru)
ACMEDns01ProviderTypeRFC2136 = ACMEDns01ProviderType(AccessProviderTypeRFC2136)
ACMEDns01ProviderTypeRuCenter = ACMEDns01ProviderType(AccessProviderTypeRuCenter)
ACMEDns01ProviderTypeSpaceship = ACMEDns01ProviderType(AccessProviderTypeSpaceship)
ACMEDns01ProviderTypeTechnitiumDNS = ACMEDns01ProviderType(AccessProviderTypeTechnitiumDNS)
ACMEDns01ProviderTypeTencentCloud = ACMEDns01ProviderType(AccessProviderTypeTencentCloud) // 兼容旧值,等同于 [ACMEDns01ProviderTypeTencentCloudDNS]
@@ -0,0 +1,44 @@
package rucenter
import (
"fmt"
"time"
"github.com/go-acme/lego/v5/providers/dns/nicru"
"github.com/certimate-go/certimate/pkg/core/certifier"
)
type ChallengerConfig struct {
Username string `json:"username"`
Password string `json:"password"`
ApplicationId string `json:"applicationId"`
ApplicationToken string `json:"applicationToken"`
DnsPropagationTimeout int `json:"dnsPropagationTimeout,omitempty"`
DnsTTL int `json:"dnsTTL,omitempty"`
}
func NewChallenger(config *ChallengerConfig) (certifier.ACMEChallenger, error) {
if config == nil {
return nil, fmt.Errorf("the configuration of the acme challenge provider is nil")
}
providerConfig := nicru.NewDefaultConfig()
providerConfig.Username = config.Username
providerConfig.Password = config.Password
providerConfig.ServiceID = config.ApplicationId
providerConfig.Secret = config.ApplicationToken
if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
}
if config.DnsTTL != 0 {
providerConfig.TTL = config.DnsTTL
}
provider, err := nicru.NewDNSProviderConfig(providerConfig)
if err != nil {
return nil, err
}
return provider, nil
}
+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="-2 2 43 43" width="200" height="200"><path fill="#798E96" d="M18.675 13.16c0 5.148-4.18 9.32-9.338 9.32C4.182 22.48 0 18.309 0 13.16c0-5.148 4.18-9.32 9.338-9.32 5.157 0 9.337 4.172 9.337 9.32ZM29.752 22.481c-5.157 0-9.338-4.173-9.338-9.32 0-5.149 4.18-9.322 9.338-9.322 5.156 0 9.337 4.173 9.337 9.321s-4.18 9.321-9.337 9.321ZM9.337 42.854c5.157 0 9.338-4.173 9.338-9.321s-4.18-9.321-9.338-9.321C4.181 24.212 0 28.385 0 33.532c0 5.149 4.18 9.322 9.337 9.322Z"></path></svg>
@@ -90,6 +90,7 @@ import AccessConfigFieldsProviderRainYun from "./AccessConfigFieldsProviderRainY
import AccessConfigFieldsProviderRatPanel from "./AccessConfigFieldsProviderRatPanel";
import AccessConfigFieldsProviderRegru from "./AccessConfigFieldsProviderRegru";
import AccessConfigFieldsProviderRFC2136 from "./AccessConfigFieldsProviderRFC2136";
import AccessConfigFieldsProviderRuCenter from "./AccessConfigFieldsProviderRuCenter";
import AccessConfigFieldsProviderS3 from "./AccessConfigFieldsProviderS3";
import AccessConfigFieldsProviderSafeLine from "./AccessConfigFieldsProviderSafeLine";
import AccessConfigFieldsProviderSamWAF from "./AccessConfigFieldsProviderSamWAF";
@@ -210,6 +211,7 @@ const providerComponentMap: Partial<Record<AccessProviderType, React.ComponentTy
[ACCESS_PROVIDERS.RATPANEL]: AccessConfigFieldsProviderRatPanel,
[ACCESS_PROVIDERS.REGRU]: AccessConfigFieldsProviderRegru,
[ACCESS_PROVIDERS.RFC2136]: AccessConfigFieldsProviderRFC2136,
[ACCESS_PROVIDERS.RUCENTER]: AccessConfigFieldsProviderRuCenter,
[ACCESS_PROVIDERS.S3]: AccessConfigFieldsProviderS3,
[ACCESS_PROVIDERS.SAFELINE]: AccessConfigFieldsProviderSafeLine,
[ACCESS_PROVIDERS.SAMWAF]: AccessConfigFieldsProviderSamWAF,
@@ -0,0 +1,76 @@
import { getI18n, useTranslation } from "react-i18next";
import { Form, Input } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import { useFormNestedFieldsContext } from "./_context";
const AccessConfigFormFieldsProviderRuCenter = () => {
const { i18n, t } = useTranslation();
const { parentNamePath } = useFormNestedFieldsContext();
const formSchema = z.object({
[parentNamePath]: getSchema({ i18n }),
});
const formRule = createSchemaFieldRule(formSchema);
const initialValues = getInitialValues();
return (
<>
<Form.Item name={[parentNamePath, "username"]} initialValue={initialValues.username} label={t("access.form.rucenter_username.label")} rules={[formRule]}>
<Input autoComplete="new-password" placeholder={t("access.form.rucenter_username.placeholder")} />
</Form.Item>
<Form.Item name={[parentNamePath, "password"]} initialValue={initialValues.password} label={t("access.form.rucenter_password.label")} rules={[formRule]}>
<Input.Password autoComplete="new-password" placeholder={t("access.form.rucenter_password.placeholder")} />
</Form.Item>
<Form.Item
name={[parentNamePath, "applicationId"]}
initialValue={initialValues.applicationId}
label={t("access.form.rucenter_application_id.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.rucenter_application_id.tooltip") }}></span>}
>
<Input placeholder={t("access.form.rucenter_application_id.placeholder")} />
</Form.Item>
<Form.Item
name={[parentNamePath, "applicationToken"]}
initialValue={initialValues.applicationToken}
label={t("access.form.rucenter_application_token.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.rucenter_application_token.tooltip") }}></span>}
>
<Input.Password placeholder={t("access.form.rucenter_application_token.placeholder")} />
</Form.Item>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
username: "",
password: "",
applicationId: "",
applicationToken: "",
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const { t: _ } = i18n;
return z.object({
username: z.union([z.string().endsWith("/NIC-D"), z.string().endsWith("/NIC-REG")]),
password: z.string().nonempty(),
applicationId: z.string().length(32),
applicationToken: z.string().length(91),
});
};
const _default = Object.assign(AccessConfigFormFieldsProviderRuCenter, {
getInitialValues,
getSchema,
});
export default _default;
+4
View File
@@ -106,6 +106,7 @@ export const ACCESS_PROVIDERS = Object.freeze({
RATPANEL: "ratpanel",
REGRU: "regru",
RFC2136: "rfc2136",
RUCENTER: "rucenter",
S3: "s3",
SAFELINE: "safeline",
SAMWAF: "samwaf",
@@ -242,6 +243,7 @@ export const accessProvidersMap: Map<AccessProvider["type"] | string, AccessProv
[ACCESS_PROVIDERS.OVHCLOUD, "provider.ovhcloud", "/imgs/providers/ovhcloud.svg", [ACCESS_USAGES.DNS]],
[ACCESS_PROVIDERS.PORKBUN, "provider.porkbun", "/imgs/providers/porkbun.svg", [ACCESS_USAGES.DNS]],
[ACCESS_PROVIDERS.REGRU, "provider.regru", "/imgs/providers/regru.svg", [ACCESS_USAGES.DNS]],
[ACCESS_PROVIDERS.RUCENTER, "provider.rucenter", "/imgs/providers/rucenter.svg", [ACCESS_USAGES.DNS]],
[ACCESS_PROVIDERS.SPACESHIP, "provider.spaceship", "/imgs/providers/spaceship.png", [ACCESS_USAGES.DNS]],
[ACCESS_PROVIDERS.VULTR, "provider.vultr", "/imgs/providers/vultr.svg", [ACCESS_USAGES.DNS]],
[ACCESS_PROVIDERS.CMCCCLOUD, "provider.cmcccloud", "/imgs/providers/cmcccloud.svg", [ACCESS_USAGES.DNS]],
@@ -421,6 +423,7 @@ export const ACME_DNS01_PROVIDERS = Object.freeze({
RAINYUN: `${ACCESS_PROVIDERS.RAINYUN}`,
REGRU: `${ACCESS_PROVIDERS.REGRU}`,
RFC2136: `${ACCESS_PROVIDERS.RFC2136}`,
RUCENTER: `${ACCESS_PROVIDERS.RUCENTER}`,
SPACESHIP: `${ACCESS_PROVIDERS.SPACESHIP}`,
UCLOUD: `${ACCESS_PROVIDERS.UCLOUD}`, // 兼容旧值,等同于 `UCLOUD_UDNR`
UCLOUD_UDNR: `${ACCESS_PROVIDERS.UCLOUD}-udnr`,
@@ -494,6 +497,7 @@ export const acmeDns01ProvidersMap: Map<ACMEDns01Provider["type"] | string, ACME
[ACME_DNS01_PROVIDERS.OVHCLOUD, "provider.ovhcloud"],
[ACME_DNS01_PROVIDERS.PORKBUN, "provider.porkbun"],
[ACME_DNS01_PROVIDERS.REGRU, "provider.regru"],
[ACME_DNS01_PROVIDERS.RUCENTER, "provider.rucenter"],
[ACME_DNS01_PROVIDERS.SPACESHIP, "provider.spaceship"],
[ACME_DNS01_PROVIDERS.VERCEL, "provider.vercel"],
[ACME_DNS01_PROVIDERS.VULTR, "provider.vultr"],
+26 -8
View File
@@ -1075,24 +1075,24 @@
"tooltip": "For more information, see <a href=\"https://ratpanel.github.io/advanced/api.html\" target=\"_blank\">https://ratpanel.github.io/advanced/api.html</a>"
},
"regru_username": {
"label": "Рег.ру username",
"placeholder": "Please enter Рег.ру username"
"label": "Reg.ru username",
"placeholder": "Please enter Reg.ru username"
},
"regru_password": {
"label": "Рег.ру password",
"placeholder": "Please enter Рег.ру password or API password"
"label": "Reg.ru password",
"placeholder": "Please enter Reg.ru password or API password"
},
"regru_mtls_certificate": {
"label": "Рег.ру mTLS certificate (Optional)",
"placeholder": "Please enter Рег.ру mTLS certificate",
"label": "Reg.ru mTLS certificate (Optional)",
"placeholder": "Please enter Reg.ru mTLS certificate",
"errmsg": {
"invalid": "Invalid certificate"
},
"tooltip": "For more information, see <a href=\"https://www.reg.ru/user/account/#/settings/api/\" target=\"_blank\">https://www.reg.ru/user/account/#/settings/api/api/</a>"
},
"regru_mtls_private_key": {
"label": "Рег.ру mTLS private key (Optional)",
"placeholder": "Please enter Рег.ру mTLS private key",
"label": "Reg.ru mTLS private key (Optional)",
"placeholder": "Please enter Reg.ru mTLS private key",
"errmsg": {
"invalid": "Invalid private key"
},
@@ -1118,6 +1118,24 @@
"label": "TSIG authentication secret (Optional)",
"placeholder": "Please enter TSIG authentication secret"
},
"rucenter_username": {
"label": "Ru-Center username",
"placeholder": "Please enter Ru-Center username"
},
"rucenter_password": {
"label": "Ru-Center password",
"placeholder": "Please enter Ru-Center password"
},
"rucenter_application_id": {
"label": "Ru-Center application ID",
"placeholder": "Please enter Ru-Center application ID",
"tooltip": "For more information, see <a href=\"https://www.nic.ru/manager/oauth.cgi?step=oauth.app_list\" target=\"_blank\">https://www.nic.ru/manager/oauth.cgi?step=oauth.app_list</a>"
},
"rucenter_application_token": {
"label": "Ru-Center application token",
"placeholder": "Please enter Ru-Center application token",
"tooltip": "For more information, see <a href=\"https://www.nic.ru/manager/oauth.cgi?step=oauth.app_list\" target=\"_blank\">https://www.nic.ru/manager/oauth.cgi?step=oauth.app_list</a>"
},
"s3_endpoint": {
"label": "Endpoint",
"placeholder": "Please enter endpoint",
@@ -200,6 +200,7 @@
"ratpanel_console": "AcePanel - Console itself",
"regru": "Рег.ру (Reg.ru)",
"rfc2136": "Dynamic DNS Updates (RFC 2136)",
"rucenter": "Руцентр (Ru-Center)",
"s3": "Object storage (S3-compatible)",
"s3_upload": "Upload to S3-compatible object storage",
"safeline": "SafeLine",
+26 -8
View File
@@ -1073,24 +1073,24 @@
"tooltip": "这是什么?请参阅 <a href=\"https://ratpanel.github.io/advanced/api.html\" target=\"_blank\">https://ratpanel.github.io/advanced/api.html</a>"
},
"regru_username": {
"label": "Рег.ру 用户名",
"placeholder": "请输入 Рег.ру 用户名"
"label": "Reg.ru 用户名",
"placeholder": "请输入 Reg.ru 用户名"
},
"regru_password": {
"label": "Рег.ру 密码",
"placeholder": "请输入 Рег.ру 密码或 API 密码"
"label": "Reg.ru 密码",
"placeholder": "请输入 Reg.ru 密码或 API 密码"
},
"regru_mtls_certificate": {
"label": "Рег.ру SSL 双向认证证书(可选)",
"placeholder": "请输入 Рег.ру SSL 双向认证证书",
"label": "Reg.ru SSL 双向认证证书(可选)",
"placeholder": "请输入 Reg.ru SSL 双向认证证书",
"errmsg": {
"invalid": "无效的证书"
},
"tooltip": "这是什么?请参阅 <a href=\"https://www.reg.ru/user/account/#/settings/api/\" target=\"_blank\">https://www.reg.ru/user/account/#/settings/api/api/</a>"
},
"regru_mtls_private_key": {
"label": "Рег.ру SSL 双向认证私钥(可选)",
"placeholder": "请输入 Рег.ру SSL 双向认证私钥",
"label": "Reg.ru SSL 双向认证私钥(可选)",
"placeholder": "请输入 Reg.ru SSL 双向认证私钥",
"errmsg": {
"invalid": "无效的私钥"
},
@@ -1116,6 +1116,24 @@
"label": "TSIG 认证密钥 Secret(可选)",
"placeholder": "请输入 TSIG 认证密钥 Secret"
},
"rucenter_username": {
"label": "Ru-Center 用户名",
"placeholder": "请输入 Ru-Center 用户名"
},
"rucenter_password": {
"label": "Ru-Center 密码",
"placeholder": "请输入 Ru-Center 密码"
},
"rucenter_application_id": {
"label": "Ru-Center 应用服务 ID",
"placeholder": "请输入 Ru-Center 应用服务 ID",
"tooltip": "这是什么?请参阅 <a href=\"https://www.nic.ru/manager/oauth.cgi?step=oauth.app_list\" target=\"_blank\">https://www.nic.ru/manager/oauth.cgi?step=oauth.app_list</a>"
},
"rucenter_application_token": {
"label": "Ru-Center 应用服务 Token",
"placeholder": "请输入 Ru-Center 应用服务 Token",
"tooltip": "这是什么?请参阅 <a href=\"https://www.nic.ru/manager/oauth.cgi?step=oauth.app_list\" target=\"_blank\">https://www.nic.ru/manager/oauth.cgi?step=oauth.app_list</a>"
},
"s3_endpoint": {
"label": "终端节点",
"placeholder": "请输入终端节点",
@@ -200,6 +200,7 @@
"ratpanel_console": "耗子面板 - 面板自身",
"regru": "Рег.ру(REG.ru)",
"rfc2136": "Dynamic DNS Updates (RFC 2136)",
"rucenter": "Руцентр(Ru-Center)",
"s3": "对象存储(S3 兼容)",
"s3_upload": "上传到 S3 兼容的对象存储",
"safeline": "雷池",