feat: 证书增加 ZeroSSL BuyPass Google 申请 (#2951)

Refs https://github.com/1Panel-dev/1Panel/issues/215
This commit is contained in:
zhengkunwang 2023-11-14 18:44:09 +08:00 committed by GitHub
parent ff6ede9429
commit 6301fede62
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 438 additions and 196 deletions

View file

@ -26,7 +26,10 @@ type WebsiteSSLRenew struct {
}
type WebsiteAcmeAccountCreate struct {
Email string `json:"email" validate:"required"`
Email string `json:"email" validate:"required"`
Type string `json:"type" validate:"required,oneof=letsencrypt zerossl buypass google"`
EabKid string `json:"eabKid"`
EabHmacKey string `json:"eabHmacKey"`
}
type WebsiteDnsAccountCreate struct {

View file

@ -5,6 +5,9 @@ type WebsiteAcmeAccount struct {
Email string `gorm:"type:varchar(256);not null" json:"email"`
URL string `gorm:"type:varchar(256);not null" json:"url"`
PrivateKey string `gorm:"type:longtext;not null" json:"-"`
Type string `gorm:"type:varchar(64);not null;default:letsencrypt" json:"type"`
EabKid string `gorm:"type:varchar(256);" json:"eabKid"`
EabHmacKey string `gorm:"type:varchar(256);" json:"eabHmacKey"`
}
func (w WebsiteAcmeAccount) TableName() string {

View file

@ -40,19 +40,28 @@ func (w WebsiteAcmeAccountService) Create(create request.WebsiteAcmeAccountCreat
return response.WebsiteAcmeAccountDTO{}, buserr.New(constant.ErrEmailIsExist)
}
client, err := ssl.NewAcmeClient(create.Email, "")
if create.Type == "google" && (create.EabKid == "" || create.EabHmacKey == "") {
return response.WebsiteAcmeAccountDTO{}, buserr.New(constant.ErrEabKidOrEabHmacKeyCannotBlank)
} else {
create.EabKid = ""
create.EabHmacKey = ""
}
acmeAccount := &model.WebsiteAcmeAccount{
Email: create.Email,
Type: create.Type,
}
client, err := ssl.NewAcmeClient(acmeAccount)
if err != nil {
return response.WebsiteAcmeAccountDTO{}, err
}
acmeAccount := model.WebsiteAcmeAccount{
Email: create.Email,
URL: client.User.Registration.URI,
PrivateKey: string(ssl.GetPrivateKey(client.User.GetPrivateKey())),
}
if err := websiteAcmeRepo.Create(acmeAccount); err != nil {
acmeAccount.PrivateKey = string(ssl.GetPrivateKey(client.User.GetPrivateKey()))
acmeAccount.URL = client.User.Registration.URI
if err := websiteAcmeRepo.Create(*acmeAccount); err != nil {
return response.WebsiteAcmeAccountDTO{}, err
}
return response.WebsiteAcmeAccountDTO{WebsiteAcmeAccount: acmeAccount}, nil
return response.WebsiteAcmeAccountDTO{WebsiteAcmeAccount: *acmeAccount}, nil
}
func (w WebsiteAcmeAccountService) Delete(id uint) error {

View file

@ -95,7 +95,7 @@ func (w WebsiteSSLService) Create(create request.WebsiteSSLCreate) (request.Webs
if err != nil {
return res, err
}
client, err := ssl.NewPrivateKeyClient(acmeAccount.Email, acmeAccount.PrivateKey)
client, err := ssl.NewAcmeClient(acmeAccount)
if err != nil {
return res, err
}
@ -174,7 +174,7 @@ func (w WebsiteSSLService) Renew(sslId uint) error {
return err
}
client, err := ssl.NewPrivateKeyClient(acmeAccount.Email, acmeAccount.PrivateKey)
client, err := ssl.NewAcmeClient(acmeAccount)
if err != nil {
return err
}
@ -242,7 +242,7 @@ func (w WebsiteSSLService) GetDNSResolve(req request.WebsiteDNSReq) ([]response.
return nil, err
}
client, err := ssl.NewPrivateKeyClient(acmeAccount.Email, acmeAccount.PrivateKey)
client, err := ssl.NewAcmeClient(acmeAccount)
if err != nil {
return nil, err
}

View file

@ -78,10 +78,11 @@ var (
// ssl
var (
ErrSSLCannotDelete = "ErrSSLCannotDelete"
ErrAccountCannotDelete = "ErrAccountCannotDelete"
ErrSSLApply = "ErrSSLApply"
ErrEmailIsExist = "ErrEmailIsExist"
ErrSSLCannotDelete = "ErrSSLCannotDelete"
ErrAccountCannotDelete = "ErrAccountCannotDelete"
ErrSSLApply = "ErrSSLApply"
ErrEmailIsExist = "ErrEmailIsExist"
ErrEabKidOrEabHmacKeyCannotBlank = "ErrEabKidOrEabHmacKeyCannotBlank"
)
// file

View file

@ -91,6 +91,7 @@ ErrSSLKeyNotFound: 'The private key file does not exist'
ErrSSLCertificateNotFound: 'The certificate file does not exist'
ErrSSLKeyFormat: 'Private key file verification error'
ErrSSLCertificateFormat: 'Certificate file format error, please use pem format'
ErrEabKidOrEabHmacKeyCannotBlank: 'EabKid or EabHmacKey cannot be empty'
#mysql
ErrUserIsExist: "The current user already exists. Please enter a new user"

View file

@ -91,6 +91,7 @@ ErrSSLKeyNotFound: '私鑰文件不存在'
ErrSSLCertificateNotFound: '證書文件不存在'
ErrSSLKeyFormat: '私鑰文件校驗錯誤'
ErrSSLCertificateFormat: '證書文件格式錯誤,請使用 pem 格式'
ErrEabKidOrEabHmacKeyCannotBlank: 'EabKid 或 EabHmacKey 不能為空'
#mysql
ErrUserIsExist: "當前用戶已存在,請重新輸入"

View file

@ -91,6 +91,7 @@ ErrSSLKeyNotFound: '私钥文件不存在'
ErrSSLCertificateNotFound: '证书文件不存在'
ErrSSLKeyFormat: '私钥文件校验失败'
ErrSSLCertificateFormat: '证书文件格式错误,请使用 pem 格式'
ErrEabKidOrEabHmacKeyCannotBlank: 'EabKid 或 EabHmacKey 不能为空'
#mysql
ErrUserIsExist: "当前用户已存在,请重新输入"

View file

@ -52,6 +52,8 @@ func Init() {
migrations.AddBindAddress,
migrations.AddCommandGroup,
migrations.AddAppSyncStatus,
migrations.UpdateAcmeAccount,
})
if err := m.Migrate(); err != nil {
global.LOG.Error(err)

View file

@ -0,0 +1,17 @@
package migrations
import (
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/go-gormigrate/gormigrate/v2"
"gorm.io/gorm"
)
var UpdateAcmeAccount = &gormigrate.Migration{
ID: "20231115-update-acme-account",
Migrate: func(tx *gorm.DB) error {
if err := tx.AutoMigrate(&model.WebsiteAcmeAccount{}); err != nil {
return err
}
return nil
},
}

View file

@ -5,10 +5,16 @@ import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/json"
"encoding/pem"
"fmt"
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/go-acme/lego/v4/certcrypto"
"github.com/go-acme/lego/v4/lego"
"github.com/go-acme/lego/v4/registration"
"io"
"net/http"
"net/url"
)
type domainError struct {
@ -16,6 +22,12 @@ type domainError struct {
Error error
}
type zeroSSLRes struct {
Success bool `json:"success"`
EabKid string `json:"eab_kid"`
EabHmacKey string `json:"eab_hmac_key"`
}
func GetPrivateKey(priKey crypto.PrivateKey) []byte {
rsaKey := priKey.(*rsa.PrivateKey)
derStream := x509.MarshalPKCS1PrivateKey(rsaKey)
@ -26,24 +38,64 @@ func GetPrivateKey(priKey crypto.PrivateKey) []byte {
return pem.EncodeToMemory(block)
}
func NewRegisterClient(email string) (*AcmeClient, error) {
priKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
panic(err)
func NewRegisterClient(acmeAccount *model.WebsiteAcmeAccount) (*AcmeClient, error) {
var (
priKey *rsa.PrivateKey
err error
)
if acmeAccount.PrivateKey != "" {
block, _ := pem.Decode([]byte(acmeAccount.PrivateKey))
priKey, err = x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return nil, err
}
} else {
priKey, err = rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return nil, err
}
}
myUser := &AcmeUser{
Email: email,
Email: acmeAccount.Email,
Key: priKey,
}
config := newConfig(myUser)
config := newConfig(myUser, acmeAccount.Type)
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
var reg *registration.Resource
if acmeAccount.Type == "zerossl" || acmeAccount.Type == "google" {
if acmeAccount.Type == "zerossl" {
var res *zeroSSLRes
res, err = getZeroSSLEabCredentials(acmeAccount.Email)
if err != nil {
return nil, err
}
if res.Success {
acmeAccount.EabKid = res.EabKid
acmeAccount.EabHmacKey = res.EabHmacKey
} else {
return nil, fmt.Errorf("get zero ssl eab credentials failed")
}
}
eabOptions := registration.RegisterEABOptions{
TermsOfServiceAgreed: true,
Kid: acmeAccount.EabKid,
HmacEncoded: acmeAccount.EabHmacKey,
}
reg, err = client.Registration.RegisterWithExternalAccountBinding(eabOptions)
if err != nil {
return nil, err
}
} else {
reg, err = client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
if err != nil {
return nil, err
}
}
myUser.Registration = reg
@ -56,41 +108,58 @@ func NewRegisterClient(email string) (*AcmeClient, error) {
return acmeClient, nil
}
func NewPrivateKeyClient(email string, privateKey string) (*AcmeClient, error) {
block, _ := pem.Decode([]byte(privateKey))
priKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return nil, err
}
myUser := &AcmeUser{
Email: email,
Key: priKey,
}
config := newConfig(myUser)
client, err := lego.NewClient(config)
if err != nil {
return nil, err
}
reg, err := client.Registration.ResolveAccountByKey()
if err != nil {
return nil, err
}
myUser.Registration = reg
acmeClient := &AcmeClient{
User: myUser,
Client: client,
Config: config,
}
return acmeClient, nil
}
func newConfig(user *AcmeUser) *lego.Config {
func newConfig(user *AcmeUser, accountType string) *lego.Config {
config := lego.NewConfig(user)
config.CADirURL = "https://acme-v02.api.letsencrypt.org/directory"
//config.CADirURL = "https://acme-staging-v02.api.letsencrypt.org/directory"
config.UserAgent = "acm_go/0.0.1"
switch accountType {
case "letsEncrypt":
config.CADirURL = "https://acme-v02.api.letsencrypt.org/directory"
case "zeroSSL":
config.CADirURL = "https://acme.zerossl.com/v2/DV90"
case "buyPass":
config.CADirURL = "https://api.buypass.com/acme/directory"
case "google":
config.CADirURL = "https://dv.acme-v02.api.pki.goog/directory"
}
config.UserAgent = "1Panel"
config.Certificate.KeyType = certcrypto.RSA2048
return config
}
func getZeroSSLEabCredentials(email string) (*zeroSSLRes, error) {
baseURL := "https://api.zerossl.com/acme/eab-credentials-email"
params := url.Values{}
params.Add("email", email)
requestURL := fmt.Sprintf("%s?%s", baseURL, params.Encode())
req, err := http.NewRequest("POST", requestURL, nil)
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("server returned non-200 status: %d %s", resp.StatusCode, http.StatusText(resp.StatusCode))
}
var result zeroSSLRes
err = json.Unmarshal(body, &result)
if err != nil {
return nil, err
}
return &result, nil
}

View file

@ -2,12 +2,17 @@ package ssl
import (
"bytes"
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/json"
"encoding/pem"
"fmt"
"github.com/go-acme/lego/v4/providers/dns/cloudflare"
"os"
"path"
"testing"
@ -23,6 +28,8 @@ import (
"github.com/go-acme/lego/v4/challenge/dns01"
"github.com/go-acme/lego/v4/lego"
"github.com/go-acme/lego/v4/registration"
"log"
)
type AppList struct {
@ -171,50 +178,11 @@ func TestCreatePrivate(t *testing.T) {
func TestSSL(t *testing.T) {
//// 本地ACME测试用
//httpClient := &http.Client{
// Transport: &http.Transport{
// TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
// }}
//priKey, err := rsa.GenerateKey(rand.Reader, 2048)
//if err != nil {
// panic(err)
//}
//derStream := x509.MarshalPKCS1PrivateKey(priKey)
//block := &pem.Block{
// Type: "privateKey",
// Bytes: derStream,
//}
//file, err := os.Create("private.key")
//if err != nil {
// return
//}
//privateByte := pem.EncodeToMemory(block)
//
//fmt.Println(string(privateByte))
//
//if err = pem.Encode(file, block); err != nil {
// return
//}
key, err := os.ReadFile("private.key")
priKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
panic(err)
log.Fatalf("Failed to generate private key: %v", err)
}
block2, _ := pem.Decode(key)
priKey, err := x509.ParsePKCS1PrivateKey(block2.Bytes)
if err != nil {
panic(err)
}
//block, _ := pem.Decode([]byte("vcCzoue3m0ufCzVx673quKBYQOho4uULpwj_P6tR60Q"))
//priKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
//if err != nil {
// panic(err)
//}
myUser := AcmeUser{
Email: "you2@yours.com",
Key: priKey,
@ -223,6 +191,7 @@ func TestSSL(t *testing.T) {
config := lego.NewConfig(&myUser)
//config.CADirURL = "https://acme-v02.api.letsencrypt.org/directory"
config.CADirURL = "https://acme-staging-v02.api.letsencrypt.org/directory"
config.CADirURL = "https://acme.zerossl.com/v2/DV90"
config.UserAgent = "acm_go/0.0.1"
config.Certificate.KeyType = certcrypto.RSA2048
@ -238,11 +207,6 @@ func TestSSL(t *testing.T) {
panic(err)
}
//reg, err := client.Registration.ResolveAccountByKey()
//if err != nil {
// panic(err)
//}
myUser.Registration = reg
//获取证书
@ -256,35 +220,14 @@ func TestSSL(t *testing.T) {
//}
//申请证书
ewDomain := "tuxpanel.com"
request := certificate.ObtainRequest{
Domains: []string{"1panel.cloud"},
Domains: []string{ewDomain},
// 证书链
Bundle: true,
}
//dns01.NewDNSProviderManual()
//dnsPodConfig := dnspod.NewDefaultConfig()
//dnsPodConfig.LoginToken = "1,1"
//provider, err := dnspod.1(dnsPodConfig)
//if err != nil {
// panic(err)
//}
//
//alidnsConfig := alidns.NewDefaultConfig()
//alidnsConfig.SecretKey = "1"
//alidnsConfig.APIKey = "1"
//p, err := alidns.NewDNSProviderConfig(alidnsConfig)
//if err != nil {
// panic(err)
//}
//p, err := dns01.NewDNSProviderManual()
//if err != nil {
// panic(err)
//}
err = client.Challenge.SetDNS01Provider(&manualDnsProvider{}, dns01.AddDNSTimeout(6*time.Minute))
if err != nil {
panic(err)
@ -294,7 +237,7 @@ func TestSSL(t *testing.T) {
if err != nil {
panic(err)
}
order, err := core.Orders.New([]string{"1panel.cloud"})
order, err := core.Orders.New([]string{ewDomain})
if err != nil {
panic(err)
}
@ -303,7 +246,6 @@ func TestSSL(t *testing.T) {
if err != nil {
panic(err)
}
//core.Challenges.New()
domain := challenge.GetTargetedDomain(auth)
chlng, err := challenge.FindChallenge(challenge.DNS01, auth)
@ -373,3 +315,143 @@ func TestSSL(t *testing.T) {
fmt.Println(cert2)
}
func generateCSR(privateKey crypto.PrivateKey, domain string) ([]byte, error) {
// 创建证书请求的模板
template := x509.CertificateRequest{
Subject: pkix.Name{
CommonName: domain,
},
SignatureAlgorithm: x509.ECDSAWithSHA256,
}
// 生成 CSR
csrBytes, err := x509.CreateCertificateRequest(rand.Reader, &template, privateKey)
if err != nil {
return nil, err
}
// 将 CSR 编码为 PEM 格式
csrPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE REQUEST", Bytes: csrBytes})
// 这里可以将 CSR 写入文件或者返回
err = os.WriteFile("csr.pem", csrPEM, 0644)
if err != nil {
return nil, err
}
return csrPEM, nil
}
func TestZeroSSL(t *testing.T) {
//switch accountType {
//case "letsencrypt":
// config.CADirURL = "https://acme-staging-v02.api.letsencrypt.org/directory"
//case "zerossl":
// config.CADirURL = "https://acme.zerossl.com/v2/DV90"
//case "buypass":
// config.CADirURL = "https://api.test4.buypass.no/acme/directory"
//}
domain := "1panel.store"
acmeServer := "https://acme.zerossl.com/v2/DV90"
//acmeServer = "https://api.test4.buypass.no/acme/directory"
//
//acmeServer = "https://api.buypass.com/acme/directory"
priKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
log.Fatalf("Failed to generate private key: %v", err)
}
user := AcmeUser{
Email: "zhengkunwang123@sina.com",
Key: priKey,
}
config := lego.NewConfig(&user)
// 设置ACME服务器URL
config.CADirURL = acmeServer
config.Certificate.KeyType = certcrypto.RSA2048
config.UserAgent = "acm_go/0.0.1"
// 创建ACME客户端
client, err := lego.NewClient(config)
if err != nil {
log.Fatal(err)
}
// ZeroSSl
kid := "xv1T5Ybi4G2otJlbCTVHjg"
hmacEncoded := "pc-mEroSmg2quRpVr7e4L5YWzhm5mSDlk4_ivMUBzAkDjbym3Y6g7RLhR1joQH-JP94qy3PEczKVfw7QSLoh8A"
eabOptions := registration.RegisterEABOptions{
TermsOfServiceAgreed: true,
Kid: kid,
HmacEncoded: hmacEncoded,
}
reg, err := client.Registration.RegisterWithExternalAccountBinding(eabOptions)
if err != nil {
log.Fatal(err)
}
// ZeroSSl
//reg, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
//if err != nil {
// log.Fatal(err)
//}
user.Registration = reg
cloudflareConfig := cloudflare.NewDefaultConfig()
cloudflareConfig.AuthEmail = "zhengkunwang123@sina.com"
cloudflareConfig.AuthKey = "c29c0d604897ec1c5c7f14746623524bf040a"
p, err := cloudflare.NewDNSProviderConfig(cloudflareConfig)
if err != nil {
log.Fatal(err)
}
if err := client.Challenge.SetDNS01Provider(p, dns01.AddDNSTimeout(3*time.Minute)); err != nil {
log.Fatal(err)
}
// 申请证书
pk, err := certcrypto.GeneratePrivateKey(certcrypto.EC256)
if err != nil {
return
}
request := certificate.ObtainRequest{
Domains: []string{domain},
Bundle: true,
PrivateKey: pk,
}
certificates, err := client.Certificate.Obtain(request)
if err != nil {
log.Fatal(err)
}
// 保存证书
err = os.WriteFile("certificate.crt", certificates.Certificate, 0644)
if err != nil {
log.Fatal(err)
}
err = os.WriteFile("private.key", certificates.PrivateKey, 0644)
if err != nil {
log.Fatal(err)
}
}
func TestGetEABCre(t *testing.T) {
res, err := getZeroSSLEabCredentials("zen@11.com")
if err != nil {
panic(err)
}
fmt.Printf("%v", res)
}

View file

@ -3,6 +3,7 @@ package ssl
import (
"crypto"
"encoding/json"
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/go-acme/lego/v4/acme"
"github.com/go-acme/lego/v4/acme/api"
"github.com/go-acme/lego/v4/certificate"
@ -41,23 +42,15 @@ type AcmeClient struct {
User *AcmeUser
}
func NewAcmeClient(email, privateKey string) (*AcmeClient, error) {
if email == "" {
func NewAcmeClient(acmeAccount *model.WebsiteAcmeAccount) (*AcmeClient, error) {
if acmeAccount.Email == "" {
return nil, errors.New("email can not blank")
}
if privateKey == "" {
client, err := NewRegisterClient(email)
if err != nil {
return nil, err
}
return client, nil
} else {
client, err := NewPrivateKeyClient(email, privateKey)
if err != nil {
return nil, err
}
return client, nil
client, err := NewRegisterClient(acmeAccount)
if err != nil {
return nil, err
}
return client, nil
}
type DnsType string
@ -78,22 +71,25 @@ type DNSParam struct {
}
func (c *AcmeClient) UseDns(dnsType DnsType, params string) error {
var param DNSParam
if err := json.Unmarshal([]byte(params), &param); err != nil {
var (
param DNSParam
p challenge.Provider
err error
)
if err = json.Unmarshal([]byte(params), &param); err != nil {
return err
}
var p challenge.Provider
var err error
if dnsType == DnsPod {
switch dnsType {
case DnsPod:
dnsPodConfig := dnspod.NewDefaultConfig()
dnsPodConfig.LoginToken = param.ID + "," + param.Token
p, err = dnspod.NewDNSProviderConfig(dnsPodConfig)
if err != nil {
return err
}
}
if dnsType == AliYun {
case AliYun:
alidnsConfig := alidns.NewDefaultConfig()
alidnsConfig.SecretKey = param.SecretKey
alidnsConfig.APIKey = param.AccessKey
@ -101,8 +97,7 @@ func (c *AcmeClient) UseDns(dnsType DnsType, params string) error {
if err != nil {
return err
}
}
if dnsType == CloudFlare {
case CloudFlare:
cloudflareConfig := cloudflare.NewDefaultConfig()
cloudflareConfig.AuthEmail = param.Email
cloudflareConfig.AuthKey = param.APIkey

View file

@ -85,7 +85,7 @@ export const SearchAcmeAccount = (req: ReqPage) => {
};
export const CreateAcmeAccount = (req: Website.AcmeAccountCreate) => {
return http.post<Website.AcmeAccount>(`/websites/acme`, req);
return http.post<Website.AcmeAccount>(`/websites/acme`, req, TimeoutEnum.T_10M);
};
export const DeleteAcmeAccount = (req: Website.DelReq) => {

View file

@ -122,3 +122,10 @@ export const Units = [
{ label: i18n.global.t('commons.units.month'), value: 'M' },
{ label: i18n.global.t('commons.units.year'), value: 'y' },
];
export const AcmeAccountTypes = [
{ label: "Let's Encrypt", value: 'letsencrypt' },
{ label: 'ZeroSSL', value: 'zerossl' },
{ label: 'Buypass', value: 'buypass' },
{ label: 'Google Cloud', value: 'google' },
];

View file

@ -1664,6 +1664,7 @@ const message = {
openrestryHelper:
'OpenResty default HTTP port: {0} HTTPS port: {1}, which may affect website domain name access and HTTPS forced redirect',
primaryDomainHelper: 'Support domain name: port',
acmeAccountType: 'Account Type',
},
php: {
short_open_tag: 'Short tag support',

View file

@ -1577,6 +1577,7 @@ const message = {
runDirHelper2: '請確保二級運行目錄位於 index 目錄下',
openrestryHelper: 'OpenResty 默認 HTTP 端口{0} HTTPS 端口{1}可能影響網站域名訪問和 HTTPS 強制跳轉',
primaryDomainHelper: '支援網域:port',
acmeAccountType: '賬號類型',
},
php: {
short_open_tag: '短標簽支持',

View file

@ -1577,6 +1577,7 @@ const message = {
runDirHelper2: '请确保二级运行目录位于 index 目录下',
openrestryHelper: 'OpenResty 默认 HTTP 端口{0} HTTPS 端口 {1}可能影响网站域名访问和 HTTPS 强制跳转',
primaryDomainHelper: '支持域名:端口',
acmeAccountType: '账号类型',
},
php: {
short_open_tag: '短标签支持',

View file

@ -12,6 +12,24 @@
<el-form-item :label="$t('website.email')" prop="email">
<el-input v-model.trim="account.email"></el-input>
</el-form-item>
<el-form-item :label="$t('website.acmeAccountType')" prop="type">
<el-select v-model="account.type">
<el-option
v-for="(acme, index) in AcmeAccountTypes"
:key="index"
:label="acme.label"
:value="acme.value"
></el-option>
</el-select>
</el-form-item>
<div v-if="account.type == 'google'">
<el-form-item label="EAB kid" prop="eabKid">
<el-input v-model.trim="account.eabKid"></el-input>
</el-form-item>
<el-form-item label="EAB HmacKey" prop="eabHmacKey">
<el-input v-model.trim="account.eabHmacKey"></el-input>
</el-form-item>
</div>
</el-form>
</el-col>
</el-row>
@ -32,17 +50,26 @@ import { Rules } from '@/global/form-rules';
import { CreateAcmeAccount } from '@/api/modules/website';
import i18n from '@/lang';
import { MsgSuccess } from '@/utils/message';
import { AcmeAccountTypes } from '@/global/mimetype';
let open = ref();
let loading = ref(false);
let accountForm = ref<FormInstance>();
let rules = ref({
const open = ref();
const loading = ref(false);
const accountForm = ref<FormInstance>();
const rules = ref({
email: [Rules.requiredInput, Rules.email],
});
let account = ref({
email: '',
type: [Rules.requiredSelect],
eabKid: [Rules.requiredInput],
eabHmacKey: [Rules.requiredInput],
});
const initData = () => ({
email: '',
type: 'letsencrypt',
eabKid: '',
eabHmacKey: '',
});
const account = ref(initData());
const em = defineEmits(['close']);
const handleClose = () => {
@ -53,9 +80,7 @@ const handleClose = () => {
const resetForm = () => {
accountForm.value.resetFields();
account.value = {
email: '',
};
account.value = initData();
};
const acceptParams = () => {

View file

@ -3,12 +3,25 @@
<template #header>
<DrawerHeader :header="$t('website.acmeAccountManage')" :back="handleClose" />
</template>
<el-alert :title="$t('ssl.acmeHelper')" type="info" :closable="false" style="margin-bottom: 5px" />
<div class="mb-1.5">
<el-alert :title="$t('ssl.acmeHelper')" type="info" :closable="false" />
</div>
<ComplexTable :data="data" :pagination-config="paginationConfig" @search="search()" v-loading="loading">
<template #toolbar>
<el-button type="primary" @click="openCreate">{{ $t('website.addAccount') }}</el-button>
</template>
<el-table-column :label="$t('website.email')" fix show-overflow-tooltip prop="email"></el-table-column>
<el-table-column
:label="$t('website.email')"
fix
show-overflow-tooltip
prop="email"
min-width="100px"
></el-table-column>
<el-table-column :label="$t('website.acmeAccountType')" fix show-overflow-tooltip prop="type">
<template #default="{ row }">
{{ getAccountType(row.type) }}
</template>
</el-table-column>
<el-table-column label="URL" show-overflow-tooltip prop="url" min-width="300px"></el-table-column>
<fu-table-operations
:ellipsis="1"
@ -32,11 +45,12 @@ import { DeleteAcmeAccount, SearchAcmeAccount } from '@/api/modules/website';
import i18n from '@/lang';
import { reactive, ref } from 'vue';
import Create from './create/index.vue';
import { AcmeAccountTypes } from '@/global/mimetype';
let open = ref(false);
let loading = ref(false);
let data = ref();
let createRef = ref();
const open = ref(false);
const loading = ref(false);
const data = ref();
const createRef = ref();
const paginationConfig = reactive({
cacheSizeKey: 'acme-account-page-size',
currentPage: 1,
@ -91,6 +105,14 @@ const deleteAccount = async (row: any) => {
});
};
const getAccountType = (type: string) => {
for (const i of AcmeAccountTypes) {
if (i.value === type) {
return i.label;
}
}
};
defineExpose({
acceptParams,
});

View file

@ -104,8 +104,8 @@ import { Website } from '@/api/interface/website';
import { MsgSuccess } from '@/utils/message';
import { GlobalStore } from '@/store';
import SSLUpload from './upload/index.vue';
const globalStore = GlobalStore();
const globalStore = GlobalStore();
const paginationConfig = reactive({
cacheSizeKey: 'ssl-page-size',
currentPage: 1,

15
go.mod
View file

@ -16,12 +16,12 @@ require (
github.com/gin-contrib/i18n v0.0.1
github.com/gin-gonic/gin v1.9.1
github.com/glebarez/sqlite v1.8.0
github.com/go-acme/lego/v4 v4.9.0
github.com/go-acme/lego/v4 v4.14.2
github.com/go-gormigrate/gormigrate/v2 v2.0.2
github.com/go-playground/validator/v10 v10.14.0
github.com/go-sql-driver/mysql v1.7.0
github.com/goh-chunlin/go-onedrive v1.1.1
github.com/golang-jwt/jwt/v4 v4.4.2
github.com/golang-jwt/jwt/v4 v4.5.0
github.com/google/uuid v1.3.1
github.com/gorilla/websocket v1.5.0
github.com/jinzhu/copier v0.3.5
@ -73,11 +73,11 @@ require (
github.com/bodgit/sevenzip v1.3.0 // indirect
github.com/bodgit/windows v1.0.0 // indirect
github.com/bytedance/sonic v1.9.1 // indirect
github.com/cenkalti/backoff/v4 v4.2.0 // indirect
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28 // indirect
github.com/cloudflare/cloudflare-go v0.49.0 // indirect
github.com/cloudflare/cloudflare-go v0.70.0 // indirect
github.com/connesc/cipherio v0.2.1 // indirect
github.com/containerd/console v1.0.3 // indirect
github.com/containerd/containerd v1.7.7 // indirect
@ -101,6 +101,7 @@ require (
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/glebarez/go-sqlite v1.21.1 // indirect
github.com/go-jose/go-jose/v3 v3.0.0 // indirect
github.com/go-logr/logr v1.2.4 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
@ -131,7 +132,7 @@ require (
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.2 // indirect
github.com/hashicorp/go-retryablehttp v0.7.4 // indirect
github.com/hashicorp/go-version v1.6.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/imdario/mergo v0.3.16 // indirect
@ -152,7 +153,7 @@ require (
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/mattn/go-shellwords v1.0.12 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/miekg/dns v1.1.50 // indirect
github.com/miekg/dns v1.1.55 // indirect
github.com/miekg/pkcs11 v1.1.1 // indirect
github.com/minio/md5-simd v1.1.2 // indirect
github.com/minio/sha256-simd v1.0.0 // indirect
@ -221,7 +222,7 @@ require (
golang.org/x/sync v0.4.0 // indirect
golang.org/x/term v0.13.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/tools v0.7.0 // indirect
golang.org/x/tools v0.10.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d // indirect

36
go.sum
View file

@ -52,7 +52,7 @@ github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935
github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0=
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
@ -102,8 +102,8 @@ github.com/bugsnag/panicwrap v1.2.0 h1:OzrKrRvXis8qEvOkfcxNcYbOd2O7xXS2nnKMEMABF
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
github.com/cenkalti/backoff/v4 v4.2.0 h1:HN5dHm3WBOgndBH6E8V0q2jIYIR3s9yglV8k/+MN3u4=
github.com/cenkalti/backoff/v4 v4.2.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
@ -120,8 +120,8 @@ github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28/go.mod h1:BVjHeAH+
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA=
github.com/cloudflare/cfssl v0.0.0-20181213083726-b94e044bb51e h1:Qux+lbuMaRzkQyTdzgtz8MgzPtzmaPQy6DXmxpdxT3U=
github.com/cloudflare/cloudflare-go v0.49.0 h1:KqJYk/YQ5ZhmyYz1oa4kGDskfF1gVuZfqesaJ/XDLto=
github.com/cloudflare/cloudflare-go v0.49.0/go.mod h1:h0QgcIZ3qEXwFiwfBO8sQxjVdYsLX+PfD7NFEnANaKg=
github.com/cloudflare/cloudflare-go v0.70.0 h1:4opGbUygM8DjirUuaz23jn3akuAcnOCEx+0nQtQEcFo=
github.com/cloudflare/cloudflare-go v0.70.0/go.mod h1:VW6GuazkaZ4xEDkFt24lkXQUsE8q7BiGqDniC2s8WEM=
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=
@ -238,13 +238,15 @@ github.com/glebarez/go-sqlite v1.21.1 h1:7MZyUPh2XTrHS7xNEHQbrhfMZuPSzhkm2A1qgg0
github.com/glebarez/go-sqlite v1.21.1/go.mod h1:ISs8MF6yk5cL4n/43rSOmVMGJJjHYr7L2MbZZ5Q4E2E=
github.com/glebarez/sqlite v1.8.0 h1:02X12E2I/4C1n+v90yTqrjRa8yuo7c3KeHI3FRznCvc=
github.com/glebarez/sqlite v1.8.0/go.mod h1:bpET16h1za2KOOMb8+jCp6UBP/iahDpfPQqSaYLTLx8=
github.com/go-acme/lego/v4 v4.9.0 h1:8Hjj44IqRS7cigshMyFQ+0pIZvwgkG/+9A0UnNh7G8A=
github.com/go-acme/lego/v4 v4.9.0/go.mod h1:g3JRUyWS3L/VObpp4bCxzJftKyf/Wba8QrSSnoiqjg4=
github.com/go-acme/lego/v4 v4.14.2 h1:/D/jqRgLi8Cbk33sLGtu2pX2jEg3bGJWHyV8kFuUHGM=
github.com/go-acme/lego/v4 v4.14.2/go.mod h1:kBXxbeTg0x9AgaOYjPSwIeJy3Y33zTz+tMD16O4MO6c=
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-gormigrate/gormigrate/v2 v2.0.2 h1:YV4Lc5yMQX8ahVW0ENPq6sPhrhdkGukc6fPRYmZ1R6Y=
github.com/go-gormigrate/gormigrate/v2 v2.0.2/go.mod h1:vld36QpBTfTzLealsHsmQQJK5lSwJt6wiORv+oFX8/I=
github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo=
github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
@ -305,8 +307,8 @@ github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69
github.com/goh-chunlin/go-onedrive v1.1.1 h1:HGtHk5iG0MZ92zYUtaY04czfZPBIJUr12UuFc+PW8m4=
github.com/goh-chunlin/go-onedrive v1.1.1/go.mod h1:N8qIGHD7tryO734epiBKk5oXcpGwxKET/u3LuBHciTs=
github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
github.com/golang-jwt/jwt/v4 v4.4.2 h1:rcc4lwaZgFMCZ5jxF9ABolDcIHdBytAFgqFPbSJQAYs=
github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang-sql/sqlexp v0.0.0-20170517235910-f1bb20e5a188 h1:+eHOFJl1BaXrQxKX+T06f78590z4qA2ZzBTqahsKSE4=
@ -428,8 +430,8 @@ github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrj
github.com/hashicorp/go-hclog v1.2.0 h1:La19f8d7WIlm4ogzNHB0JGqs5AUDAZ2UfCY4sJXcJdM=
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.2 h1:AcYqCvkpalPnPF2pn0KamgwamS42TqUDDYFRKq/RAd0=
github.com/hashicorp/go-retryablehttp v0.7.2/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
github.com/hashicorp/go-retryablehttp v0.7.4 h1:ZQgVdpTdAL7WpMIwLzCfbalOcSUdkDZnpUv3/+BxzFA=
github.com/hashicorp/go-retryablehttp v0.7.4/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
@ -542,8 +544,8 @@ github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zk
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/mholt/archiver/v4 v4.0.0-alpha.8 h1:tRGQuDVPh66WCOelqe6LIGh0gwmfwxUrSSDunscGsRM=
github.com/mholt/archiver/v4 v4.0.0-alpha.8/go.mod h1:5f7FUYGXdJWUjESffJaYR4R60VhnHxb2X3T1teMyv5A=
github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA=
github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
github.com/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo=
github.com/miekg/dns v1.1.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
github.com/miekg/pkcs11 v1.0.2/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU=
github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
@ -797,7 +799,6 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
@ -840,6 +841,7 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
@ -969,7 +971,6 @@ golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -1060,12 +1061,11 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4=
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
golang.org/x/tools v0.10.0 h1:tvDr/iQoUqNdohiYm0LmmKcBk+q86lb9EprIUFhHHGg=
golang.org/x/tools v0.10.0/go.mod h1:UJwyiVBsOA2uwvK/e5OY3GTpDUJriEd+/YlqAwLPmyM=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=