mirror of
https://github.com/certimate-go/certimate.git
synced 2026-06-22 21:05:48 +08:00
feat(provider): new acme dns-01 provider: rfc2136
This commit is contained in:
parent
1f6ef6dcb3
commit
c2a2ad6cb0
33
internal/certapply/applicators/sp_rfc2136.go
Normal file
33
internal/certapply/applicators/sp_rfc2136.go
Normal file
@ -0,0 +1,33 @@
|
||||
package applicators
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-acme/lego/v4/challenge"
|
||||
|
||||
"github.com/certimate-go/certimate/internal/domain"
|
||||
"github.com/certimate-go/certimate/pkg/core/ssl-applicator/acme-dns01/providers/rfc2136"
|
||||
xmaps "github.com/certimate-go/certimate/pkg/utils/maps"
|
||||
)
|
||||
|
||||
func init() {
|
||||
if err := ACMEDns01Registries.Register(domain.ACMEDns01ProviderTypeRFC2136, func(options *ProviderFactoryOptions) (challenge.Provider, error) {
|
||||
credentials := domain.AccessConfigForRFC2136{}
|
||||
if err := xmaps.Populate(options.ProviderAccessConfig, &credentials); err != nil {
|
||||
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||
}
|
||||
|
||||
provider, err := rfc2136.NewChallengeProvider(&rfc2136.ChallengeProviderConfig{
|
||||
Host: credentials.Host,
|
||||
Port: credentials.Port,
|
||||
TsigAlgorithm: credentials.TsigAlgorithm,
|
||||
TsigKey: credentials.TsigKey,
|
||||
TsigSecret: credentials.TsigSecret,
|
||||
DnsPropagationTimeout: options.DnsPropagationTimeout,
|
||||
DnsTTL: options.DnsTTL,
|
||||
})
|
||||
return provider, err
|
||||
}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
@ -202,6 +202,15 @@ type AccessConfigForGcore struct {
|
||||
ApiToken string `json:"apiToken"`
|
||||
}
|
||||
|
||||
type AccessConfigForGlobalSectigo struct {
|
||||
AccessConfigForACMEExternalAccountBinding
|
||||
ValidationType string `json:"validationType"`
|
||||
}
|
||||
|
||||
type AccessConfigForGlobalSignAtlas struct {
|
||||
AccessConfigForACMEExternalAccountBinding
|
||||
}
|
||||
|
||||
type AccessConfigForGname struct {
|
||||
AppId string `json:"appId"`
|
||||
AppKey string `json:"appKey"`
|
||||
@ -220,10 +229,6 @@ type AccessConfigForGoEdge struct {
|
||||
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
|
||||
}
|
||||
|
||||
type AccessConfigForGlobalSignAtlas struct {
|
||||
AccessConfigForACMEExternalAccountBinding
|
||||
}
|
||||
|
||||
type AccessConfigForGoogleTrustServices struct {
|
||||
AccessConfigForACMEExternalAccountBinding
|
||||
}
|
||||
@ -335,17 +340,20 @@ type AccessConfigForRatPanel struct {
|
||||
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
|
||||
}
|
||||
|
||||
type AccessConfigForRFC2136 struct {
|
||||
Host string `json:"host"`
|
||||
Port int32 `json:"port"`
|
||||
TsigAlgorithm string `json:"tsigAlgorithm,omitempty"`
|
||||
TsigKey string `json:"tsigKey,omitempty"`
|
||||
TsigSecret string `json:"tsigSecret,omitempty"`
|
||||
}
|
||||
|
||||
type AccessConfigForSafeLine struct {
|
||||
ServerUrl string `json:"serverUrl"`
|
||||
ApiToken string `json:"apiToken"`
|
||||
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
|
||||
}
|
||||
|
||||
type AccessConfigForGlobalSectigo struct {
|
||||
AccessConfigForACMEExternalAccountBinding
|
||||
ValidationType string `json:"validationType"`
|
||||
}
|
||||
|
||||
type AccessConfigForSlackBot struct {
|
||||
BotToken string `json:"botToken"`
|
||||
ChannelId string `json:"channelId,omitempty"`
|
||||
|
||||
@ -79,7 +79,7 @@ const (
|
||||
AccessProviderTypeQingCloud = AccessProviderType("qingcloud") // 青云(预留)
|
||||
AccessProviderTypeRainYun = AccessProviderType("rainyun")
|
||||
AccessProviderTypeRatPanel = AccessProviderType("ratpanel")
|
||||
AccessProviderTypeRFC2136 = AccessProviderType("rfc2136") // RFC2136(预留)
|
||||
AccessProviderTypeRFC2136 = AccessProviderType("rfc2136")
|
||||
AccessProviderTypeSafeLine = AccessProviderType("safeline")
|
||||
AccessProviderTypeSectigo = AccessProviderType("sectigo")
|
||||
AccessProviderTypeSlackBot = AccessProviderType("slackbot")
|
||||
@ -174,6 +174,7 @@ const (
|
||||
ACMEDns01ProviderTypePorkbun = ACMEDns01ProviderType(AccessProviderTypePorkbun)
|
||||
ACMEDns01ProviderTypePowerDNS = ACMEDns01ProviderType(AccessProviderTypePowerDNS)
|
||||
ACMEDns01ProviderTypeRainYun = ACMEDns01ProviderType(AccessProviderTypeRainYun)
|
||||
ACMEDns01ProviderTypeRFC2136 = ACMEDns01ProviderType(AccessProviderTypeRFC2136)
|
||||
ACMEDns01ProviderTypeSpaceship = ACMEDns01ProviderType(AccessProviderTypeSpaceship)
|
||||
ACMEDns01ProviderTypeTechnitiumDNS = ACMEDns01ProviderType(AccessProviderTypeTechnitiumDNS)
|
||||
ACMEDns01ProviderTypeTencentCloud = ACMEDns01ProviderType(AccessProviderTypeTencentCloud) // 兼容旧值,等同于 [ACMEDns01ProviderTypeTencentCloudDNS]
|
||||
|
||||
@ -0,0 +1,55 @@
|
||||
package rfc2136
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/providers/dns/rfc2136"
|
||||
|
||||
"github.com/certimate-go/certimate/pkg/core"
|
||||
)
|
||||
|
||||
type ChallengeProviderConfig struct {
|
||||
Host string `json:"host"`
|
||||
Port int32 `json:"port"`
|
||||
TsigAlgorithm string `json:"tsigAlgorithm,omitempty"`
|
||||
TsigKey string `json:"tsigKey,omitempty"`
|
||||
TsigSecret string `json:"tsigSecret,omitempty"`
|
||||
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
|
||||
DnsTTL int32 `json:"dnsTTL,omitempty"`
|
||||
}
|
||||
|
||||
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the acme challenge provider is nil")
|
||||
}
|
||||
|
||||
if config.Port == 0 {
|
||||
config.Port = 53
|
||||
}
|
||||
|
||||
if config.TsigAlgorithm == "" {
|
||||
config.TsigAlgorithm = "hmac-sha1."
|
||||
}
|
||||
|
||||
providerConfig := rfc2136.NewDefaultConfig()
|
||||
providerConfig.Nameserver = net.JoinHostPort(config.Host, strconv.Itoa(int(config.Port)))
|
||||
providerConfig.TSIGAlgorithm = config.TsigAlgorithm
|
||||
providerConfig.TSIGKey = config.TsigKey
|
||||
providerConfig.TSIGSecret = config.TsigSecret
|
||||
if config.DnsPropagationTimeout != 0 {
|
||||
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
||||
}
|
||||
if config.DnsTTL != 0 {
|
||||
providerConfig.TTL = int(config.DnsTTL)
|
||||
}
|
||||
|
||||
provider, err := rfc2136.NewDNSProviderConfig(providerConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return provider, nil
|
||||
}
|
||||
BIN
ui/public/imgs/providers/rfc.png
Normal file
BIN
ui/public/imgs/providers/rfc.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 288 B |
@ -70,6 +70,7 @@ import AccessConfigFieldsProviderProxmoxVE from "./forms/AccessConfigFieldsProvi
|
||||
import AccessConfigFieldsProviderQiniu from "./forms/AccessConfigFieldsProviderQiniu";
|
||||
import AccessConfigFieldsProviderRainYun from "./forms/AccessConfigFieldsProviderRainYun";
|
||||
import AccessConfigFieldsProviderRatPanel from "./forms/AccessConfigFieldsProviderRatPanel";
|
||||
import AccessConfigFieldsProviderRFC2136 from "./forms/AccessConfigFieldsProviderRFC2136";
|
||||
import AccessConfigFieldsProviderSafeLine from "./forms/AccessConfigFieldsProviderSafeLine";
|
||||
import AccessConfigFieldsProviderSectigo from "./forms/AccessConfigFieldsProviderSectigo";
|
||||
import AccessConfigFieldsProviderSlackBot from "./forms/AccessConfigFieldsProviderSlackBot";
|
||||
@ -311,6 +312,9 @@ const AccessForm = ({ className, style, disabled, initialValues, mode, usage, on
|
||||
case ACCESS_PROVIDERS.RATPANEL: {
|
||||
return <AccessConfigFieldsProviderRatPanel />;
|
||||
}
|
||||
case ACCESS_PROVIDERS.RFC2136: {
|
||||
return <AccessConfigFieldsProviderRFC2136 />;
|
||||
}
|
||||
case ACCESS_PROVIDERS.SAFELINE: {
|
||||
return <AccessConfigFieldsProviderSafeLine />;
|
||||
}
|
||||
|
||||
@ -36,8 +36,8 @@ const AccessConfigFormFieldsProviderACMEHttpReq = () => {
|
||||
>
|
||||
<Select
|
||||
options={[
|
||||
{ value: "", label: "(default)" },
|
||||
{ value: "RAW", label: "RAW" },
|
||||
{ label: "(default)", value: "" },
|
||||
{ label: "RAW", value: "RAW" },
|
||||
]}
|
||||
placeholder={t("access.form.acmehttpreq_mode.placeholder")}
|
||||
/>
|
||||
|
||||
@ -0,0 +1,103 @@
|
||||
import { getI18n, useTranslation } from "react-i18next";
|
||||
import { Form, Input, Select, InputNumber } from "antd";
|
||||
import { createSchemaFieldRule } from "antd-zod";
|
||||
import { z } from "zod";
|
||||
|
||||
import { validDomainName, validIPv4Address, validIPv6Address, validPortNumber } from "@/utils/validators";
|
||||
|
||||
import { useFormNestedFieldsContext } from "./_context";
|
||||
|
||||
const AccessConfigFormFieldsProviderRFC2136 = () => {
|
||||
const { i18n, t } = useTranslation();
|
||||
|
||||
const { parentNamePath } = useFormNestedFieldsContext();
|
||||
const formSchema = z.object({
|
||||
[parentNamePath]: getSchema({ i18n }),
|
||||
});
|
||||
const formRule = createSchemaFieldRule(formSchema);
|
||||
const initialValues = getInitialValues();
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex space-x-2">
|
||||
<div className="w-2/3">
|
||||
<Form.Item name={[parentNamePath, "host"]} initialValue={initialValues.host} label={t("access.form.rfc2136_host.label")} rules={[formRule]}>
|
||||
<Input placeholder={t("access.form.rfc2136_host.placeholder")} />
|
||||
</Form.Item>
|
||||
</div>
|
||||
|
||||
<div className="w-1/3">
|
||||
<Form.Item name={[parentNamePath, "port"]} initialValue={initialValues.port} label={t("access.form.rfc2136_port.label")} rules={[formRule]}>
|
||||
<InputNumber style={{ width: "100%" }} min={1} max={65535} placeholder={t("access.form.rfc2136_port.placeholder")} />
|
||||
</Form.Item>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Form.Item
|
||||
name={[parentNamePath, "tsigAlgorithm"]}
|
||||
initialValue={initialValues.tsigAlgorithm}
|
||||
label={t("access.form.rfc2136_tsig_algorithm.label")}
|
||||
rules={[formRule]}
|
||||
>
|
||||
<Select
|
||||
options={[
|
||||
{ label: "HMAC-SHA-1", value: "hmac-sha1." },
|
||||
{ label: "HMAC-SHA-224", value: "hmac-sha224." },
|
||||
{ label: "HMAC-SHA-256", value: "hmac-sha256." },
|
||||
{ label: "HMAC-SHA-384", value: "hmac-sha384." },
|
||||
{ label: "HMAC-SHA-512", value: "hmac-sha512." },
|
||||
]}
|
||||
placeholder={t("access.form.rfc2136_tsig_algorithm.placeholder")}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item name={[parentNamePath, "tsigKey"]} initialValue={initialValues.tsigKey} label={t("access.form.rfc2136_tsig_key.label")} rules={[formRule]}>
|
||||
<Input allowClear autoComplete="new-password" placeholder={t("access.form.rfc2136_tsig_key.placeholder")} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name={[parentNamePath, "tsigSecret"]}
|
||||
initialValue={initialValues.tsigSecret}
|
||||
label={t("access.form.rfc2136_tsig_secret.label")}
|
||||
rules={[formRule]}
|
||||
>
|
||||
<Input.Password allowClear autoComplete="new-password" placeholder={t("access.form.rfc2136_tsig_secret.placeholder")} />
|
||||
</Form.Item>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
|
||||
return {
|
||||
host: "127.0.0.1",
|
||||
port: 53,
|
||||
tsigAlgorithm: "hmac-sha1.",
|
||||
tsigKey: "",
|
||||
tsigSecret: "",
|
||||
};
|
||||
};
|
||||
|
||||
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
|
||||
const { t } = i18n;
|
||||
|
||||
return z.object({
|
||||
host: z.string().refine((v) => validDomainName(v) || validIPv4Address(v) || validIPv6Address(v), t("common.errmsg.host_invalid")),
|
||||
port: z.preprocess(
|
||||
(v) => Number(v),
|
||||
z
|
||||
.number()
|
||||
.int(t("access.form.rfc2136_port.placeholder"))
|
||||
.refine((v) => validPortNumber(v), t("common.errmsg.port_invalid"))
|
||||
),
|
||||
tsigAlgorithm: z.string().nonempty(t("access.form.rfc2136_tsig_algorithm.placeholder")),
|
||||
tsigKey: z.string().nullish(),
|
||||
tsigSecret: z.string().nullish(),
|
||||
});
|
||||
};
|
||||
|
||||
const _default = Object.assign(AccessConfigFormFieldsProviderRFC2136, {
|
||||
getInitialValues,
|
||||
getSchema,
|
||||
});
|
||||
|
||||
export default _default;
|
||||
@ -77,6 +77,7 @@ export const ACCESS_PROVIDERS = Object.freeze({
|
||||
QINIU: "qiniu",
|
||||
RAINYUN: "rainyun",
|
||||
RATPANEL: "ratpanel",
|
||||
RFC2136: "rfc2136",
|
||||
SAFELINE: "safeline",
|
||||
SECTIGO: "sectigo",
|
||||
SLACKBOT: "slackbot",
|
||||
@ -187,6 +188,7 @@ export const accessProvidersMap: Map<AccessProvider["type"] | string, AccessProv
|
||||
[ACCESS_PROVIDERS.WESTCN, "provider.westcn", "/imgs/providers/westcn.svg", [ACCESS_USAGES.DNS]],
|
||||
[ACCESS_PROVIDERS.POWERDNS, "provider.powerdns", "/imgs/providers/powerdns.svg", [ACCESS_USAGES.DNS]],
|
||||
[ACCESS_PROVIDERS.TECHNITIUMDNS, "provider.technitiumdns", "/imgs/providers/technitiumdns.png", [ACCESS_USAGES.DNS]],
|
||||
[ACCESS_PROVIDERS.RFC2136, "provider.rfc2136", "/imgs/providers/rfc.png", [ACCESS_USAGES.DNS]],
|
||||
[ACCESS_PROVIDERS.ACMEDNS, "provider.acmedns", "/imgs/providers/acmedns.png", [ACCESS_USAGES.DNS]],
|
||||
[ACCESS_PROVIDERS.ACMEHTTPREQ, "provider.acmehttpreq", "/imgs/providers/acmehttpreq.svg", [ACCESS_USAGES.DNS]],
|
||||
|
||||
@ -320,6 +322,7 @@ export const ACME_DNS01_PROVIDERS = Object.freeze({
|
||||
PORKBUN: `${ACCESS_PROVIDERS.PORKBUN}`,
|
||||
POWERDNS: `${ACCESS_PROVIDERS.POWERDNS}`,
|
||||
RAINYUN: `${ACCESS_PROVIDERS.RAINYUN}`,
|
||||
RFC2136: `${ACCESS_PROVIDERS.RFC2136}`,
|
||||
SPACESHIP: `${ACCESS_PROVIDERS.SPACESHIP}`,
|
||||
UCLOUD_UDNR: `${ACCESS_PROVIDERS.UCLOUD}-udnr`,
|
||||
TECHNITIUMDNS: `${ACCESS_PROVIDERS.TECHNITIUMDNS}`,
|
||||
@ -384,6 +387,7 @@ export const acmeDns01ProvidersMap: Map<ACMEDns01Provider["type"] | string, ACME
|
||||
[ACME_DNS01_PROVIDERS.WESTCN, "provider.westcn"],
|
||||
[ACME_DNS01_PROVIDERS.POWERDNS, "provider.powerdns"],
|
||||
[ACME_DNS01_PROVIDERS.TECHNITIUMDNS, "provider.technitiumdns"],
|
||||
[ACME_DNS01_PROVIDERS.RFC2136, "provider.rfc2136"],
|
||||
[ACME_DNS01_PROVIDERS.ACMEDNS, "provider.acmedns"],
|
||||
[ACME_DNS01_PROVIDERS.ACMEHTTPREQ, "provider.acmehttpreq"],
|
||||
] satisfies Array<[ACMEDns01ProviderType, string]>
|
||||
|
||||
@ -405,6 +405,16 @@
|
||||
"access.form.ratpanel_access_token.label": "RatPanel access token",
|
||||
"access.form.ratpanel_access_token.placeholder": "Please enter RatPanel access token",
|
||||
"access.form.ratpanel_access_token.tooltip": "For more information, see <a href=\"https://ratpanel.github.io/advanced/api.html\" target=\"_blank\">https://ratpanel.github.io/advanced/api.html</a>",
|
||||
"access.form.rfc2136_host.label": "DNS server host",
|
||||
"access.form.rfc2136_host.placeholder": "Please enter DNS server host",
|
||||
"access.form.rfc2136_port.label": "DNS server port",
|
||||
"access.form.rfc2136_port.placeholder": "Please enter DNS server port",
|
||||
"access.form.rfc2136_tsig_algorithm.label": "TSIG algorithm",
|
||||
"access.form.rfc2136_tsig_algorithm.placeholder": "Please select TSIG algorithm",
|
||||
"access.form.rfc2136_tsig_key.label": "TSIG authentication key (Optional)",
|
||||
"access.form.rfc2136_tsig_key.placeholder": "Please enter TSIG authentication key",
|
||||
"access.form.rfc2136_tsig_secret.label": "TSIG authentication secret (Optional)",
|
||||
"access.form.rfc2136_tsig_secret.placeholder": "Please enter TSIG authentication secret",
|
||||
"access.form.safeline_server_url.label": "SafeLine server URL",
|
||||
"access.form.safeline_server_url.placeholder": "Please enter SafeLine server URL",
|
||||
"access.form.safeline_api_token.label": "SafeLine API token",
|
||||
|
||||
@ -132,6 +132,7 @@
|
||||
"provider.ratpanel": "RatPanel",
|
||||
"provider.ratpanel.console": "RatPanel - Console itself",
|
||||
"provider.ratpanel.site": "RatPanel - Website",
|
||||
"provider.rfc2136": "RFC 2136: Dynamic DNS Updates",
|
||||
"provider.safeline": "SafeLine",
|
||||
"provider.sectigo": "Sectigo",
|
||||
"provider.slackbot": "Slack Bot",
|
||||
|
||||
@ -404,6 +404,16 @@
|
||||
"access.form.ratpanel_access_token.label": "耗子面板 AccessToken",
|
||||
"access.form.ratpanel_access_token.placeholder": "请输入耗子面板 AccessToken",
|
||||
"access.form.ratpanel_access_token.tooltip": "这是什么?请参阅 <a href=\"https://ratpanel.github.io/advanced/api.html\" target=\"_blank\">https://ratpanel.github.io/advanced/api.html</a>",
|
||||
"access.form.rfc2136_host.label": "DNS 服务器地址",
|
||||
"access.form.rfc2136_host.placeholder": "请输入 DNS 服务器地址",
|
||||
"access.form.rfc2136_port.label": "DNS 服务器端口",
|
||||
"access.form.rfc2136_port.placeholder": "请输入 DNS 服务器端口",
|
||||
"access.form.rfc2136_tsig_algorithm.label": "TSIG 算法",
|
||||
"access.form.rfc2136_tsig_algorithm.placeholder": "请选择 TSIG 算法",
|
||||
"access.form.rfc2136_tsig_key.label": "TSIG 认证密钥 Key(可选)",
|
||||
"access.form.rfc2136_tsig_key.placeholder": "请输入 TSIG 认证密钥 Key",
|
||||
"access.form.rfc2136_tsig_secret.label": "TSIG 认证密钥 Secret(可选)",
|
||||
"access.form.rfc2136_tsig_secret.placeholder": "请输入 TSIG 认证密钥 Secret",
|
||||
"access.form.safeline_server_url.label": "雷池服务地址",
|
||||
"access.form.safeline_server_url.placeholder": "请输入雷池服务地址",
|
||||
"access.form.safeline_api_token.label": "雷池 API Token",
|
||||
|
||||
@ -132,6 +132,7 @@
|
||||
"provider.ratpanel": "耗子面板",
|
||||
"provider.ratpanel.console": "耗子面板 - 面板自身",
|
||||
"provider.ratpanel.site": "耗子面板 - 网站",
|
||||
"provider.rfc2136": "RFC 2136: Dynamic DNS Updates",
|
||||
"provider.safeline": "雷池",
|
||||
"provider.sectigo": "Sectigo",
|
||||
"provider.slackbot": "Slack 机器人",
|
||||
|
||||
Loading…
Reference in New Issue
Block a user