diff --git a/internal/certapply/applicators/sp_technitiumdns.go b/internal/certapply/applicators/sp_technitiumdns.go new file mode 100644 index 00000000..c1e90816 --- /dev/null +++ b/internal/certapply/applicators/sp_technitiumdns.go @@ -0,0 +1,31 @@ +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/technitiumdns" + xmaps "github.com/certimate-go/certimate/pkg/utils/maps" +) + +func init() { + if err := ACMEDns01Registries.Register(domain.ACMEDns01ProviderTypeTechnitiumDNS, func(options *ProviderFactoryOptions) (challenge.Provider, error) { + credentials := domain.AccessConfigForTechnitiumDNS{} + if err := xmaps.Populate(options.ProviderAccessConfig, &credentials); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + provider, err := technitiumdns.NewChallengeProvider(&technitiumdns.ChallengeProviderConfig{ + ServerUrl: credentials.ServerUrl, + ApiToken: credentials.ApiToken, + AllowInsecureConnections: credentials.AllowInsecureConnections, + DnsPropagationTimeout: options.DnsPropagationTimeout, + DnsTTL: options.DnsTTL, + }) + return provider, err + }); err != nil { + panic(err) + } +} diff --git a/internal/domain/access.go b/internal/domain/access.go index 2578f9f0..f40834f8 100644 --- a/internal/domain/access.go +++ b/internal/domain/access.go @@ -379,6 +379,12 @@ type AccessConfigForSSLCom struct { AccessConfigForACMEExternalAccountBinding } +type AccessConfigForTechnitiumDNS struct { + ServerUrl string `json:"serverUrl"` + ApiToken string `json:"apiToken"` + AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"` +} + type AccessConfigForTelegramBot struct { BotToken string `json:"botToken"` ChatId int64 `json:"chatId,omitempty"` diff --git a/internal/domain/provider.go b/internal/domain/provider.go index 8b088cbc..0937d998 100644 --- a/internal/domain/provider.go +++ b/internal/domain/provider.go @@ -23,8 +23,9 @@ const ( AccessProviderTypeBaishan = AccessProviderType("baishan") AccessProviderTypeBaotaPanel = AccessProviderType("baotapanel") AccessProviderTypeBaotaWAF = AccessProviderType("baotawaf") - AccessProviderTypeBytePlus = AccessProviderType("byteplus") + AccessProviderTypeBookMyName = AccessProviderType("bookmyname") // BookMyName(预留) AccessProviderTypeBunny = AccessProviderType("bunny") + AccessProviderTypeBytePlus = AccessProviderType("byteplus") AccessProviderTypeCacheFly = AccessProviderType("cachefly") AccessProviderTypeCdnfly = AccessProviderType("cdnfly") AccessProviderTypeCloudflare = AccessProviderType("cloudflare") @@ -52,14 +53,17 @@ const ( AccessProviderTypeGlobalSignAtlas = AccessProviderType("globalsignatlas") AccessProviderTypeGoogleTrustServices = AccessProviderType("googletrustservices") AccessProviderTypeHetzner = AccessProviderType("hetzner") + AccessProviderTypeHostinger = AccessProviderType("hostinger") // Hostinger(预留) AccessProviderTypeHuaweiCloud = AccessProviderType("huaweicloud") AccessProviderTypeJDCloud = AccessProviderType("jdcloud") AccessProviderTypeKong = AccessProviderType("kong") + AccessProviderTypeKsyun = AccessProviderType("ksyun") // 金山云(预留) AccessProviderTypeKubernetes = AccessProviderType("k8s") AccessProviderTypeLarkBot = AccessProviderType("larkbot") AccessProviderTypeLetsEncrypt = AccessProviderType("letsencrypt") AccessProviderTypeLetsEncryptStaging = AccessProviderType("letsencryptstaging") AccessProviderTypeLeCDN = AccessProviderType("lecdn") + AccessProviderTypeLinode = AccessProviderType("linode") // Linode(预留) AccessProviderTypeLocal = AccessProviderType("local") AccessProviderTypeMattermost = AccessProviderType("mattermost") AccessProviderTypeNamecheap = AccessProviderType("namecheap") @@ -75,12 +79,14 @@ const ( AccessProviderTypeQingCloud = AccessProviderType("qingcloud") // 青云(预留) AccessProviderTypeRainYun = AccessProviderType("rainyun") AccessProviderTypeRatPanel = AccessProviderType("ratpanel") + AccessProviderTypeRFC2136 = AccessProviderType("rfc2136") // RFC2136(预留) AccessProviderTypeSafeLine = AccessProviderType("safeline") AccessProviderTypeSectigo = AccessProviderType("sectigo") AccessProviderTypeSlackBot = AccessProviderType("slackbot") AccessProviderTypeSpaceship = AccessProviderType("spaceship") AccessProviderTypeSSH = AccessProviderType("ssh") AccessProviderTypeSSLCOM = AccessProviderType("sslcom") + AccessProviderTypeTechnitiumDNS = AccessProviderType("technitiumdns") AccessProviderTypeTelegramBot = AccessProviderType("telegrambot") AccessProviderTypeTencentCloud = AccessProviderType("tencentcloud") AccessProviderTypeUCloud = AccessProviderType("ucloud") @@ -169,6 +175,7 @@ const ( ACMEDns01ProviderTypePowerDNS = ACMEDns01ProviderType(AccessProviderTypePowerDNS) ACMEDns01ProviderTypeRainYun = ACMEDns01ProviderType(AccessProviderTypeRainYun) ACMEDns01ProviderTypeSpaceship = ACMEDns01ProviderType(AccessProviderTypeSpaceship) + ACMEDns01ProviderTypeTechnitiumDNS = ACMEDns01ProviderType(AccessProviderTypeTechnitiumDNS) ACMEDns01ProviderTypeTencentCloud = ACMEDns01ProviderType(AccessProviderTypeTencentCloud) // 兼容旧值,等同于 [ACMEDns01ProviderTypeTencentCloudDNS] ACMEDns01ProviderTypeTencentCloudDNS = ACMEDns01ProviderType(AccessProviderTypeTencentCloud + "-dns") ACMEDns01ProviderTypeTencentCloudEO = ACMEDns01ProviderType(AccessProviderTypeTencentCloud + "-eo") diff --git a/pkg/core/ssl-applicator/acme-dns01/providers/powerdns/powerdns.go b/pkg/core/ssl-applicator/acme-dns01/providers/powerdns/powerdns.go index abb38dba..45dc432c 100644 --- a/pkg/core/ssl-applicator/acme-dns01/providers/powerdns/powerdns.go +++ b/pkg/core/ssl-applicator/acme-dns01/providers/powerdns/powerdns.go @@ -3,13 +3,13 @@ package powerdns import ( "crypto/tls" "errors" - "net/http" "net/url" "time" "github.com/go-acme/lego/v4/providers/dns/pdns" "github.com/certimate-go/certimate/pkg/core" + xhttp "github.com/certimate-go/certimate/pkg/utils/http" ) type ChallengeProviderConfig struct { @@ -30,12 +30,9 @@ func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, providerConfig.Host = serverUrl providerConfig.APIKey = config.ApiKey if config.AllowInsecureConnections { - providerConfig.HTTPClient.Transport = &http.Transport{ - Proxy: http.ProxyFromEnvironment, - TLSClientConfig: &tls.Config{ - InsecureSkipVerify: true, - }, - } + transport := xhttp.NewDefaultTransport() + transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} + providerConfig.HTTPClient.Transport = transport } if config.DnsPropagationTimeout != 0 { providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second diff --git a/pkg/core/ssl-applicator/acme-dns01/providers/technitiumdns/technitiumdns.go b/pkg/core/ssl-applicator/acme-dns01/providers/technitiumdns/technitiumdns.go new file mode 100644 index 00000000..f3390d48 --- /dev/null +++ b/pkg/core/ssl-applicator/acme-dns01/providers/technitiumdns/technitiumdns.go @@ -0,0 +1,48 @@ +package technitiumdns + +import ( + "crypto/tls" + "errors" + "time" + + "github.com/go-acme/lego/v4/providers/dns/technitium" + + "github.com/certimate-go/certimate/pkg/core" + xhttp "github.com/certimate-go/certimate/pkg/utils/http" +) + +type ChallengeProviderConfig struct { + ServerUrl string `json:"serverUrl"` + ApiToken string `json:"apiToken"` + AllowInsecureConnections bool `json:"allowInsecureConnections,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") + } + + providerConfig := technitium.NewDefaultConfig() + providerConfig.BaseURL = config.ServerUrl + providerConfig.APIToken = config.ApiToken + if config.AllowInsecureConnections { + transport := xhttp.NewDefaultTransport() + transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} + providerConfig.HTTPClient.Transport = transport + } + if config.DnsPropagationTimeout != 0 { + providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second + } + if config.DnsTTL != 0 { + providerConfig.TTL = int(config.DnsTTL) + } + + provider, err := technitium.NewDNSProviderConfig(providerConfig) + if err != nil { + return nil, err + } + + return provider, nil +} diff --git a/ui/public/imgs/providers/technitiumdns.png b/ui/public/imgs/providers/technitiumdns.png new file mode 100644 index 00000000..09d325f7 Binary files /dev/null and b/ui/public/imgs/providers/technitiumdns.png differ diff --git a/ui/src/components/access/AccessForm.tsx b/ui/src/components/access/AccessForm.tsx index c97f9bd3..383351d9 100644 --- a/ui/src/components/access/AccessForm.tsx +++ b/ui/src/components/access/AccessForm.tsx @@ -76,6 +76,7 @@ import AccessConfigFieldsProviderSlackBot from "./forms/AccessConfigFieldsProvid import AccessConfigFieldsProviderSpaceship from "./forms/AccessConfigFieldsProviderSpaceship"; import AccessConfigFieldsProviderSSH from "./forms/AccessConfigFieldsProviderSSH"; import AccessConfigFieldsProviderSSLCom from "./forms/AccessConfigFieldsProviderSSLCom"; +import AccessConfigFieldsProviderTechnitiumDNS from "./forms/AccessConfigFieldsProviderTechnitiumDNS"; import AccessConfigFieldsProviderTelegramBot from "./forms/AccessConfigFieldsProviderTelegramBot"; import AccessConfigFieldsProviderTencentCloud from "./forms/AccessConfigFieldsProviderTencentCloud"; import AccessConfigFieldsProviderUCloud from "./forms/AccessConfigFieldsProviderUCloud"; @@ -322,15 +323,18 @@ const AccessForm = ({ className, style, disabled, initialValues, mode, usage, on case ACCESS_PROVIDERS.SPACESHIP: { return ; } + case ACCESS_PROVIDERS.SSLCOM: { + return ; + } case ACCESS_PROVIDERS.SSH: { return ; } + case ACCESS_PROVIDERS.TECHNITIUMDNS: { + return ; + } case ACCESS_PROVIDERS.TELEGRAMBOT: { return ; } - case ACCESS_PROVIDERS.SSLCOM: { - return ; - } case ACCESS_PROVIDERS.TENCENTCLOUD: { return ; } diff --git a/ui/src/components/access/forms/AccessConfigFieldsProviderTechnitiumDNS.tsx b/ui/src/components/access/forms/AccessConfigFieldsProviderTechnitiumDNS.tsx new file mode 100644 index 00000000..f0105b2b --- /dev/null +++ b/ui/src/components/access/forms/AccessConfigFieldsProviderTechnitiumDNS.tsx @@ -0,0 +1,76 @@ +import { getI18n, useTranslation } from "react-i18next"; +import { Form, Input, Switch } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import { useFormNestedFieldsContext } from "./_context"; + +const AccessConfigFormFieldsProviderTechnitiumDNS = () => { + const { i18n, t } = useTranslation(); + + const { parentNamePath } = useFormNestedFieldsContext(); + const formSchema = z.object({ + [parentNamePath]: getSchema({ i18n }), + }); + const formRule = createSchemaFieldRule(formSchema); + const initialValues = getInitialValues(); + + return ( + <> + + + + + } + > + + + + + + + + ); +}; + +const getInitialValues = (): Nullish>> => { + return { + serverUrl: "http://:5380/", + apiToken: "", + }; +}; + +const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType }) => { + const { t } = i18n; + + return z.object({ + serverUrl: z.url(t("common.errmsg.url_invalid")), + apiToken: z.string().nonempty(t("access.form.technitiumdns_api_token.placeholder")), + allowInsecureConnections: z.boolean().nullish(), + }); +}; + +const _default = Object.assign(AccessConfigFormFieldsProviderTechnitiumDNS, { + getInitialValues, + getSchema, +}); + +export default _default; diff --git a/ui/src/domain/provider.ts b/ui/src/domain/provider.ts index 33bba32a..3d0526e5 100644 --- a/ui/src/domain/provider.ts +++ b/ui/src/domain/provider.ts @@ -83,6 +83,7 @@ export const ACCESS_PROVIDERS = Object.freeze({ SPACESHIP: "spaceship", SSH: "ssh", SSLCOM: "sslcom", + TECHNITIUMDNS: "technitiumdns", TELEGRAMBOT: "telegrambot", TENCENTCLOUD: "tencentcloud", UCLOUD: "ucloud", @@ -185,6 +186,7 @@ export const accessProvidersMap: Map diff --git a/ui/src/i18n/locales/en/nls.access.json b/ui/src/i18n/locales/en/nls.access.json index cdda2558..66b5faab 100644 --- a/ui/src/i18n/locales/en/nls.access.json +++ b/ui/src/i18n/locales/en/nls.access.json @@ -122,9 +122,6 @@ "access.form.baiducloud_secret_access_key.label": "Baidu Cloud SecretAccessKey", "access.form.baiducloud_secret_access_key.placeholder": "Please enter Baidu Cloud SecretAccessKey", "access.form.baiducloud_secret_access_key.tooltip": "For more information, see https://intl.cloud.baidu.com/doc/Reference/s/jjwvz2e3p-en", - "access.form.bunny_api_key.label": "Bunny API key", - "access.form.bunny_api_key.placeholder": "Please enter Bunny API key", - "access.form.bunny_api_key.tooltip": "For more information, see https://docs.bunny.net/reference/bunnynet-api-overview", "access.form.baishan_api_token.label": "Baishan Cloud API token", "access.form.baishan_api_token.placeholder": "Please enter Baishan Cloud API token", "access.form.baotapanel_server_url.label": "aaPanel server URL", @@ -139,6 +136,9 @@ "access.form.baotawaf_api_key.label": "aaWAF API key", "access.form.baotawaf_api_key.placeholder": "Please enter aaWAF API key", "access.form.baotawaf_api_key.tooltip": "For more information, see https://github.com/aaPanel/aaWAF/blob/main/API.md", + "access.form.bunny_api_key.label": "Bunny API key", + "access.form.bunny_api_key.placeholder": "Please enter Bunny API key", + "access.form.bunny_api_key.tooltip": "For more information, see https://docs.bunny.net/reference/bunnynet-api-overview", "access.form.byteplus_access_key.label": "BytePlus AccessKey", "access.form.byteplus_access_key.placeholder": "Please enter BytePlus AccessKey", "access.form.byteplus_access_key.tooltip": "For more information, see https://docs.byteplus.com/en/docs/byteplus-platform/docs-managing-keys", @@ -454,6 +454,11 @@ "access.form.telegrambot_token.label": "Telegram bot token", "access.form.telegrambot_token.placeholder": "Please enter Telegram bot token", "access.form.telegrambot_token.tooltip": "How to get it? Please refer to https://gist.github.com/nafiesl/4ad622f344cd1dc3bb1ecbe468ff9f8a", + "access.form.technitiumdns_server_url.label": "Technitium DNS server URL", + "access.form.technitiumdns_server_url.placeholder": "Please enter Technitium DNS server URL", + "access.form.technitiumdns_api_token.label": "Technitium DNS API token", + "access.form.technitiumdns_api_token.placeholder": "Please enter Technitium DNS API token", + "access.form.technitiumdns_api_token.tooltip": "For more information, see https://github.com/TechnitiumSoftware/DnsServer/blob/master/APIDOCS.md", "access.form.telegrambot_chat_id.label": "Telegram chat ID (Optional)", "access.form.telegrambot_chat_id.placeholder": "Please enter the default Telegram chat ID", "access.form.telegrambot_chat_id.help": "Notes: It can be overrided in the workflows.", diff --git a/ui/src/i18n/locales/en/nls.provider.json b/ui/src/i18n/locales/en/nls.provider.json index 39426fd4..0723fef4 100644 --- a/ui/src/i18n/locales/en/nls.provider.json +++ b/ui/src/i18n/locales/en/nls.provider.json @@ -138,6 +138,7 @@ "provider.spaceship": "Spaceship", "provider.ssh": "Remote host (SSH)", "provider.sslcom": "SSL.com", + "provider.technitiumdns": "Technitium DNS", "provider.telegrambot": "Telegram Bot", "provider.tencentcloud": "Tencent Cloud", "provider.tencentcloud.cdn": "Tencent Cloud - CDN (Content Delivery Network)", diff --git a/ui/src/i18n/locales/zh/nls.access.json b/ui/src/i18n/locales/zh/nls.access.json index 48b35010..dc8b24fd 100644 --- a/ui/src/i18n/locales/zh/nls.access.json +++ b/ui/src/i18n/locales/zh/nls.access.json @@ -450,6 +450,11 @@ "access.form.ssh_jump_servers.item.label": "跳板机", "access.form.ssh_jump_servers.add": "添加跳板机", "access.form.sslcom_eab.guide": "点击下方链接了解如何获取 SSL.com EAB:
https://www.ssl.com/how-to/generate-acme-credentials-for-reseller-customers/", + "access.form.technitiumdns_server_url.label": "Technitium DNS 服务地址", + "access.form.technitiumdns_server_url.placeholder": "请输入 Technitium DNS 服务地址", + "access.form.technitiumdns_api_token.label": "Technitium DNS API Token", + "access.form.technitiumdns_api_token.placeholder": "请输入 Technitium DNS API Token", + "access.form.technitiumdns_api_token.tooltip": "这是什么?请参阅 https://github.com/TechnitiumSoftware/DnsServer/blob/master/APIDOCS.md", "access.form.telegrambot_token.label": "Telegram 机器人 API Token", "access.form.telegrambot_token.placeholder": "请输入 Telegram 机器人 API Token", "access.form.telegrambot_token.tooltip": "如何获取此参数?请参阅 https://gist.github.com/nafiesl/4ad622f344cd1dc3bb1ecbe468ff9f8a", diff --git a/ui/src/i18n/locales/zh/nls.provider.json b/ui/src/i18n/locales/zh/nls.provider.json index cb7086c6..8ec4657e 100644 --- a/ui/src/i18n/locales/zh/nls.provider.json +++ b/ui/src/i18n/locales/zh/nls.provider.json @@ -138,6 +138,7 @@ "provider.spaceship": "Spaceship", "provider.ssh": "远程主机(SSH)", "provider.sslcom": "SSL.com", + "provider.technitiumdns": "Technitium DNS", "provider.telegrambot": "Telegram 机器人", "provider.tencentcloud": "腾讯云", "provider.tencentcloud.cdn": "腾讯云 - 内容分发网络 CDN",