From f77681b9c9bc18159e15a1589118d34a0c1e6767 Mon Sep 17 00:00:00 2001 From: endymx <345793738@qq.com> Date: Fri, 13 Sep 2024 10:53:06 +0800 Subject: [PATCH] =?UTF-8?q?feat(ssl=E8=AF=81=E4=B9=A6):=20=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0=E5=8D=8E=E4=B8=BA=E4=BA=91dns=20(#6407)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/utils/ssl/client.go | 12 + backend/utils/ssl/huaweicloud/huaweicloud.go | 288 ++++++++++++++++++ .../utils/ssl/huaweicloud/huaweicloud.toml | 29 ++ .../utils/ssl/huaweicloud/huaweicloud_test.go | 183 +++++++++++ go.mod | 3 + go.sum | 21 ++ 6 files changed, 536 insertions(+) create mode 100644 backend/utils/ssl/huaweicloud/huaweicloud.go create mode 100644 backend/utils/ssl/huaweicloud/huaweicloud.toml create mode 100644 backend/utils/ssl/huaweicloud/huaweicloud_test.go diff --git a/backend/utils/ssl/client.go b/backend/utils/ssl/client.go index 253af0e22..a1e21c281 100644 --- a/backend/utils/ssl/client.go +++ b/backend/utils/ssl/client.go @@ -3,6 +3,7 @@ package ssl import ( "crypto" "encoding/json" + "github.com/1Panel-dev/1Panel/backend/utils/ssl/huaweicloud" "os" "strings" "time" @@ -72,6 +73,7 @@ const ( NameCom DnsType = "NameCom" Godaddy DnsType = "Godaddy" TencentCloud DnsType = "TencentCloud" + HuaweiCloud DnsType = "HuaweiCloud" ) type DNSParam struct { @@ -84,6 +86,7 @@ type DNSParam struct { APIUser string `json:"apiUser"` APISecret string `json:"apiSecret"` SecretID string `json:"secretID"` + Region string `json:"region"` } var ( @@ -166,6 +169,15 @@ func (c *AcmeClient) UseDns(dnsType DnsType, params string, websiteSSL model.Web tencentCloudConfig.PollingInterval = pollingInterval tencentCloudConfig.TTL = ttl p, err = tencentcloud.NewDNSProviderConfig(tencentCloudConfig) + case HuaweiCloud: + huaweiCloudConfig := huaweicloud.NewDefaultConfig() + huaweiCloudConfig.AccessKeyID = param.AccessKey + huaweiCloudConfig.SecretAccessKey = param.SecretKey + huaweiCloudConfig.Region = param.Region + huaweiCloudConfig.PropagationTimeout = propagationTimeout + huaweiCloudConfig.PollingInterval = pollingInterval + huaweiCloudConfig.TTL = int32(ttl) + p, err = huaweicloud.NewDNSProviderConfig(huaweiCloudConfig) } if err != nil { return err diff --git a/backend/utils/ssl/huaweicloud/huaweicloud.go b/backend/utils/ssl/huaweicloud/huaweicloud.go new file mode 100644 index 000000000..7f32f76d6 --- /dev/null +++ b/backend/utils/ssl/huaweicloud/huaweicloud.go @@ -0,0 +1,288 @@ +// Package huaweicloud implements a DNS provider for solving the DNS-01 challenge using Huawei Cloud. +package huaweicloud + +import ( + "errors" + "fmt" + "strconv" + "strings" + "sync" + "time" + + "github.com/go-acme/lego/v4/challenge/dns01" + "github.com/go-acme/lego/v4/platform/config/env" + "github.com/go-acme/lego/v4/platform/wait" + hwauthbasic "github.com/huaweicloud/huaweicloud-sdk-go-v3/core/auth/basic" + hwconfig "github.com/huaweicloud/huaweicloud-sdk-go-v3/core/config" + hwdns "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/dns/v2" + hwmodel "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/dns/v2/model" + hwregion "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/dns/v2/region" +) + +// Environment variables names. +const ( + envNamespace = "HUAWEICLOUD_" + + EnvAccessKeyID = envNamespace + "ACCESS_KEY_ID" + EnvSecretAccessKey = envNamespace + "SECRET_ACCESS_KEY" + EnvRegion = envNamespace + "REGION" + + EnvTTL = envNamespace + "TTL" + EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT" + EnvPollingInterval = envNamespace + "POLLING_INTERVAL" + EnvHTTPTimeout = envNamespace + "HTTP_TIMEOUT" +) + +// Config is used to configure the creation of the DNSProvider. +type Config struct { + AccessKeyID string + SecretAccessKey string + Region string + + PropagationTimeout time.Duration + PollingInterval time.Duration + TTL int32 + HTTPTimeout time.Duration +} + +// NewDefaultConfig returns a default configuration for the DNSProvider. +func NewDefaultConfig() *Config { + return &Config{ + TTL: int32(env.GetOrDefaultInt(EnvTTL, 300)), + PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, dns01.DefaultPropagationTimeout), + PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, dns01.DefaultPollingInterval), + HTTPTimeout: env.GetOrDefaultSecond(EnvHTTPTimeout, 30*time.Second), + } +} + +// DNSProvider implements the challenge.Provider interface. +type DNSProvider struct { + config *Config + client *hwdns.DnsClient + + recordIDs map[string]string + recordIDsMu sync.Mutex +} + +// NewDNSProvider returns a DNSProvider instance configured for Huawei Cloud. +// Credentials must be passed in the environment variables: +// HUAWEICLOUD_ACCESS_KEY_ID, HUAWEICLOUD_SECRET_ACCESS_KEY, and HUAWEICLOUD_REGION. +func NewDNSProvider() (*DNSProvider, error) { + values, err := env.Get(EnvAccessKeyID, EnvSecretAccessKey, EnvRegion) + if err != nil { + return nil, fmt.Errorf("huaweicloud: %w", err) + } + + config := NewDefaultConfig() + config.AccessKeyID = values[EnvAccessKeyID] + config.SecretAccessKey = values[EnvSecretAccessKey] + config.Region = values[EnvRegion] + + return NewDNSProviderConfig(config) +} + +// NewDNSProviderConfig return a DNSProvider instance configured for Huawei Cloud. +func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { + if config == nil { + return nil, errors.New("huaweicloud: the configuration of the DNS provider is nil") + } + + if config.AccessKeyID == "" || config.SecretAccessKey == "" || config.Region == "" { + return nil, errors.New("huaweicloud: credentials missing") + } + + auth, err := hwauthbasic.NewCredentialsBuilder(). + WithAk(config.AccessKeyID). + WithSk(config.SecretAccessKey). + SafeBuild() + if err != nil { + return nil, fmt.Errorf("huaweicloud: crendential build: %w", err) + } + + region, err := hwregion.SafeValueOf(config.Region) + if err != nil { + return nil, fmt.Errorf("huaweicloud: safe region: %w", err) + } + + client, err := hwdns.DnsClientBuilder(). + WithHttpConfig(hwconfig.DefaultHttpConfig().WithTimeout(config.HTTPTimeout)). + WithRegion(region). + WithCredential(auth). + SafeBuild() + if err != nil { + return nil, fmt.Errorf("huaweicloud: client build: %w", err) + } + + return &DNSProvider{ + config: config, + client: hwdns.NewDnsClient(client), + recordIDs: map[string]string{}, + }, nil +} + +// Present creates a TXT record using the specified parameters. +func (d *DNSProvider) Present(domain, token, keyAuth string) error { + info := dns01.GetChallengeInfo(domain, keyAuth) + + authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN) + if err != nil { + return fmt.Errorf("huaweicloud: could not find zone for domain %q: %w", domain, err) + } + + zoneID, err := d.getZoneID(authZone) + if err != nil { + return fmt.Errorf("huaweicloud: %w", err) + } + + recordSetID, err := d.getOrCreateRecordSetID(domain, zoneID, info) + if err != nil { + return fmt.Errorf("huaweicloud: %w", err) + } + + d.recordIDsMu.Lock() + d.recordIDs[token] = recordSetID + d.recordIDsMu.Unlock() + + err = wait.For("record set sync on "+domain, d.config.PropagationTimeout, d.config.PollingInterval, func() (bool, error) { + rs, errShow := d.client.ShowRecordSet(&hwmodel.ShowRecordSetRequest{ + ZoneId: zoneID, + RecordsetId: recordSetID, + }) + if errShow != nil { + return false, fmt.Errorf("show record set: %w", errShow) + } + + return !strings.HasSuffix(deref(rs.Status), "PENDING_"), nil + }) + if err != nil { + return fmt.Errorf("huaweicloud: %w", err) + } + + return nil +} + +// CleanUp removes the TXT record matching the specified parameters. +func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { + info := dns01.GetChallengeInfo(domain, keyAuth) + + // gets the record's unique ID from when we created it + d.recordIDsMu.Lock() + recordID, ok := d.recordIDs[token] + d.recordIDsMu.Unlock() + if !ok { + return fmt.Errorf("huaweicloud: unknown record ID for '%s' '%s'", info.EffectiveFQDN, token) + } + + authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN) + if err != nil { + return fmt.Errorf("huaweicloud: could not find zone for domain %q: %w", domain, err) + } + + zoneID, err := d.getZoneID(authZone) + if err != nil { + return fmt.Errorf("huaweicloud: %w", err) + } + + request := &hwmodel.DeleteRecordSetRequest{ + ZoneId: zoneID, + RecordsetId: recordID, + } + + _, err = d.client.DeleteRecordSet(request) + if err != nil { + return fmt.Errorf("huaweicloud: delete record: %w", err) + } + + return nil +} + +// Timeout returns the timeout and interval to use when checking for DNS propagation. +// Adjusting here to cope with spikes in propagation times. +func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { + return d.config.PropagationTimeout, d.config.PollingInterval +} + +func (d *DNSProvider) getOrCreateRecordSetID(domain, zoneID string, info dns01.ChallengeInfo) (string, error) { + records, err := d.client.ListRecordSetsByZone(&hwmodel.ListRecordSetsByZoneRequest{ + ZoneId: zoneID, + Name: pointer(info.EffectiveFQDN), + }) + if err != nil { + return "", fmt.Errorf("record list: unable to get record %s for zone %s: %w", info.EffectiveFQDN, domain, err) + } + + var existingRecordSet *hwmodel.ListRecordSets + + for _, record := range deref(records.Recordsets) { + if deref(record.Type) == "TXT" && deref(record.Name) == info.EffectiveFQDN { + existingRecordSet = &record + } + } + + value := strconv.Quote(info.Value) + + if existingRecordSet == nil { + request := &hwmodel.CreateRecordSetRequest{ + ZoneId: zoneID, + Body: &hwmodel.CreateRecordSetRequestBody{ + Name: info.EffectiveFQDN, + Description: pointer("Added TXT record for ACME dns-01 challenge using lego client"), + Type: "TXT", + Ttl: pointer(d.config.TTL), + Records: []string{value}, + }, + } + + resp, errCreate := d.client.CreateRecordSet(request) + if errCreate != nil { + return "", fmt.Errorf("create record set: %w", errCreate) + } + + return deref(resp.Id), nil + } + + updateRequest := &hwmodel.UpdateRecordSetRequest{ + ZoneId: zoneID, + RecordsetId: deref(existingRecordSet.Id), + Body: &hwmodel.UpdateRecordSetReq{ + Name: existingRecordSet.Name, + Description: existingRecordSet.Description, + Type: existingRecordSet.Type, + Ttl: existingRecordSet.Ttl, + Records: pointer(append(deref(existingRecordSet.Records), value)), + }, + } + + resp, err := d.client.UpdateRecordSet(updateRequest) + if err != nil { + return "", fmt.Errorf("update record set: %w", err) + } + + return deref(resp.Id), nil +} + +func (d *DNSProvider) getZoneID(authZone string) (string, error) { + zones, err := d.client.ListPublicZones(&hwmodel.ListPublicZonesRequest{}) + if err != nil { + return "", fmt.Errorf("unable to get zone: %w", err) + } + + for _, zone := range deref(zones.Zones) { + if deref(zone.Name) == authZone { + return deref(zone.Id), nil + } + } + + return "", fmt.Errorf("zone %q not found", authZone) +} + +func pointer[T any](v T) *T { return &v } + +func deref[T any](v *T) T { + if v == nil { + var zero T + return zero + } + + return *v +} diff --git a/backend/utils/ssl/huaweicloud/huaweicloud.toml b/backend/utils/ssl/huaweicloud/huaweicloud.toml new file mode 100644 index 000000000..235416551 --- /dev/null +++ b/backend/utils/ssl/huaweicloud/huaweicloud.toml @@ -0,0 +1,29 @@ +Name = "Huawei Cloud" +Description = '''''' +URL = "https://huaweicloud.com" +Code = "huaweicloud" +Since = "v4.19" + +Example = ''' +HUAWEICLOUD_ACCESS_KEY_ID=your-access-key-id \ +HUAWEICLOUD_SECRET_ACCESS_KEY=your-secret-access-key \ +HUAWEICLOUD_REGION=cn-south-1 \ +lego --email you@example.com --dns huaweicloud --domains my.example.org run +''' + +[Configuration] + [Configuration.Credentials] + HUAWEICLOUD_ACCESS_KEY_ID = "Access key ID" + HUAWEICLOUD_SECRET_ACCESS_KEY = "Access Key secret" + HUAWEICLOUD_REGION = "Region" + + [Configuration.Additional] + HUAWEICLOUD_POLLING_INTERVAL = "Time between DNS propagation check" + HUAWEICLOUD_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation" + HUAWEICLOUD_TTL = "The TTL of the TXT record used for the DNS challenge" + HUAWEICLOUD_HTTP_TIMEOUT = "API request timeout" + +[Links] + API = "https://console-intl.huaweicloud.com/apiexplorer/#/openapi/DNS/doc?locale=en-us" + CN_API = "https://support.huaweicloud.com/api-dns/zh-cn_topic_0132421999.html" + GoClient = "https://github.com/huaweicloud/huaweicloud-sdk-go-v3" diff --git a/backend/utils/ssl/huaweicloud/huaweicloud_test.go b/backend/utils/ssl/huaweicloud/huaweicloud_test.go new file mode 100644 index 000000000..02ba1576d --- /dev/null +++ b/backend/utils/ssl/huaweicloud/huaweicloud_test.go @@ -0,0 +1,183 @@ +package huaweicloud + +import ( + "testing" + "time" + + "github.com/go-acme/lego/v4/platform/tester" + hwregion "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/dns/v2/region" + "github.com/stretchr/testify/require" +) + +const envDomain = envNamespace + "DOMAIN" + +var envTest = tester.NewEnvTest(EnvAccessKeyID, EnvSecretAccessKey, EnvRegion). + WithDomain(envDomain) + +func TestNewDNSProvider(t *testing.T) { + testCases := []struct { + desc string + envVars map[string]string + expected string + }{ + { + desc: "success", + envVars: map[string]string{ + EnvAccessKeyID: "123", + EnvSecretAccessKey: "456", + EnvRegion: hwregion.CN_EAST_2.Id, + }, + // The "success" cannot be tested because there is an API call that require a valid authentication. + // Also, there is a bug during the error message creation: + // https://github.com/huaweicloud/huaweicloud-sdk-go-v3/pull/81 + expected: "huaweicloud: client build: runtime error: invalid memory address or nil pointer dereference", + }, + { + desc: "missing credentials", + envVars: map[string]string{ + EnvAccessKeyID: "", + EnvSecretAccessKey: "", + EnvRegion: "", + }, + expected: "huaweicloud: some credentials information are missing: HUAWEICLOUD_ACCESS_KEY_ID,HUAWEICLOUD_SECRET_ACCESS_KEY,HUAWEICLOUD_REGION", + }, + { + desc: "missing access id", + envVars: map[string]string{ + EnvAccessKeyID: "", + EnvSecretAccessKey: "456", + EnvRegion: hwregion.CN_EAST_2.Id, + }, + expected: "huaweicloud: some credentials information are missing: HUAWEICLOUD_ACCESS_KEY_ID", + }, + { + desc: "missing secret key", + envVars: map[string]string{ + EnvAccessKeyID: "123", + EnvSecretAccessKey: "", + EnvRegion: hwregion.CN_EAST_2.Id, + }, + expected: "huaweicloud: some credentials information are missing: HUAWEICLOUD_SECRET_ACCESS_KEY", + }, + { + desc: "missing secret key", + envVars: map[string]string{ + EnvAccessKeyID: "123", + EnvSecretAccessKey: "456", + EnvRegion: "", + }, + expected: "huaweicloud: some credentials information are missing: HUAWEICLOUD_REGION", + }, + } + + for _, test := range testCases { + t.Run(test.desc, func(t *testing.T) { + defer envTest.RestoreEnv() + envTest.ClearEnv() + + envTest.Apply(test.envVars) + + p, err := NewDNSProvider() + + if test.expected == "" { + require.NoError(t, err) + require.NotNil(t, p) + require.NotNil(t, p.config) + require.NotNil(t, p.client) + } else { + require.EqualError(t, err, test.expected) + } + }) + } +} + +func TestNewDNSProviderConfig(t *testing.T) { + testCases := []struct { + desc string + accessKeyID string + secretAccessKey string + region string + expected string + }{ + { + desc: "success", + accessKeyID: "123", + secretAccessKey: "456", + region: hwregion.CN_EAST_2.Id, + // The "success" cannot be tested because there is an API call that require a valid authentication. + // Also, there is a bug during the error message creation: + // https://github.com/huaweicloud/huaweicloud-sdk-go-v3/pull/81 + expected: "huaweicloud: client build: runtime error: invalid memory address or nil pointer dereference", + }, + { + desc: "missing credentials", + expected: "huaweicloud: credentials missing", + }, + { + desc: "missing secret id", + secretAccessKey: "456", + region: hwregion.CN_EAST_2.Id, + expected: "huaweicloud: credentials missing", + }, + { + desc: "missing secret key", + accessKeyID: "123", + region: hwregion.CN_EAST_2.Id, + expected: "huaweicloud: credentials missing", + }, + { + desc: "missing region", + accessKeyID: "123", + secretAccessKey: "456", + expected: "huaweicloud: credentials missing", + }, + } + + for _, test := range testCases { + t.Run(test.desc, func(t *testing.T) { + config := NewDefaultConfig() + config.AccessKeyID = test.accessKeyID + config.SecretAccessKey = test.secretAccessKey + config.Region = test.region + + p, err := NewDNSProviderConfig(config) + + if test.expected == "" { + require.NoError(t, err) + require.NotNil(t, p) + require.NotNil(t, p.config) + require.NotNil(t, p.client) + } else { + require.EqualError(t, err, test.expected) + } + }) + } +} + +func TestLivePresent(t *testing.T) { + if !envTest.IsLiveTest() { + t.Skip("skipping live test") + } + + envTest.RestoreEnv() + provider, err := NewDNSProvider() + require.NoError(t, err) + + err = provider.Present(envTest.GetDomain(), "", "123d==") + require.NoError(t, err) +} + +func TestLiveCleanUp(t *testing.T) { + if !envTest.IsLiveTest() { + t.Skip("skipping live test") + } + + envTest.RestoreEnv() + provider, err := NewDNSProvider() + require.NoError(t, err) + + time.Sleep(1 * time.Second) + + err = provider.CleanUp(envTest.GetDomain(), "", "123d==") + require.NoError(t, err) +} diff --git a/go.mod b/go.mod index f9739a829..43aa53792 100644 --- a/go.mod +++ b/go.mod @@ -24,6 +24,7 @@ require ( github.com/golang-jwt/jwt/v4 v4.5.0 github.com/google/uuid v1.6.0 github.com/gorilla/websocket v1.5.1 + github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.113 github.com/jackc/pgx/v5 v5.5.4 github.com/jinzhu/copier v0.4.0 github.com/jinzhu/gorm v1.9.16 @@ -221,6 +222,7 @@ require ( github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.898 // indirect github.com/therootcompany/xz v1.0.1 // indirect github.com/theupdateframework/notary v0.7.0 // indirect + github.com/tjfoc/gmsm v1.4.1 // indirect github.com/tklauser/go-sysconf v0.3.13 // indirect github.com/tklauser/numcpus v0.7.0 // indirect github.com/tonistiigi/fsutil v0.0.0-20240301111122-7525a1af2bb5 // indirect @@ -233,6 +235,7 @@ require ( github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect + go.mongodb.org/mongo-driver v1.12.0 // indirect go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.48.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.48.0 // indirect diff --git a/go.sum b/go.sum index 0fcba9a1e..b4de1181c 100644 --- a/go.sum +++ b/go.sum @@ -369,6 +369,7 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -453,6 +454,8 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.113 h1:odui9Ua0u1hPfpkutN/tGvtt0ms55I+gQqIdU8K1rlo= +github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.113/go.mod h1:JWz2ujO9X3oU5wb6kXp+DpR2UuDj2SldDbX8T0FSuhI= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= @@ -501,6 +504,7 @@ github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI= github.com/klauspost/compress v1.17.6/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= @@ -599,6 +603,7 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mojocn/base64Captcha v1.3.6 h1:gZEKu1nsKpttuIAQgWHO+4Mhhls8cAKyiV2Ew03H+Tw= github.com/mojocn/base64Captcha v1.3.6/go.mod h1:i5CtHvm+oMbj1UzEPXaA8IH/xHFZ3DGY3Wh3dBpZ28E= +github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/mozillazg/go-httpheader v0.2.1/go.mod h1:jJ8xECTlalr6ValeXYdOF8fFUISeBAdw6E61aqQma60= @@ -780,6 +785,8 @@ github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+x github.com/therootcompany/xz v1.0.1/go.mod h1:3K3UH1yCKgBneZYhuQUvJ9HPD19UEXEI0BWbMn8qNMY= github.com/theupdateframework/notary v0.7.0 h1:QyagRZ7wlSpjT5N2qQAh/pN+DVqgekv4DzbAiAiEL3c= github.com/theupdateframework/notary v0.7.0/go.mod h1:c9DRxcmhHmVLDay4/2fUYdISnHqbFDGRSlXPO0AhYWw= +github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho= +github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE= github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= github.com/tklauser/go-sysconf v0.3.13 h1:GBUpcahXSpR2xN01jhkNAbTLRk2Yzgggk8IM08lq3r4= github.com/tklauser/go-sysconf v0.3.13/go.mod h1:zwleP4Q4OehZHGn4CYZDipCgg9usW5IJePewFCGVEa0= @@ -807,6 +814,9 @@ github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8= github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/vbatts/tar-split v0.11.5 h1:3bHCTIheBm1qFTcgh9oPu+nNBtX+XJIupG/vacinCts= github.com/vbatts/tar-split v0.11.5/go.mod h1:yZbwRsSeGjusneWgA781EKej9HF8vme8okylkAeNKLk= +github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= +github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= +github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= @@ -816,6 +826,7 @@ github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17 github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xlzd/gotp v0.1.0 h1:37blvlKCh38s+fkem+fFh7sMnceltoIEBYTVXyoa5Po= github.com/xlzd/gotp v0.1.0/go.mod h1:ndLJ3JKzi3xLmUProq4LLxCuECL93dG9WASNLpHz8qg= +github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -824,6 +835,8 @@ github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5t github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +go.mongodb.org/mongo-driver v1.12.0 h1:aPx33jmn/rQuJXPQLZQ8NtfPQG8CaqgLThFtqRb0PiE= +go.mongodb.org/mongo-driver v1.12.0/go.mod h1:AZkxhPnFJUoH7kZlFkVKucV20K387miPfm7oimrSmK0= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -872,11 +885,14 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -994,12 +1010,14 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1009,9 +1027,12 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=