feat: certificate key algorithm

This commit is contained in:
Fu Diwei
2024-10-16 20:20:27 +08:00
parent 71f43c5bd4
commit 1ce2a52d70
15 changed files with 595 additions and 409 deletions
+43 -22
View File
@@ -60,11 +60,12 @@ type Certificate struct {
}
type ApplyOption struct {
Email string `json:"email"`
Domain string `json:"domain"`
Access string `json:"access"`
Nameservers string `json:"nameservers"`
Timeout int64 `json:"timeout"`
Email string `json:"email"`
Domain string `json:"domain"`
Access string `json:"access"`
KeyAlgorithm string `json:"keyAlgorithm"`
Nameservers string `json:"nameservers"`
Timeout int64 `json:"timeout"`
}
type ApplyUser struct {
@@ -91,11 +92,10 @@ type Applicant interface {
func Get(record *models.Record) (Applicant, error) {
if record.GetString("applyConfig") == "" {
return nil, errors.New("apply config is empty")
return nil, errors.New("applyConfig is empty")
}
applyConfig := &domain.ApplyConfig{}
record.UnmarshalJSONField("applyConfig", applyConfig)
access, err := app.GetApp().Dao().FindRecordById("access", applyConfig.Access)
@@ -103,17 +103,23 @@ func Get(record *models.Record) (Applicant, error) {
return nil, fmt.Errorf("access record not found: %w", err)
}
email := applyConfig.Email
if email == "" {
email = defaultEmail
if applyConfig.Email == "" {
applyConfig.Email = defaultEmail
}
if applyConfig.Timeout == 0 {
applyConfig.Timeout = defaultTimeout
}
option := &ApplyOption{
Email: email,
Domain: record.GetString("domain"),
Access: access.GetString("config"),
Nameservers: applyConfig.Nameservers,
Timeout: applyConfig.Timeout,
Email: applyConfig.Email,
Domain: record.GetString("domain"),
Access: access.GetString("config"),
KeyAlgorithm: applyConfig.KeyAlgorithm,
Nameservers: applyConfig.Nameservers,
Timeout: applyConfig.Timeout,
}
switch access.GetString("configType") {
case configTypeAliyun:
return NewAliyun(option), nil
@@ -171,7 +177,7 @@ func apply(option *ApplyOption, provider challenge.Provider) (*Certificate, erro
// This CA URL is configured for a local dev instance of Boulder running in Docker in a VM.
config.CADirURL = sslProviderUrls[sslProvider.Provider]
config.Certificate.KeyType = certcrypto.RSA2048
config.Certificate.KeyType = parseKeyAlgorithm(option.KeyAlgorithm)
// A client facilitates communication with the CA server.
client, err := lego.NewClient(config)
@@ -180,7 +186,7 @@ func apply(option *ApplyOption, provider challenge.Provider) (*Certificate, erro
}
challengeOptions := make([]dns01.ChallengeOption, 0)
nameservers := ParseNameservers(option.Nameservers)
nameservers := parseNameservers(option.Nameservers)
if len(nameservers) > 0 {
challengeOptions = append(challengeOptions, dns01.AddRecursiveNameservers(nameservers))
}
@@ -195,7 +201,6 @@ func apply(option *ApplyOption, provider challenge.Provider) (*Certificate, erro
myUser.Registration = reg
domains := strings.Split(option.Domain, ";")
request := certificate.ObtainRequest{
Domains: domains,
Bundle: true,
@@ -231,7 +236,6 @@ func getReg(client *lego.Client, sslProvider *SSLProviderConfig) (*registration.
default:
err = errors.New("unknown ssl provider")
}
if err != nil {
@@ -241,15 +245,13 @@ func getReg(client *lego.Client, sslProvider *SSLProviderConfig) (*registration.
return reg, nil
}
func ParseNameservers(ns string) []string {
func parseNameservers(ns string) []string {
nameservers := make([]string, 0)
lines := strings.Split(ns, ";")
for _, line := range lines {
line = strings.TrimSpace(line)
if line == "" {
continue
}
@@ -259,3 +261,22 @@ func ParseNameservers(ns string) []string {
return nameservers
}
func parseKeyAlgorithm(algo string) certcrypto.KeyType {
switch algo {
case "RSA2048":
return certcrypto.RSA2048
case "RSA3072":
return certcrypto.RSA3072
case "RSA4096":
return certcrypto.RSA4096
case "RSA8192":
return certcrypto.RSA8192
case "EC256":
return certcrypto.EC256
case "EC384":
return certcrypto.EC384
default:
return certcrypto.RSA2048
}
}
+5 -4
View File
@@ -1,10 +1,11 @@
package domain
type ApplyConfig struct {
Email string `json:"email"`
Access string `json:"access"`
Timeout int64 `json:"timeout"`
Nameservers string `json:"nameservers"`
Email string `json:"email"`
Access string `json:"access"`
KeyAlgorithm string `json:"keyAlgorithm"`
Nameservers string `json:"nameservers"`
Timeout int64 `json:"timeout"`
}
type DeployConfig struct {
+329
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
-324
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -5,8 +5,8 @@
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Certimate - Your Trusted SSL Automation Partner</title>
<script type="module" crossorigin src="/assets/index-DIhd7QG6.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-CV_7sKTK.css">
<script type="module" crossorigin src="/assets/index-C20g8xcX.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-YqBWA4KK.css">
</head>
<body class="bg-background">
<div id="root"></div>␍
+72 -5
View File
@@ -11,6 +11,7 @@
"@hookform/resolvers": "^3.9.0",
"@radix-ui/react-accordion": "^1.2.0",
"@radix-ui/react-alert-dialog": "^1.1.1",
"@radix-ui/react-collapsible": "^1.1.1",
"@radix-ui/react-dialog": "^1.1.1",
"@radix-ui/react-dropdown-menu": "^2.1.1",
"@radix-ui/react-label": "^2.1.0",
@@ -1184,6 +1185,35 @@
}
}
},
"node_modules/@radix-ui/react-accordion/node_modules/@radix-ui/react-collapsible": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/@radix-ui/react-collapsible/-/react-collapsible-1.1.0.tgz",
"integrity": "sha512-zQY7Epa8sTL0mq4ajSJpjgn2YmCgyrG7RsQgLp3C0LQVkG7+Tf6Pv1CeNWZLyqMjhdPkBa5Lx7wYBeSu7uCSTA==",
"dependencies": {
"@radix-ui/primitive": "1.1.0",
"@radix-ui/react-compose-refs": "1.1.0",
"@radix-ui/react-context": "1.1.0",
"@radix-ui/react-id": "1.1.0",
"@radix-ui/react-presence": "1.1.0",
"@radix-ui/react-primitive": "2.0.0",
"@radix-ui/react-use-controllable-state": "1.1.0",
"@radix-ui/react-use-layout-effect": "1.1.0"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-alert-dialog": {
"version": "1.1.1",
"resolved": "https://registry.npmmirror.com/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.1.1.tgz",
@@ -1234,15 +1264,15 @@
}
},
"node_modules/@radix-ui/react-collapsible": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/@radix-ui/react-collapsible/-/react-collapsible-1.1.0.tgz",
"integrity": "sha512-zQY7Epa8sTL0mq4ajSJpjgn2YmCgyrG7RsQgLp3C0LQVkG7+Tf6Pv1CeNWZLyqMjhdPkBa5Lx7wYBeSu7uCSTA==",
"version": "1.1.1",
"resolved": "https://registry.npmmirror.com/@radix-ui/react-collapsible/-/react-collapsible-1.1.1.tgz",
"integrity": "sha512-1///SnrfQHJEofLokyczERxQbWfCGQlQ2XsCZMucVs6it+lq9iw4vXy+uDn1edlb58cOZOWSldnfPAYcT4O/Yg==",
"dependencies": {
"@radix-ui/primitive": "1.1.0",
"@radix-ui/react-compose-refs": "1.1.0",
"@radix-ui/react-context": "1.1.0",
"@radix-ui/react-context": "1.1.1",
"@radix-ui/react-id": "1.1.0",
"@radix-ui/react-presence": "1.1.0",
"@radix-ui/react-presence": "1.1.1",
"@radix-ui/react-primitive": "2.0.0",
"@radix-ui/react-use-controllable-state": "1.1.0",
"@radix-ui/react-use-layout-effect": "1.1.0"
@@ -1262,6 +1292,43 @@
}
}
},
"node_modules/@radix-ui/react-collapsible/node_modules/@radix-ui/react-context": {
"version": "1.1.1",
"resolved": "https://registry.npmmirror.com/@radix-ui/react-context/-/react-context-1.1.1.tgz",
"integrity": "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==",
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-collapsible/node_modules/@radix-ui/react-presence": {
"version": "1.1.1",
"resolved": "https://registry.npmmirror.com/@radix-ui/react-presence/-/react-presence-1.1.1.tgz",
"integrity": "sha512-IeFXVi4YS1K0wVZzXNrbaaUvIJ3qdY+/Ih4eHFhWA9SwGR9UDX7Ck8abvL57C4cv3wwMvUE0OG69Qc3NCcTe/A==",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.0",
"@radix-ui/react-use-layout-effect": "1.1.0"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-collection": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/@radix-ui/react-collection/-/react-collection-1.1.0.tgz",
+1
View File
@@ -13,6 +13,7 @@
"@hookform/resolvers": "^3.9.0",
"@radix-ui/react-accordion": "^1.2.0",
"@radix-ui/react-alert-dialog": "^1.1.1",
"@radix-ui/react-collapsible": "^1.1.1",
"@radix-ui/react-dialog": "^1.1.1",
"@radix-ui/react-dropdown-menu": "^2.1.1",
"@radix-ui/react-label": "^2.1.0",
+22
View File
@@ -0,0 +1,22 @@
import * as React from "react";
import * as CollapsiblePrimitive from "@radix-ui/react-collapsible";
import { cn } from "@/lib/utils";
const Collapsible = CollapsiblePrimitive.Root;
const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger;
const CollapsibleContent = React.forwardRef<
React.ElementRef<typeof CollapsiblePrimitive.CollapsibleContent>,
React.ComponentPropsWithoutRef<typeof CollapsiblePrimitive.CollapsibleContent>
>(({ className, ...props }, ref) => (
<CollapsiblePrimitive.CollapsibleContent
ref={ref}
className={cn("overflow-y-hidden transition-all data-[state=closed]:animate-collapsible-up data-[state=open]:animate-collapsible-down", className)}
{...props}
/>
));
CollapsibleContent.displayName = CollapsiblePrimitive.CollapsibleContent.displayName;
export { Collapsible, CollapsibleTrigger, CollapsibleContent };
+2 -1
View File
@@ -50,8 +50,9 @@ export type DeployConfig = {
export type ApplyConfig = {
access: string;
email: string;
timeout?: number;
keyAlgorithm?: string;
nameservers?: string;
timeout?: number;
};
export type Statistic = {
+6 -4
View File
@@ -30,15 +30,17 @@
"domain.application.form.domain.changed.message": "Domain updated successfully",
"domain.application.form.email.label": "Email",
"domain.application.form.email.tips": "(A email is required to apply for a certificate)",
"domain.application.form.email.placeholder": "Please select email",
"domain.application.form.email.add": "Add Email",
"domain.application.form.email.list": "Email List",
"domain.application.form.email.errmsg.empty": "Please select email",
"domain.application.form.access.label": "DNS Provider Authorization Configuration",
"domain.application.form.access.placeholder": "Please select DNS provider authorization configuration",
"domain.application.form.access.errmsg.empty": "Please select DNS provider authorization configuration",
"domain.application.form.access.list": "Provider Authorization Configurations",
"domain.application.form.timeout.label": "Timeout",
"domain.application.form.timeoue.placeholder": "Timeout (seconds)",
"domain.application.form.advanced_settings.label": "Advanced Settings",
"domain.application.form.key_algorithm.label": "Certificate Key Algorithm",
"domain.application.form.key_algorithm.placeholder": "Please select certificate key algorithm",
"domain.application.form.timeout.label": "DNS Propagation Timeout (seconds)",
"domain.application.form.timeoue.placeholder": "Please enter maximum waiting time for DNS propagation",
"domain.application.unsaved.message": "Please save applyment configuration first",
"domain.deployment.tab": "Deploy Settings",
+6 -4
View File
@@ -30,15 +30,17 @@
"domain.application.form.domain.changed.message": "域名编辑成功",
"domain.application.form.email.label": "邮箱",
"domain.application.form.email.tips": "(申请证书需要提供邮箱)",
"domain.application.form.email.placeholder": "请选择邮箱",
"domain.application.form.email.add": "添加邮箱",
"domain.application.form.email.list": "邮箱列表",
"domain.application.form.email.errmsg.empty": "请选择邮箱",
"domain.application.form.access.label": "DNS 服务商授权配置",
"domain.application.form.access.placeholder": "请选择 DNS 服务商授权配置",
"domain.application.form.access.errmsg.empty": "请选择 DNS 服务商授权配置",
"domain.application.form.access.list": "已有的 DNS 服务商授权配置",
"domain.application.form.timeout.label": "超时时间",
"domain.application.form.timeoue.placeholder": "超时时间(单位:秒)",
"domain.application.form.advanced_settings.label": "高级设置",
"domain.application.form.key_algorithm.label": "数字证书算法",
"domain.application.form.key_algorithm.placeholder": "请选择数字证书算法",
"domain.application.form.timeout.label": "DNS 传播检查超时时间(单位:秒)",
"domain.application.form.timeoue.placeholder": "请输入 DNS 传播检查超时时间",
"domain.application.unsaved.message": "请先保存申请配置",
"domain.deployment.tab": "部署配置",
+96 -42
View File
@@ -4,11 +4,12 @@ import { useForm } from "react-hook-form";
import { useTranslation } from "react-i18next";
import z from "zod";
import { zodResolver } from "@hookform/resolvers/zod";
import { Plus } from "lucide-react";
import { ChevronsUpDown, Plus } from "lucide-react";
import { ClientResponseError } from "pocketbase";
import { Button } from "@/components/ui/button";
import { Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator } from "@/components/ui/breadcrumb";
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue } from "@/components/ui/select";
@@ -58,8 +59,9 @@ const Edit = () => {
}),
email: z.string().email("common.errmsg.email_invalid").optional(),
access: z.string().regex(/^[a-zA-Z0-9]+$/, {
message: "domain.application.form.access.errmsg.empty",
message: "domain.application.form.access.placeholder",
}),
keyAlgorithm: z.string().optional(),
nameservers: z.string().optional(),
timeout: z.number().optional(),
});
@@ -71,6 +73,7 @@ const Edit = () => {
domain: "",
email: "",
access: "",
keyAlgorithm: "RSA2048",
nameservers: "",
timeout: 60,
},
@@ -83,6 +86,7 @@ const Edit = () => {
domain: domain.domain,
email: domain.applyConfig?.email,
access: domain.applyConfig?.access,
keyAlgorithm: domain.applyConfig?.keyAlgorithm,
nameservers: domain.applyConfig?.nameservers,
timeout: domain.applyConfig?.timeout,
});
@@ -101,6 +105,7 @@ const Edit = () => {
applyConfig: {
email: data.email ?? "",
access: data.access,
keyAlgorithm: data.keyAlgorithm,
nameservers: data.nameservers,
timeout: data.timeout,
},
@@ -235,6 +240,7 @@ const Edit = () => {
</FormItem>
)}
/>
{/* 邮箱 */}
<FormField
control={form.control}
@@ -261,7 +267,7 @@ const Edit = () => {
}}
>
<SelectTrigger>
<SelectValue placeholder={t("domain.application.form.email.errmsg.empty")} />
<SelectValue placeholder={t("domain.application.form.email.placeholder")} />
</SelectTrigger>
<SelectContent>
<SelectGroup>
@@ -280,7 +286,8 @@ const Edit = () => {
</FormItem>
)}
/>
{/* 授权 */}
{/* DNS 服务商授权 */}
<FormField
control={form.control}
name="access"
@@ -332,48 +339,95 @@ const Edit = () => {
)}
/>
{/* 超时时间 */}
<FormField
control={form.control}
name="timeout"
render={({ field }) => (
<FormItem>
<FormLabel>{t("domain.application.form.timeout.label")}</FormLabel>
<FormControl>
<Input
type="number"
placeholder={t("ddomain.application.form.timeout.placeholder")}
{...field}
value={field.value}
onChange={(e) => {
form.setValue("timeout", parseInt(e.target.value));
}}
<div>
<hr />
<Collapsible>
<CollapsibleTrigger className="w-full my-4">
<div className="flex items-center justify-between space-x-4">
<span className="flex-1 text-sm text-gray-600 text-left">{t("domain.application.form.advanced_settings.label")}</span>
<ChevronsUpDown className="h-4 w-4" />
</div>
</CollapsibleTrigger>
<CollapsibleContent>
<div className="flex flex-col space-y-8">
{/* 证书算法 */}
<FormField
control={form.control}
name="keyAlgorithm"
render={({ field }) => (
<FormItem>
<FormLabel>{t("domain.application.form.key_algorithm.label")}</FormLabel>
<Select
{...field}
value={field.value}
onValueChange={(value) => {
form.setValue("keyAlgorithm", value);
}}
>
<SelectTrigger>
<SelectValue placeholder={t("domain.application.form.key_algorithm.placeholder")} />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectItem value="RSA2048">RSA2048</SelectItem>
<SelectItem value="RSA3072">RSA3072</SelectItem>
<SelectItem value="RSA4096">RSA4096</SelectItem>
<SelectItem value="RSA8192">RSA8192</SelectItem>
<SelectItem value="EC256">EC256</SelectItem>
<SelectItem value="EC384">EC384</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
</FormItem>
)}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
{/* DNS */}
<FormField
control={form.control}
name="nameservers"
render={({ field }) => (
<FormItem>
<StringList
value={field.value ?? ""}
onValueChange={(val: string) => {
form.setValue("nameservers", val);
}}
valueType="dns"
></StringList>
{/* nameservers */}
<FormField
control={form.control}
name="nameservers"
render={({ field }) => (
<FormItem>
<StringList
value={field.value ?? ""}
onValueChange={(val: string) => {
form.setValue("nameservers", val);
}}
valueType="dns"
></StringList>
<FormMessage />
</FormItem>
)}
/>
<FormMessage />
</FormItem>
)}
/>
{/* DNS 超时时间 */}
<FormField
control={form.control}
name="timeout"
render={({ field }) => (
<FormItem>
<FormLabel>{t("domain.application.form.timeout.label")}</FormLabel>
<FormControl>
<Input
type="number"
placeholder={t("domain.application.form.timeout.placeholder")}
{...field}
value={field.value}
onChange={(e) => {
form.setValue("timeout", parseInt(e.target.value));
}}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
</CollapsibleContent>
</Collapsible>
</div>
<div className="flex justify-end">
<Button type="submit">{domain?.id ? t("common.save") : t("common.next")}</Button>
+10
View File
@@ -70,10 +70,20 @@ module.exports = {
height: "0",
},
},
"collapsible-down": {
from: { height: 0 },
to: { height: "var(--radix-collapsible-content-height)" },
},
"collapsible-up": {
from: { height: "var(--radix-collapsible-content-height)" },
to: { height: 0 },
},
},
animation: {
"accordion-down": "accordion-down 0.2s ease-out",
"accordion-up": "accordion-up 0.2s ease-out",
"collapsible-down": "collapsible-down 0.2s ease-out",
"collapsible-up": "collapsible-up 0.2s ease-out",
},
},
},