2
0
mirror of https://github.com/acepanel/panel.git synced 2026-02-04 07:57:21 +08:00

refactor: 移除 lego

This commit is contained in:
耗子
2024-03-23 00:50:59 +08:00
parent 7cb5a3db4b
commit 5390ef3140
14 changed files with 615 additions and 548 deletions

View File

@@ -650,7 +650,7 @@ func (r *CertController) Renew(ctx http.Context) http.Response {
// @Produce json
// @Security BearerToken
// @Param data body requests.Obtain true "request"
// @Success 200 {object} SuccessResponse{data=map[string]acme.Resolve}
// @Success 200 {object} SuccessResponse{data=[]acme.DNSRecord}
// @Router /panel/cert/manualDNS [post]
func (r *CertController) ManualDNS(ctx http.Context) http.Response {
var obtainRequest requests.Obtain

View File

@@ -26,7 +26,6 @@ func (r *DNSStore) Rules(ctx http.Context) map[string]string {
"data.token": "required_if:type,dnspod",
"data.access_key": "required_if:type,aliyun",
"data.secret_key": "required_if:type,aliyun",
"data.email": "required_if:type,cloudflare",
"data.api_key": "required_if:type,cloudflare",
}
}

View File

@@ -535,9 +535,9 @@ const docTemplate = `{
"type": "object",
"properties": {
"data": {
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/acme.Resolve"
"type": "array",
"items": {
"$ref": "#/definitions/acme.DNSRecord"
}
}
}
@@ -3696,9 +3696,6 @@ const docTemplate = `{
"api_key": {
"type": "string"
},
"email": {
"type": "string"
},
"id": {
"type": "string"
},
@@ -3710,12 +3707,9 @@ const docTemplate = `{
}
}
},
"acme.Resolve": {
"acme.DNSRecord": {
"type": "object",
"properties": {
"err": {
"type": "string"
},
"key": {
"type": "string"
},

View File

@@ -528,9 +528,9 @@
"type": "object",
"properties": {
"data": {
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/acme.Resolve"
"type": "array",
"items": {
"$ref": "#/definitions/acme.DNSRecord"
}
}
}
@@ -3689,9 +3689,6 @@
"api_key": {
"type": "string"
},
"email": {
"type": "string"
},
"id": {
"type": "string"
},
@@ -3703,12 +3700,9 @@
}
}
},
"acme.Resolve": {
"acme.DNSRecord": {
"type": "object",
"properties": {
"err": {
"type": "string"
},
"key": {
"type": "string"
},

View File

@@ -6,8 +6,6 @@ definitions:
type: string
api_key:
type: string
email:
type: string
id:
type: string
secret_key:
@@ -15,10 +13,8 @@ definitions:
token:
type: string
type: object
acme.Resolve:
acme.DNSRecord:
properties:
err:
type: string
key:
type: string
value:
@@ -955,9 +951,9 @@ paths:
- $ref: '#/definitions/controllers.SuccessResponse'
- properties:
data:
additionalProperties:
$ref: '#/definitions/acme.Resolve'
type: object
items:
$ref: '#/definitions/acme.DNSRecord'
type: array
type: object
security:
- BearerToken: []

22
go.mod
View File

@@ -6,7 +6,6 @@ require (
github.com/docker/docker v25.0.5+incompatible
github.com/docker/go-connections v0.5.0
github.com/gertd/go-pluralize v0.2.1
github.com/go-acme/lego/v4 v4.16.1
github.com/gookit/color v1.5.4
github.com/gookit/validate v1.5.2
github.com/goravel/framework v1.13.1-0.20240215091018-1a9f352e523c
@@ -14,6 +13,11 @@ require (
github.com/gorilla/websocket v1.5.1
github.com/iancoleman/strcase v0.3.0
github.com/imroc/req/v3 v3.43.1
github.com/libdns/alidns v1.0.3
github.com/libdns/cloudflare v0.1.1
github.com/libdns/dnspod v0.0.3
github.com/libdns/libdns v0.2.2
github.com/mholt/acmez v1.2.0
github.com/mholt/archiver/v3 v3.5.1
github.com/mojocn/base64Captcha v1.3.6
github.com/shirou/gopsutil v3.21.11+incompatible
@@ -21,6 +25,7 @@ require (
github.com/stretchr/testify v1.9.0
github.com/swaggo/http-swagger/v2 v2.0.2
github.com/swaggo/swag v1.16.3
go.uber.org/zap v1.24.0
golang.org/x/crypto v0.21.0
)
@@ -41,7 +46,6 @@ require (
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/RichardKnop/logging v0.0.0-20190827224416-1a693bdd4fae // indirect
github.com/RichardKnop/machinery/v2 v2.0.12-0.20231012204029-bdb94a90ca41 // indirect
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1755 // indirect
github.com/andybalholm/brotli v1.1.0 // indirect
github.com/aws/aws-sdk-go v1.49.6 // indirect
github.com/bytedance/sonic v1.11.0 // indirect
@@ -50,7 +54,6 @@ require (
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
github.com/chenzhuoyu/iasm v0.9.1 // indirect
github.com/cloudflare/circl v1.3.7 // indirect
github.com/cloudflare/cloudflare-go v0.86.0 // indirect
github.com/containerd/log v0.1.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
@@ -59,7 +62,6 @@ require (
github.com/docker/go-units v0.5.0 // indirect
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/fatih/color v1.15.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
@@ -67,7 +69,6 @@ require (
github.com/gin-gonic/gin v1.9.1 // indirect
github.com/glebarez/go-sqlite v1.22.0 // indirect
github.com/glebarez/sqlite v1.10.0 // indirect
github.com/go-jose/go-jose/v4 v4.0.1 // indirect
github.com/go-logr/logr v1.4.1 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
@@ -94,7 +95,6 @@ require (
github.com/golang/protobuf v1.5.3 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/gomodule/redigo v2.0.0+incompatible // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7 // indirect
github.com/google/s2a-go v0.1.7 // indirect
github.com/google/uuid v1.6.0 // indirect
@@ -106,13 +106,12 @@ require (
github.com/goravel/file-rotatelogs/v2 v2.4.2 // indirect
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-retryablehttp v0.7.5 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
github.com/jackc/pgx/v5 v5.4.3 // indirect
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 // indirect
github.com/jackc/pgx/v5 v5.5.5 // indirect
github.com/jackc/puddle/v2 v2.2.1 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
@@ -128,7 +127,6 @@ require (
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/microsoft/go-mssqldb v1.6.0 // indirect
github.com/miekg/dns v1.1.58 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
@@ -209,7 +207,7 @@ require (
google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac // indirect
google.golang.org/grpc v1.61.1 // indirect
google.golang.org/protobuf v1.32.0 // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect

61
go.sum
View File

@@ -47,7 +47,6 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.4.0/go.mod h1:ON4tFdPTwRcgWEaVDrN3584Ef+b7GgSJaXxe5fW9t4M=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.1/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q=
@@ -71,7 +70,6 @@ github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
github.com/Azure/go-autorest/autorest v0.11.29 h1:I4+HL/JDvErx2LjyzaVxllw2lRDB5/BT2Bm4g20iqYw=
github.com/Azure/go-autorest/autorest/adal v0.9.22 h1:/GblQdIudfEM3AWWZ0mrYJQSd7JS4S/Mbzh6F0ov0Xc=
github.com/Azure/go-autorest/autorest/adal v0.9.22/go.mod h1:XuAbAEUv2Tta//+voMI038TrJBqjKam0me7qR+L8Cmk=
github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw=
@@ -96,8 +94,6 @@ github.com/RichardKnop/logging v0.0.0-20190827224416-1a693bdd4fae h1:DcFpTQBYQ9C
github.com/RichardKnop/logging v0.0.0-20190827224416-1a693bdd4fae/go.mod h1:rJJ84PyA/Wlmw1hO+xTzV2wsSUon6J5ktg0g8BF2PuU=
github.com/RichardKnop/machinery/v2 v2.0.12-0.20231012204029-bdb94a90ca41 h1:7fLtRodG39Hn6AKqKE+ejO0Jj9B8O9RQOto13lkoDac=
github.com/RichardKnop/machinery/v2 v2.0.12-0.20231012204029-bdb94a90ca41/go.mod h1:92dLVxckr2Lv7oKxoS3Uci0Of8Cuy+lmPi2dd6Euwkw=
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1755 h1:J45/QHgrzUdqe/Vco/Vxk0wRvdS2nKUxmf/zLgvfass=
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1755/go.mod h1:RcDobYh8k5VP6TNybz9m++gL3ijVI5wueVr0EM10VsU=
github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
@@ -106,6 +102,7 @@ github.com/aws/aws-sdk-go v1.34.28/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/
github.com/aws/aws-sdk-go v1.37.16/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
github.com/aws/aws-sdk-go v1.49.6 h1:yNldzF5kzLBRvKlKz1S0bkvc2+04R1kt13KfBWQBfFA=
github.com/aws/aws-sdk-go v1.49.6/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
github.com/brianvoe/gofakeit/v6 v6.28.0 h1:Xib46XXuQfmlLS2EXRuJpqcw8St6qSZz75OUo0tgAW4=
@@ -143,8 +140,6 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
github.com/cloudflare/cloudflare-go v0.86.0 h1:jEKN5VHNYNYtfDL2lUFLTRo+nOVNPFxpXTstVx0rqHI=
github.com/cloudflare/cloudflare-go v0.86.0/go.mod h1:wYW/5UP02TUfBToa/yKbQHV+r6h1NnJ1Je7XjuGM4Jw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
@@ -187,8 +182,6 @@ github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5y
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
@@ -210,13 +203,9 @@ github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec
github.com/glebarez/go-sqlite v1.22.0/go.mod h1:PlBIdHe0+aUEFn+r2/uthrWq4FxbzugL0L8Li6yQJbc=
github.com/glebarez/sqlite v1.10.0 h1:u4gt8y7OND/cCei/NMHmfbLxF6xP2wgKcT/BJf2pYkc=
github.com/glebarez/sqlite v1.10.0/go.mod h1:IJ+lfSOmiekhQsFTJRx/lHtGYmCdtAiTaf5wI9u5uHA=
github.com/go-acme/lego/v4 v4.16.1 h1:JxZ93s4KG0jL27rZ30UsIgxap6VGzKuREsSkkyzeoCQ=
github.com/go-acme/lego/v4 v4.16.1/go.mod h1:AVvwdPned/IWpD/ihHhMsKnveF7HHYAz/CmtXi7OZoE=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-jose/go-jose/v4 v4.0.1 h1:QVEPDE3OluqXBQZDcnNvQrInro2h0e4eqNbnZSWqS6U=
github.com/go-jose/go-jose/v4 v4.0.1/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
@@ -291,7 +280,6 @@ github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
@@ -363,8 +351,6 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
@@ -423,15 +409,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFb
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c=
github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hashicorp/go-retryablehttp v0.7.5 h1:bJj+Pj19UZMIweq/iie+1u5YCdGrnxCT9yvm0e+Nd5M=
github.com/hashicorp/go-retryablehttp v0.7.5/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
@@ -448,10 +427,12 @@ github.com/imroc/req/v3 v3.43.1/go.mod h1:SQIz5iYop16MJxbo8ib+4LnostGCok8NQf8Toy
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgx/v5 v5.4.3 h1:cxFyXhxlvAifxnkKKdlxv8XqUf59tDlYjnV5YYfsJJY=
github.com/jackc/pgx/v5 v5.4.3/go.mod h1:Ig06C2Vu0t5qXC60W8sqIthScaEnFvojjj9dSljmHRA=
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 h1:L0QtFUgDarD7Fpv9jeVMgy/+Ec0mtnmYuImjTz6dtDA=
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw=
github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A=
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=
github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM=
github.com/jcmturner/gofork v1.7.6/go.mod h1:1622LH6i/EZqLloHfE7IeZ0uEJwMSUyQ/nDd82IeqRo=
@@ -463,7 +444,6 @@ github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkr
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
@@ -471,7 +451,6 @@ github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfC
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
@@ -513,22 +492,30 @@ github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc h1:RKf14vYWi2t
github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc/go.mod h1:kopuH9ugFRkIXf3YoqHKyrJ9YfUFsckUU9S7B+XP+is=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/libdns/alidns v1.0.3 h1:LFHuGnbseq5+HCeGa1aW8awyX/4M2psB9962fdD2+yQ=
github.com/libdns/alidns v1.0.3/go.mod h1:e18uAG6GanfRhcJj6/tps2rCMzQJaYVcGKT+ELjdjGE=
github.com/libdns/cloudflare v0.1.1 h1:FVPfWwP8zZCqj268LZjmkDleXlHPlFU9KC4OJ3yn054=
github.com/libdns/cloudflare v0.1.1/go.mod h1:9VK91idpOjg6v7/WbjkEW49bSCxj00ALesIFDhJ8PBU=
github.com/libdns/dnspod v0.0.3 h1:xJHDIujgLjvZnpB8/rMoCHUqA/KxSGBqRUXxSIzNzAA=
github.com/libdns/dnspod v0.0.3/go.mod h1:XLnqMmK7QlLPEbHwcOxbRvlzRvDgaaUlthRNFOPjXPI=
github.com/libdns/libdns v0.2.0/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40=
github.com/libdns/libdns v0.2.1/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40=
github.com/libdns/libdns v0.2.2 h1:O6ws7bAfRPaBsgAYt8MDe2HcNBGC29hkZ9MX2eUSX3s=
github.com/libdns/libdns v0.2.2/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE=
github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mholt/acmez v1.2.0 h1:1hhLxSgY5FvH5HCnGUuwbKY2VQVo8IU7rxXKSnZ7F30=
github.com/mholt/acmez v1.2.0/go.mod h1:VT9YwH1xgNX1kmYY89gY8xPJC84BFAisjo8Egigt4kE=
github.com/mholt/archiver/v3 v3.5.1 h1:rDjOBX9JSF5BvoJGvjqK479aL70qh9DIpZCl+k7Clwo=
github.com/mholt/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4=
github.com/microsoft/go-mssqldb v1.6.0 h1:mM3gYdVwEPFrlg/Dvr2DNVEgYFG7L42l+dGc67NNNpc=
github.com/microsoft/go-mssqldb v1.6.0/go.mod h1:00mDtPbeQCRGC1HwOOR5K/gr30P1NcEG0vx6Kbv2aJU=
github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4=
github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
@@ -536,7 +523,6 @@ github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8=
@@ -756,12 +742,16 @@ go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.7.0 h1:pskyeJh/3AmoQ8CPE95vxHLqp1G1GfGNXTmcl9NEKTc=
golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
@@ -1168,8 +1158,8 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -1177,7 +1167,6 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=

View File

@@ -1,8 +1,6 @@
package internal
import (
"github.com/go-acme/lego/v4/certificate"
requests "panel/app/http/requests/cert"
"panel/app/models"
"panel/pkg/acme"
@@ -21,8 +19,8 @@ type Cert interface {
CertUpdate(request requests.CertUpdate) error
CertShow(ID uint) (models.Cert, error)
CertDestroy(ID uint) error
ObtainAuto(ID uint) (certificate.Resource, error)
ObtainManual(ID uint) (certificate.Resource, error)
ManualDNS(ID uint) (map[string]acme.Resolve, error)
Renew(ID uint) (certificate.Resource, error)
ObtainAuto(ID uint) (acme.Certificate, error)
ObtainManual(ID uint) (acme.Certificate, error)
ManualDNS(ID uint) ([]acme.DNSRecord, error)
Renew(ID uint) (acme.Certificate, error)
}

View File

@@ -2,10 +2,10 @@
package services
import (
"context"
"errors"
"time"
"github.com/go-acme/lego/v4/certcrypto"
"github.com/go-acme/lego/v4/certificate"
"github.com/goravel/framework/facades"
requests "panel/app/http/requests/cert"
@@ -15,6 +15,7 @@ import (
)
type CertImpl struct {
client *acme.Client
}
func NewCertImpl() *CertImpl {
@@ -34,15 +35,15 @@ func (s *CertImpl) UserStore(request requests.UserStore) error {
var client *acme.Client
switch user.CA {
case "letsencrypt":
client, err = acme.NewRegisterClient(user.Email, acme.CALetEncrypt, certcrypto.KeyType(user.KeyType))
client, err = acme.NewRegisterAccount(context.Background(), user.Email, acme.CALetsEncrypt, nil, acme.KeyType(user.KeyType))
case "buypass":
client, err = acme.NewRegisterClient(user.Email, acme.CABuypass, certcrypto.KeyType(user.KeyType))
client, err = acme.NewRegisterAccount(context.Background(), user.Email, acme.CABuypass, nil, acme.KeyType(user.KeyType))
case "zerossl":
client, err = acme.NewRegisterWithExternalAccountBindingClient(user.Email, *user.Kid, *user.HmacEncoded, acme.CAZeroSSL, certcrypto.KeyType(user.KeyType))
client, err = acme.NewRegisterAccount(context.Background(), user.Email, acme.CAZeroSSL, &acme.EAB{KeyID: *user.Kid, MACKey: *user.HmacEncoded}, acme.KeyType(user.KeyType))
case "sslcom":
client, err = acme.NewRegisterWithExternalAccountBindingClient(user.Email, *user.Kid, *user.HmacEncoded, acme.CASSLcom, certcrypto.KeyType(user.KeyType))
client, err = acme.NewRegisterAccount(context.Background(), user.Email, acme.CASSLcom, &acme.EAB{KeyID: *user.Kid, MACKey: *user.HmacEncoded}, acme.KeyType(user.KeyType))
case "google":
client, err = acme.NewRegisterWithExternalAccountBindingClient(user.Email, *user.Kid, *user.HmacEncoded, acme.CAGoogle, certcrypto.KeyType(user.KeyType))
client, err = acme.NewRegisterAccount(context.Background(), user.Email, acme.CAGoogle, &acme.EAB{KeyID: *user.Kid, MACKey: *user.HmacEncoded}, acme.KeyType(user.KeyType))
default:
return errors.New("CA 提供商不支持")
}
@@ -51,7 +52,7 @@ func (s *CertImpl) UserStore(request requests.UserStore) error {
return errors.New("向 CA 注册账号失败,请检查参数是否正确")
}
privateKey, err := acme.GetPrivateKey(client.User.GetPrivateKey(), acme.KeyType(user.KeyType))
privateKey, err := acme.EncodePrivateKey(client.Account.PrivateKey)
if err != nil {
return errors.New("获取私钥失败")
}
@@ -77,15 +78,15 @@ func (s *CertImpl) UserUpdate(request requests.UserUpdate) error {
var client *acme.Client
switch user.CA {
case "letsencrypt":
client, err = acme.NewRegisterClient(user.Email, acme.CALetEncrypt, certcrypto.KeyType(user.KeyType))
client, err = acme.NewRegisterAccount(context.Background(), user.Email, acme.CALetsEncrypt, nil, acme.KeyType(user.KeyType))
case "buypass":
client, err = acme.NewRegisterClient(user.Email, acme.CABuypass, certcrypto.KeyType(user.KeyType))
client, err = acme.NewRegisterAccount(context.Background(), user.Email, acme.CABuypass, nil, acme.KeyType(user.KeyType))
case "zerossl":
client, err = acme.NewRegisterWithExternalAccountBindingClient(user.Email, *user.Kid, *user.HmacEncoded, acme.CAZeroSSL, certcrypto.KeyType(user.KeyType))
client, err = acme.NewRegisterAccount(context.Background(), user.Email, acme.CAZeroSSL, &acme.EAB{KeyID: *user.Kid, MACKey: *user.HmacEncoded}, acme.KeyType(user.KeyType))
case "sslcom":
client, err = acme.NewRegisterWithExternalAccountBindingClient(user.Email, *user.Kid, *user.HmacEncoded, acme.CASSLcom, certcrypto.KeyType(user.KeyType))
client, err = acme.NewRegisterAccount(context.Background(), user.Email, acme.CASSLcom, &acme.EAB{KeyID: *user.Kid, MACKey: *user.HmacEncoded}, acme.KeyType(user.KeyType))
case "google":
client, err = acme.NewRegisterWithExternalAccountBindingClient(user.Email, *user.Kid, *user.HmacEncoded, acme.CAGoogle, certcrypto.KeyType(user.KeyType))
client, err = acme.NewRegisterAccount(context.Background(), user.Email, acme.CAGoogle, &acme.EAB{KeyID: *user.Kid, MACKey: *user.HmacEncoded}, acme.KeyType(user.KeyType))
default:
return errors.New("CA 提供商不支持")
}
@@ -94,7 +95,7 @@ func (s *CertImpl) UserUpdate(request requests.UserUpdate) error {
return errors.New("向 CA 注册账号失败,请检查参数是否正确")
}
privateKey, err := acme.GetPrivateKey(client.User.GetPrivateKey(), acme.KeyType(user.KeyType))
privateKey, err := acme.EncodePrivateKey(client.Account.PrivateKey)
if err != nil {
return errors.New("获取私钥失败")
}
@@ -238,67 +239,50 @@ func (s *CertImpl) CertDestroy(ID uint) error {
}
// ObtainAuto 自动签发证书
func (s *CertImpl) ObtainAuto(ID uint) (certificate.Resource, error) {
func (s *CertImpl) ObtainAuto(ID uint) (acme.Certificate, error) {
var cert models.Cert
err := facades.Orm().Query().With("Website").With("User").With("DNS").Where("id = ?", ID).First(&cert)
if err != nil {
return certificate.Resource{}, err
return acme.Certificate{}, err
}
var ca string
switch cert.User.CA {
case "letsencrypt":
ca = acme.CALetEncrypt
case "buypass":
ca = acme.CABuypass
case "zerossl":
ca = acme.CAZeroSSL
case "sslcom":
ca = acme.CASSLcom
case "google":
ca = acme.CAGoogle
}
client, err := acme.NewPrivateKeyClient(cert.User.Email, cert.User.PrivateKey, ca, certcrypto.KeyType(cert.User.KeyType))
client, err := s.getClient(cert)
if err != nil {
return certificate.Resource{}, err
return acme.Certificate{}, err
}
if cert.DNS != nil {
err = client.UseDns(acme.DnsType(cert.DNS.Type), cert.DNS.Data)
client.UseDns(acme.DnsType(cert.DNS.Type), cert.DNS.Data)
} else {
if cert.Website == nil {
return certificate.Resource{}, errors.New("该证书没有关联网站,无法自动签发")
return acme.Certificate{}, errors.New("该证书没有关联网站,无法自动签发")
} else {
err = client.UseHTTP(cert.Website.Path)
client.UseHTTP(cert.Website.Path)
}
}
ssl, err := client.ObtainSSL(context.Background(), cert.Domains, acme.KeyType(cert.Type))
if err != nil {
return certificate.Resource{}, err
return acme.Certificate{}, err
}
ssl, err := client.ObtainSSL(cert.Domains)
if err != nil {
return certificate.Resource{}, err
}
cert.CertURL = &ssl.CertURL
cert.Cert = string(ssl.Certificate)
cert.CertURL = &ssl.URL
cert.Cert = string(ssl.ChainPEM)
cert.Key = string(ssl.PrivateKey)
err = facades.Orm().Query().Save(&cert)
if err != nil {
return certificate.Resource{}, err
return acme.Certificate{}, err
}
if cert.Website != nil {
if err := tools.Write("/www/server/vhost/ssl/"+cert.Website.Name+".pem", string(ssl.Certificate), 0644); err != nil {
return certificate.Resource{}, err
if err := tools.Write("/www/server/vhost/ssl/"+cert.Website.Name+".pem", cert.Cert, 0644); err != nil {
return acme.Certificate{}, err
}
if err := tools.Write("/www/server/vhost/ssl/"+cert.Website.Name+".key", string(ssl.PrivateKey), 0644); err != nil {
return certificate.Resource{}, err
if err := tools.Write("/www/server/vhost/ssl/"+cert.Website.Name+".key", cert.Key, 0644); err != nil {
return acme.Certificate{}, err
}
if _, err := tools.Exec("systemctl reload openresty"); err != nil {
return certificate.Resource{}, err
if err = tools.ServiceReload("openresty"); err != nil {
return acme.Certificate{}, err
}
}
@@ -306,59 +290,39 @@ func (s *CertImpl) ObtainAuto(ID uint) (certificate.Resource, error) {
}
// ObtainManual 手动签发证书
func (s *CertImpl) ObtainManual(ID uint) (certificate.Resource, error) {
func (s *CertImpl) ObtainManual(ID uint) (acme.Certificate, error) {
var cert models.Cert
err := facades.Orm().Query().With("User").Where("id = ?", ID).First(&cert)
if err != nil {
return certificate.Resource{}, err
return acme.Certificate{}, err
}
var ca string
switch cert.User.CA {
case "letsencrypt":
ca = acme.CALetEncrypt
case "buypass":
ca = acme.CABuypass
case "zerossl":
ca = acme.CAZeroSSL
case "sslcom":
ca = acme.CASSLcom
case "google":
ca = acme.CAGoogle
if s.client == nil {
return acme.Certificate{}, errors.New("请重新获取 DNS 解析记录")
}
client, err := acme.NewPrivateKeyClient(cert.User.Email, cert.User.PrivateKey, ca, certcrypto.KeyType(cert.User.KeyType))
ssl, err := s.client.ObtainSSLManual()
if err != nil {
return certificate.Resource{}, err
return acme.Certificate{}, err
}
err = client.UseManualDns()
if err != nil {
return certificate.Resource{}, err
}
ssl, err := client.ObtainSSL(cert.Domains)
if err != nil {
return certificate.Resource{}, err
}
cert.CertURL = &ssl.CertURL
cert.Cert = string(ssl.Certificate)
cert.CertURL = &ssl.URL
cert.Cert = string(ssl.ChainPEM)
cert.Key = string(ssl.PrivateKey)
err = facades.Orm().Query().Save(&cert)
if err != nil {
return certificate.Resource{}, err
return acme.Certificate{}, err
}
if cert.Website != nil {
if err := tools.Write("/www/server/vhost/ssl/"+cert.Website.Name+".pem", string(ssl.Certificate), 0644); err != nil {
return certificate.Resource{}, err
if err := tools.Write("/www/server/vhost/ssl/"+cert.Website.Name+".pem", cert.Cert, 0644); err != nil {
return acme.Certificate{}, err
}
if err := tools.Write("/www/server/vhost/ssl/"+cert.Website.Name+".key", string(ssl.PrivateKey), 0644); err != nil {
return certificate.Resource{}, err
if err := tools.Write("/www/server/vhost/ssl/"+cert.Website.Name+".key", cert.Key, 0644); err != nil {
return acme.Certificate{}, err
}
if _, err := tools.Exec("systemctl reload openresty"); err != nil {
return certificate.Resource{}, err
if err = tools.ServiceReload("openresty"); err != nil {
return acme.Certificate{}, err
}
}
@@ -366,108 +330,106 @@ func (s *CertImpl) ObtainManual(ID uint) (certificate.Resource, error) {
}
// ManualDNS 获取手动 DNS 解析信息
func (s *CertImpl) ManualDNS(ID uint) (map[string]acme.Resolve, error) {
func (s *CertImpl) ManualDNS(ID uint) ([]acme.DNSRecord, error) {
var cert models.Cert
err := facades.Orm().Query().With("User").Where("id = ?", ID).First(&cert)
if err != nil {
return nil, err
}
var ca string
switch cert.User.CA {
case "letsencrypt":
ca = acme.CALetEncrypt
case "buypass":
ca = acme.CABuypass
case "zerossl":
ca = acme.CAZeroSSL
case "sslcom":
ca = acme.CASSLcom
case "google":
ca = acme.CAGoogle
}
client, err := acme.NewPrivateKeyClient(cert.User.Email, cert.User.PrivateKey, ca, certcrypto.KeyType(cert.User.KeyType))
client, err := s.getClient(cert)
if err != nil {
return nil, err
}
err = client.UseManualDns()
if err != nil {
return nil, err
}
client.UseManualDns(len(cert.Domains))
records, err := client.GetDNSRecords(context.Background(), cert.Domains, acme.KeyType(cert.Type))
return client.GetDNSResolve(cert.Domains)
// 15 分钟后清理客户端
s.client = client
time.AfterFunc(15*time.Minute, func() {
s.client = nil
})
return records, err
}
// Renew 续签证书
func (s *CertImpl) Renew(ID uint) (certificate.Resource, error) {
func (s *CertImpl) Renew(ID uint) (acme.Certificate, error) {
var cert models.Cert
err := facades.Orm().Query().With("Website").With("User").With("DNS").Where("id = ?", ID).First(&cert)
if err != nil {
return certificate.Resource{}, err
return acme.Certificate{}, err
}
var ca string
switch cert.User.CA {
case "letsencrypt":
ca = acme.CALetEncrypt
case "buypass":
ca = acme.CABuypass
case "zerossl":
ca = acme.CAZeroSSL
case "sslcom":
ca = acme.CASSLcom
case "google":
ca = acme.CAGoogle
}
client, err := acme.NewPrivateKeyClient(cert.User.Email, cert.User.PrivateKey, ca, certcrypto.KeyType(cert.User.KeyType))
client, err := s.getClient(cert)
if err != nil {
return certificate.Resource{}, err
return acme.Certificate{}, err
}
if cert.CertURL == nil {
return certificate.Resource{}, errors.New("该证书没有签发成功,无法续签")
return acme.Certificate{}, errors.New("该证书没有签发成功,无法续签")
}
if cert.DNS != nil {
err = client.UseDns(acme.DnsType(cert.DNS.Type), cert.DNS.Data)
client.UseDns(acme.DnsType(cert.DNS.Type), cert.DNS.Data)
} else {
if cert.Website == nil {
return certificate.Resource{}, errors.New("该证书没有关联网站,无法续签,可以尝试手动签发")
return acme.Certificate{}, errors.New("该证书没有关联网站,无法续签,可以尝试手动签发")
} else {
err = client.UseHTTP(cert.Website.Path)
client.UseHTTP(cert.Website.Path)
}
}
if err != nil {
return certificate.Resource{}, err
return acme.Certificate{}, err
}
ssl, err := client.RenewSSL(*cert.CertURL)
ssl, err := client.RenewSSL(context.Background(), *cert.CertURL, cert.Domains, acme.KeyType(cert.Type))
if err != nil {
return certificate.Resource{}, err
return acme.Certificate{}, err
}
cert.CertURL = &ssl.CertURL
cert.Cert = string(ssl.Certificate)
cert.CertURL = &ssl.URL
cert.Cert = string(ssl.ChainPEM)
cert.Key = string(ssl.PrivateKey)
err = facades.Orm().Query().Save(&cert)
if err != nil {
return certificate.Resource{}, err
return acme.Certificate{}, err
}
if cert.Website != nil {
if err := tools.Write("/www/server/vhost/ssl/"+cert.Website.Name+".pem", string(ssl.Certificate), 0644); err != nil {
return certificate.Resource{}, err
if err := tools.Write("/www/server/vhost/ssl/"+cert.Website.Name+".pem", cert.Cert, 0644); err != nil {
return acme.Certificate{}, err
}
if err := tools.Write("/www/server/vhost/ssl/"+cert.Website.Name+".key", string(ssl.PrivateKey), 0644); err != nil {
return certificate.Resource{}, err
if err := tools.Write("/www/server/vhost/ssl/"+cert.Website.Name+".key", cert.Key, 0644); err != nil {
return acme.Certificate{}, err
}
if _, err := tools.Exec("systemctl reload openresty"); err != nil {
return certificate.Resource{}, err
if err = tools.ServiceReload("openresty"); err != nil {
return acme.Certificate{}, err
}
}
return ssl, nil
}
func (s *CertImpl) getClient(cert models.Cert) (*acme.Client, error) {
var ca string
var eab *acme.EAB
switch cert.User.CA {
case "letsencrypt":
ca = acme.CALetsEncrypt
case "buypass":
ca = acme.CABuypass
case "zerossl":
ca = acme.CAZeroSSL
eab = &acme.EAB{KeyID: *cert.User.Kid, MACKey: *cert.User.HmacEncoded}
case "sslcom":
ca = acme.CASSLcom
eab = &acme.EAB{KeyID: *cert.User.Kid, MACKey: *cert.User.HmacEncoded}
case "google":
ca = acme.CAGoogle
eab = &acme.EAB{KeyID: *cert.User.Kid, MACKey: *cert.User.HmacEncoded}
}
return acme.NewPrivateKeyAccount(cert.User.Email, cert.User.PrivateKey, ca, eab)
}

View File

@@ -1,177 +1,195 @@
package acme
import (
"context"
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
"net/http"
"strings"
"github.com/go-acme/lego/v4/certcrypto"
"github.com/go-acme/lego/v4/lego"
"github.com/go-acme/lego/v4/registration"
"github.com/mholt/acmez"
"github.com/mholt/acmez/acme"
"go.uber.org/zap"
)
const (
CALetEncrypt = "https://acme-v02.api.letsencrypt.org/directory"
CAZeroSSL = "https://acme.zerossl.com/v2/DV90"
CAGoogle = "https://dv.acme-v02.api.pki.goog/directory"
CABuypass = "https://api.buypass.com/acme/directory"
CASSLcom = "https://acme.ssl.com/sslcom-dv-rsa"
CALetsEncryptStaging = "https://acme-staging-v02.api.letsencrypt.org/directory"
CALetsEncrypt = "https://acme-v02.api.letsencrypt.org/directory"
CAZeroSSL = "https://acme.zerossl.com/v2/DV90"
CAGoogle = "https://dv.acme-v02.api.pki.goog/directory"
CABuypass = "https://api.buypass.com/acme/directory"
CASSLcom = "https://acme.ssl.com/sslcom-dv-rsa"
)
type KeyType = certcrypto.KeyType
type KeyType string
const (
KeyEC256 = certcrypto.EC256
KeyEC384 = certcrypto.EC384
KeyRSA2048 = certcrypto.RSA2048
KeyRSA3072 = certcrypto.RSA3072
KeyRSA4096 = certcrypto.RSA4096
KeyEC256 = KeyType("P256")
KeyEC384 = KeyType("P384")
KeyRSA2048 = KeyType("2048")
KeyRSA3072 = KeyType("3072")
KeyRSA4096 = KeyType("4096")
)
type domainError struct {
Domain string
Error error
}
type EAB = acme.EAB
type User struct {
Email string
Registration *registration.Resource
Key crypto.PrivateKey
}
func NewRegisterAccount(ctx context.Context, email, CA string, eab *EAB, keyType KeyType) (*Client, error) {
client, err := getClient(CA)
if err != nil {
return nil, err
}
func (u *User) GetEmail() string {
return u.Email
}
func (u *User) GetRegistration() *registration.Resource {
return u.Registration
}
func (u *User) GetPrivateKey() crypto.PrivateKey {
return u.Key
}
func GetPrivateKey(priKey crypto.PrivateKey, keyType KeyType) ([]byte, error) {
var marshal []byte
var block *pem.Block
var err error
switch keyType {
case KeyEC256, KeyEC384:
key := priKey.(*ecdsa.PrivateKey)
marshal, err = x509.MarshalECPrivateKey(key)
accountPrivateKey, err := generatePrivateKey(keyType)
if err != nil {
return nil, err
}
account := acme.Account{
Contact: []string{"mailto:" + email},
TermsOfServiceAgreed: true,
PrivateKey: accountPrivateKey,
}
if eab != nil {
err = account.SetExternalAccountBinding(ctx, client.Client, *eab)
if err != nil {
return nil, err
}
block = &pem.Block{
Type: "PRIVATE KEY",
Bytes: marshal,
}
case KeyRSA2048, KeyRSA3072, KeyRSA4096:
key := priKey.(*rsa.PrivateKey)
marshal = x509.MarshalPKCS1PrivateKey(key)
block = &pem.Block{
Type: "privateKey",
Bytes: marshal,
}
account, err = client.NewAccount(ctx, account)
if err != nil {
return nil, err
}
return &Client{Account: account, zClient: client}, nil
}
func NewPrivateKeyAccount(email string, privateKey string, CA string, eab *EAB) (*Client, error) {
client, err := getClient(CA)
if err != nil {
return nil, err
}
key, err := parsePrivateKey([]byte(privateKey))
if err != nil {
return nil, err
}
account := acme.Account{
Contact: []string{"mailto:" + email},
TermsOfServiceAgreed: true,
PrivateKey: key,
}
if eab != nil {
err = account.SetExternalAccountBinding(context.Background(), client.Client, *eab)
if err != nil {
return nil, err
}
}
return pem.EncodeToMemory(block), nil
account, err = client.GetAccount(context.Background(), account)
if err != nil {
return nil, err
}
return &Client{Account: account, zClient: client}, nil
}
func NewRegisterClient(email string, CA string, keyType certcrypto.KeyType) (*Client, error) {
privateKey, err := certcrypto.GeneratePrivateKey(keyType)
if err != nil {
return nil, err
func parsePrivateKey(key []byte) (crypto.Signer, error) {
keyBlockDER, _ := pem.Decode(key)
if keyBlockDER == nil {
return nil, errors.New("invalid PEM block")
}
user := &User{
Email: email,
Key: privateKey,
}
config := lego.NewConfig(user)
config.CADirURL = CA
config.Certificate.KeyType = keyType
client, err := lego.NewClient(config)
if err != nil {
return nil, err
}
reg, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
if err != nil {
return nil, err
}
user.Registration = reg
acmeClient := &Client{
User: user,
Client: client,
Config: config,
if keyBlockDER.Type != "PRIVATE KEY" && !strings.HasSuffix(keyBlockDER.Type, " PRIVATE KEY") {
return nil, fmt.Errorf("unknown PEM header %q", keyBlockDER.Type)
}
return acmeClient, nil
if parse, err := x509.ParsePKCS1PrivateKey(keyBlockDER.Bytes); err == nil {
return parse, nil
}
if parse, err := x509.ParsePKCS8PrivateKey(keyBlockDER.Bytes); err == nil {
switch parse.(type) {
case *rsa.PrivateKey, *ecdsa.PrivateKey, ed25519.PrivateKey:
return parse.(crypto.Signer), nil
default:
return nil, fmt.Errorf("found unknown private key type in PKCS#8 wrapping: %T", key)
}
}
if parse, err := x509.ParseECPrivateKey(keyBlockDER.Bytes); err == nil {
return parse, nil
}
return nil, errors.New("解析私钥失败")
}
func NewRegisterWithExternalAccountBindingClient(email, kid, hmac, CA string, keyType certcrypto.KeyType) (*Client, error) {
privateKey, err := certcrypto.GeneratePrivateKey(keyType)
if err != nil {
return nil, err
func generatePrivateKey(keyType KeyType) (crypto.Signer, error) {
switch keyType {
case KeyEC256:
return ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
case KeyEC384:
return ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
case KeyRSA2048:
return rsa.GenerateKey(rand.Reader, 2048)
case KeyRSA3072:
return rsa.GenerateKey(rand.Reader, 3072)
case KeyRSA4096:
return rsa.GenerateKey(rand.Reader, 4096)
}
user := &User{
Email: email,
Key: privateKey,
}
config := lego.NewConfig(user)
config.CADirURL = CA
config.Certificate.KeyType = keyType
client, err := lego.NewClient(config)
if err != nil {
return nil, err
}
reg, err := client.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{TermsOfServiceAgreed: true, Kid: kid, HmacEncoded: hmac})
if err != nil {
return nil, err
}
user.Registration = reg
acmeClient := &Client{
User: user,
Client: client,
Config: config,
}
return acmeClient, nil
return nil, errors.New("未知的密钥类型")
}
func NewPrivateKeyClient(email string, privateKey string, CA string, keyType certcrypto.KeyType) (*Client, error) {
key, err := certcrypto.ParsePEMPrivateKey([]byte(privateKey))
if err != nil {
return nil, err
func EncodePrivateKey(key crypto.Signer) ([]byte, error) {
var pemType string
var keyBytes []byte
switch key := key.(type) {
case *ecdsa.PrivateKey:
var err error
pemType = "EC"
keyBytes, err = x509.MarshalECPrivateKey(key)
if err != nil {
return nil, err
}
case *rsa.PrivateKey:
pemType = "RSA"
keyBytes = x509.MarshalPKCS1PrivateKey(key)
case ed25519.PrivateKey:
var err error
pemType = "ED25519"
keyBytes, err = x509.MarshalPKCS8PrivateKey(key)
if err != nil {
return nil, err
}
default:
return nil, fmt.Errorf("未知的密钥类型 %T", key)
}
user := &User{
Email: email,
Key: key,
}
config := lego.NewConfig(user)
config.CADirURL = CA
config.Certificate.KeyType = keyType
client, err := lego.NewClient(config)
if err != nil {
return nil, err
}
reg, err := client.Registration.ResolveAccountByKey()
if err != nil {
return nil, err
}
user.Registration = reg
acmeClient := &Client{
User: user,
Client: client,
Config: config,
}
return acmeClient, nil
pemKey := pem.Block{Type: pemType + " PRIVATE KEY", Bytes: keyBytes}
return pem.EncodeToMemory(&pemKey), nil
}
func getClient(CA string) (acmez.Client, error) {
logger, err := zap.NewProduction()
if err != nil {
return acmez.Client{}, err
}
client := acmez.Client{
Client: &acme.Client{
Directory: CA,
HTTPClient: http.DefaultClient,
Logger: logger,
},
}
return client, nil
}

View File

@@ -1,192 +1,139 @@
package acme
import (
"time"
"context"
"sort"
"github.com/go-acme/lego/v4/acme"
"github.com/go-acme/lego/v4/acme/api"
"github.com/go-acme/lego/v4/certificate"
"github.com/go-acme/lego/v4/challenge"
"github.com/go-acme/lego/v4/challenge/dns01"
"github.com/go-acme/lego/v4/lego"
"github.com/go-acme/lego/v4/providers/dns/alidns"
"github.com/go-acme/lego/v4/providers/dns/cloudflare"
"github.com/go-acme/lego/v4/providers/dns/dnspod"
"github.com/go-acme/lego/v4/providers/http/webroot"
"github.com/libdns/libdns"
"github.com/mholt/acmez"
"github.com/mholt/acmez/acme"
)
type Client struct {
Config *lego.Config
Client *lego.Client
User *User
type Certificate struct {
PrivateKey []byte
acme.Certificate
}
type DnsType string
const (
DnsPod DnsType = "dnspod"
AliYun DnsType = "aliyun"
CloudFlare DnsType = "cloudflare"
)
type DNSParam struct {
ID string `form:"id" json:"id"`
Token string `form:"token" json:"token"`
AccessKey string `form:"access_key" json:"access_key"`
SecretKey string `form:"secret_key" json:"secret_key"`
Email string `form:"email" json:"email"`
APIkey string `form:"api_key" json:"api_key"`
type Client struct {
Account acme.Account
zClient acmez.Client
// 手动 DNS 所需的信号通道
manualDNSSolver
}
// UseDns 使用 DNS 接口验证
func (c *Client) UseDns(dnsType DnsType, param DNSParam) error {
var p challenge.Provider
var err error
if dnsType == DnsPod {
dnsPodConfig := dnspod.NewDefaultConfig()
dnsPodConfig.LoginToken = param.ID + "," + param.Token
p, err = dnspod.NewDNSProviderConfig(dnsPodConfig)
if err != nil {
return err
}
func (c *Client) UseDns(dnsType DnsType, param DNSParam) {
c.zClient.ChallengeSolvers = map[string]acmez.Solver{
acme.ChallengeTypeDNS01: dnsSolver{
dns: dnsType,
param: param,
records: &[]libdns.Record{},
},
}
if dnsType == AliYun {
aliyunConfig := alidns.NewDefaultConfig()
aliyunConfig.SecretKey = param.SecretKey
aliyunConfig.APIKey = param.AccessKey
p, err = alidns.NewDNSProviderConfig(aliyunConfig)
if err != nil {
return err
}
}
if dnsType == CloudFlare {
cloudflareConfig := cloudflare.NewDefaultConfig()
cloudflareConfig.AuthEmail = param.Email
cloudflareConfig.AuthToken = param.APIkey
p, err = cloudflare.NewDNSProviderConfig(cloudflareConfig)
if err != nil {
return err
}
}
return c.Client.Challenge.SetDNS01Provider(p, dns01.AddDNSTimeout(3*time.Minute))
}
// UseManualDns 使用手动 DNS 验证
func (c *Client) UseManualDns(checkDns ...bool) error {
p := &manualDnsProvider{}
var err error
if len(checkDns) > 0 && !checkDns[0] {
err = c.Client.Challenge.SetDNS01Provider(p, dns01.DisableCompletePropagationRequirement())
} else {
err = c.Client.Challenge.SetDNS01Provider(p, dns01.AddDNSTimeout(3*time.Minute))
func (c *Client) UseManualDns(total int, check ...bool) {
c.controlChan = make(chan struct{})
c.dataChan = make(chan any)
c.zClient.ChallengeSolvers = map[string]acmez.Solver{
acme.ChallengeTypeDNS01: manualDNSSolver{
check: len(check) > 0 && check[0],
controlChan: c.controlChan,
dataChan: c.dataChan,
records: &[]DNSRecord{},
},
}
return err
}
// UseHTTP 使用 HTTP 验证
func (c *Client) UseHTTP(path string) error {
httpProvider, err := webroot.NewHTTPProvider(path)
if err != nil {
return err
func (c *Client) UseHTTP(path string) {
c.zClient.ChallengeSolvers = map[string]acmez.Solver{
acme.ChallengeTypeHTTP01: httpSolver{
path: path,
},
}
err = c.Client.Challenge.SetHTTP01Provider(httpProvider)
if err != nil {
return err
}
return nil
}
// ObtainSSL 签发 SSL 证书
func (c *Client) ObtainSSL(domains []string) (certificate.Resource, error) {
request := certificate.ObtainRequest{
Domains: domains,
Bundle: true,
MustStaple: false,
}
certificates, err := c.Client.Certificate.Obtain(request)
func (c *Client) ObtainSSL(ctx context.Context, domains []string, keyType KeyType) (Certificate, error) {
certPrivateKey, err := generatePrivateKey(keyType)
if err != nil {
return certificate.Resource{}, err
return Certificate{}, err
}
pemPrivateKey, err := EncodePrivateKey(certPrivateKey)
if err != nil {
return Certificate{}, err
}
return *certificates, nil
certs, err := c.zClient.ObtainCertificate(ctx, c.Account, certPrivateKey, domains)
if err != nil {
return Certificate{}, err
}
cert := c.selectPreferredChain(certs)
return Certificate{PrivateKey: pemPrivateKey, Certificate: cert}, nil
}
// ObtainSSLManual 手动验证 SSL 证书
func (c *Client) ObtainSSLManual() (Certificate, error) {
// 发送信号,开始验证
c.controlChan <- struct{}{}
// 等待验证完成
data := <-c.dataChan
if err, ok := data.(error); ok {
return Certificate{}, err
}
return data.(Certificate), nil
}
// RenewSSL 续签 SSL 证书
func (c *Client) RenewSSL(certUrl string) (certificate.Resource, error) {
certificates, err := c.Client.Certificate.Get(certUrl, true)
func (c *Client) RenewSSL(ctx context.Context, certUrl string, domains []string, keyType KeyType) (Certificate, error) {
_, err := c.zClient.GetCertificateChain(ctx, c.Account, certUrl)
if err != nil {
return certificate.Resource{}, err
return Certificate{}, err
}
certificates, err = c.Client.Certificate.RenewWithOptions(*certificates, &certificate.RenewOptions{
Bundle: true,
MustStaple: false,
return c.ObtainSSL(ctx, domains, keyType)
}
// GetDNSRecords 获取 DNS 解析(手动设置)
func (c *Client) GetDNSRecords(ctx context.Context, domains []string, keyType KeyType) ([]DNSRecord, error) {
go func(ctx context.Context, domains []string, keyType KeyType) {
certs, err := c.ObtainSSL(ctx, domains, keyType)
// 将证书和错误信息发送到 dataChan
if err != nil {
c.dataChan <- err
return
}
c.dataChan <- certs
}(ctx, domains, keyType)
// 这里要少一次循环,因为需要卡住最后一次的 dataChan等待手动 DNS 验证完成
for i := 1; i < len(domains); i++ {
<-c.dataChan
c.controlChan <- struct{}{}
}
// 因为上面少了一次循环,所以这里接收到的即为完整的 DNS 记录切片
data := <-c.dataChan
if err, ok := data.(error); ok {
return nil, err
}
return data.([]DNSRecord), nil
}
func (c *Client) selectPreferredChain(certChains []acme.Certificate) acme.Certificate {
if len(certChains) == 1 {
return certChains[0]
}
sort.Slice(certChains, func(i, j int) bool {
return len(certChains[i].ChainPEM) < len(certChains[j].ChainPEM)
})
if err != nil {
return certificate.Resource{}, err
}
return *certificates, nil
}
// GetDNSResolve 获取 DNS 解析(手动设置)
func (c *Client) GetDNSResolve(domains []string) (map[string]Resolve, error) {
core, err := api.New(c.Config.HTTPClient, c.Config.UserAgent, c.Config.CADirURL, c.User.Registration.URI, c.User.Key)
if err != nil {
return nil, err
}
order, err := core.Orders.New(domains)
if err != nil {
return nil, err
}
resolves := make(map[string]Resolve)
resChan, errChan := make(chan acme.Authorization), make(chan domainError)
for _, authzURL := range order.Authorizations {
go func(authzURL string) {
authz, err := core.Authorizations.Get(authzURL)
if err != nil {
errChan <- domainError{Domain: authz.Identifier.Value, Error: err}
return
}
resChan <- authz
}(authzURL)
}
var responses []acme.Authorization
for i := 0; i < len(order.Authorizations); i++ {
select {
case res := <-resChan:
responses = append(responses, res)
case err := <-errChan:
resolves[err.Domain] = Resolve{Err: err.Error.Error()}
}
}
close(resChan)
close(errChan)
for _, auth := range responses {
domain := challenge.GetTargetedDomain(auth)
acmeChallenge, err := challenge.FindChallenge(challenge.DNS01, auth)
if err != nil {
resolves[domain] = Resolve{Err: err.Error()}
continue
}
keyAuth, err := core.GetKeyAuthorization(acmeChallenge.Token)
if err != nil {
resolves[domain] = Resolve{Err: err.Error()}
continue
}
challengeInfo := dns01.GetChallengeInfo(domain, keyAuth)
resolves[domain] = Resolve{
Key: challengeInfo.FQDN,
Value: challengeInfo.Value,
}
}
return resolves, nil
return certChains[0]
}

View File

@@ -1,7 +1,7 @@
package acme
import (
"fmt"
"context"
"testing"
"github.com/stretchr/testify/suite"
@@ -16,24 +16,26 @@ func TestClientTestSuite(t *testing.T) {
}
func (s *ClientTestSuite) TestObtainSSL() {
client, err := NewRegisterClient("ci@haozi.net", "https://acme-staging-v02.api.letsencrypt.org/directory", KeyEC256)
ctx := context.Background()
client, err := NewRegisterAccount(ctx, "ci@haozi.net", CALetsEncryptStaging, nil, KeyEC256)
s.Nil(err)
err = client.UseDns(DnsPod, DNSParam{
ID: "xxx",
Token: "xxx",
client.UseDns(DnsPod, DNSParam{
ID: "123456",
Token: "654321",
})
s.Nil(err)
err = client.UseManualDns(false)
s.Nil(err)
/*client.UseManualDns(2)
resolves, err := client.GetDNSResolve([]string{"haozi.dev"})
resolves, err := client.GetDNSRecords(ctx, []string{"*.haozi.net", "haozi.net"}, KeyEC256)
debug.Dump(resolves)
s.Nil(err)
s.NotNil(resolves)
ssl, err := client.ObtainSSL([]string{"haozi.dev"})
fmt.Println(err.Error())
time.Sleep(2 * time.Minute)
ssl, err := client.ObtainSSLManual()*/
ssl, err := client.ObtainSSL(ctx, []string{"*.haozi.net", "haozi.net"}, KeyEC256)
s.Error(err)
s.NotNil(ssl)
}

View File

@@ -1,19 +0,0 @@
package acme
type Resolve struct {
Key string `json:"key"`
Value string `json:"value"`
Err string `json:"err"`
}
type manualDnsProvider struct {
Resolve *Resolve
}
func (p *manualDnsProvider) Present(domain, token, keyAuth string) error {
return nil
}
func (p *manualDnsProvider) CleanUp(domain, token, keyAuth string) error {
return nil
}

189
pkg/acme/solvers.go Normal file
View File

@@ -0,0 +1,189 @@
package acme
import (
"context"
"fmt"
"os"
"path/filepath"
"time"
"github.com/libdns/alidns"
"github.com/libdns/cloudflare"
"github.com/libdns/dnspod"
"github.com/libdns/libdns"
"github.com/mholt/acmez/acme"
"golang.org/x/net/publicsuffix"
)
type httpSolver struct {
path string
}
func (s httpSolver) Present(ctx context.Context, challenge acme.Challenge) error {
var err error
if s.path == "" {
return nil
}
challengeFilePath := filepath.Join(s.path, challenge.HTTP01ResourcePath())
err = os.MkdirAll(filepath.Dir(challengeFilePath), 0o755)
if err != nil {
return fmt.Errorf("无法在网站目录创建 HTTP 挑战所需的目录: %w", err)
}
err = os.WriteFile(challengeFilePath, []byte(challenge.KeyAuthorization), 0o644)
if err != nil {
return fmt.Errorf("无法在网站目录创建 HTTP 挑战所需的文件: %w", err)
}
return nil
}
// CleanUp cleans up the HTTP server if it is the last one to finish.
func (s httpSolver) CleanUp(_ context.Context, challenge acme.Challenge) error {
if s.path == "" {
return nil
}
err := os.Remove(filepath.Join(s.path, challenge.HTTP01ResourcePath()))
if err != nil {
return fmt.Errorf("无法删除 HTTP 挑战文件: %w", err)
}
return nil
}
type dnsSolver struct {
dns DnsType
param DNSParam
records *[]libdns.Record
}
func (s dnsSolver) Present(ctx context.Context, challenge acme.Challenge) error {
dnsName := challenge.DNS01TXTRecordName()
keyAuth := challenge.DNS01KeyAuthorization()
provider, err := s.getDNSProvider()
if err != nil {
return fmt.Errorf("获取 DNS 提供商失败: %w", err)
}
zone, err := publicsuffix.EffectiveTLDPlusOne(dnsName)
if err != nil {
return fmt.Errorf("获取域名 %q 的顶级域失败: %w", dnsName, err)
}
rec := libdns.Record{
Type: "TXT",
Name: libdns.RelativeName(dnsName+".", zone+"."),
Value: keyAuth,
}
results, err := provider.AppendRecords(ctx, zone+".", []libdns.Record{rec})
if err != nil {
return fmt.Errorf("域名 %q 添加临时记录 %q 失败: %w", zone, dnsName, err)
}
if len(results) != 1 {
return fmt.Errorf("预期添加 1 条记录,但实际添加了 %d 条记录", len(results))
}
s.records = &results
return nil
}
func (s dnsSolver) CleanUp(ctx context.Context, challenge acme.Challenge) error {
dnsName := challenge.DNS01TXTRecordName()
provider, err := s.getDNSProvider()
if err != nil {
return fmt.Errorf("获取 DNS 提供商失败: %w", err)
}
zone, err := publicsuffix.EffectiveTLDPlusOne(dnsName)
if err != nil {
return fmt.Errorf("获取域名 %q 的顶级域失败: %w", dnsName, err)
}
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
defer cancel()
_, err = provider.DeleteRecords(ctx, zone+".", *s.records)
if err != nil {
return fmt.Errorf("域名 %q 删除临时记录 %q 失败: %w", zone, dnsName, err)
}
return nil
}
func (s dnsSolver) getDNSProvider() (DNSProvider, error) {
var dns DNSProvider
switch s.dns {
case DnsPod:
dns = &dnspod.Provider{
APIToken: s.param.ID + "," + s.param.Token,
}
case AliYun:
dns = &alidns.Provider{
AccKeyID: s.param.AccessKey,
AccKeySecret: s.param.SecretKey,
}
case CloudFlare:
dns = &cloudflare.Provider{
APIToken: s.param.APIkey,
}
default:
return nil, fmt.Errorf("未知的 DNS 提供商 %q", s.dns)
}
return dns, nil
}
type DnsType string
const (
DnsPod DnsType = "dnspod"
AliYun DnsType = "aliyun"
CloudFlare DnsType = "cloudflare"
)
type DNSParam struct {
ID string `form:"id" json:"id"`
Token string `form:"token" json:"token"`
AccessKey string `form:"access_key" json:"access_key"`
SecretKey string `form:"secret_key" json:"secret_key"`
APIkey string `form:"api_key" json:"api_key"`
}
type DNSProvider interface {
libdns.RecordAppender
libdns.RecordDeleter
}
type manualDNSSolver struct {
check bool
controlChan chan struct{}
dataChan chan any
records *[]DNSRecord
}
func (s manualDNSSolver) Present(ctx context.Context, challenge acme.Challenge) error {
dnsName := challenge.DNS01TXTRecordName()
keyAuth := challenge.DNS01KeyAuthorization()
// 追加记录到 records 中
*s.records = append(*s.records, DNSRecord{
Key: dnsName,
Value: keyAuth,
})
s.dataChan <- *s.records
// 等待信号以继续
<-s.controlChan
return nil
}
func (s manualDNSSolver) CleanUp(ctx context.Context, challenge acme.Challenge) error {
return nil
}
type DNSRecord struct {
Key string `json:"key"`
Value string `json:"value"`
}