WIP: HTTP-01 solver that stores challenge tokens in FrostFS #4
9 changed files with 692 additions and 11 deletions
30
cmd/flags.go
30
cmd/flags.go
|
@ -29,6 +29,11 @@ const (
|
|||
flgHTTPWebroot = "http.webroot"
|
||||
flgHTTPMemcachedHost = "http.memcached-host"
|
||||
flgHTTPS3Bucket = "http.s3-bucket"
|
||||
flgHTTPFrostFSEndpoint = "http.frostfs-endpoint"
|
||||
flgHTTPFrostFSContainer = "http.frostfs-container"
|
||||
flgHTTPFrostFSWallet = "http.frostfs-wallet"
|
||||
flgHTTPFrostFSWalletAccount = "http.frostfs-wallet-account"
|
||||
flgHTTPFrostFSWalletPass = "http.frostfs-wallet-password"
|
||||
flgTLS = "tls"
|
||||
flgTLSPort = "tls.port"
|
||||
flgDNS = "dns"
|
||||
|
@ -135,6 +140,31 @@ func CreateFlags(defaultPath string) []cli.Flag {
|
|||
Name: flgHTTPS3Bucket,
|
||||
Usage: "Set the S3 bucket name to use for HTTP-01 based challenges. Challenges will be written to the S3 bucket.",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flgHTTPFrostFSEndpoint,
|
||||
Usage: "Set FrostFS endpoint to use for HTTP-01 based challenges. Challenges will be written to FrostFS container",
|
||||
EnvVars: []string{"FROSTFS_ENDPOINT"},
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flgHTTPFrostFSContainer,
|
||||
Usage: "Set FrostFS container ID to use for HTTP-01 based challenges. Challenges will be written to FrostFS container",
|
||||
EnvVars: []string{"FROSTFS_CONTAINER"},
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flgHTTPFrostFSWallet,
|
||||
Usage: "Path to NEO wallet to use for interaction with FrostFS. If no wallet is provided an ephemeral one will be generated. Such key will only work for publicly writable containers and will significantly reduce security: any attacker with knowledge of FrostFS endpoint and CID will be able to obtain certificates for your domain",
|
||||
EnvVars: []string{"FROSTFS_WALLET"},
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flgHTTPFrostFSWalletAccount,
|
||||
Usage: "Wallet account to use for interaction with FrostFS. If not set, the first account from wallet will be used",
|
||||
EnvVars: []string{"FROSTFS_WALLET_ACCOUNT"},
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flgHTTPFrostFSWalletPass,
|
||||
Usage: "Account password to decrypt the wallet. If not set, an empty password is assumed",
|
||||
EnvVars: []string{"FROSTFS_WALLET_PASSWORD"},
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: flgTLS,
|
||||
Usage: "Use the TLS-ALPN-01 challenge to solve challenges. Can be mixed with other types of challenges.",
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
"github.com/go-acme/lego/v4/lego"
|
||||
"github.com/go-acme/lego/v4/log"
|
||||
"github.com/go-acme/lego/v4/providers/dns"
|
||||
"github.com/go-acme/lego/v4/providers/http/frostfs"
|
||||
"github.com/go-acme/lego/v4/providers/http/memcached"
|
||||
"github.com/go-acme/lego/v4/providers/http/s3"
|
||||
"github.com/go-acme/lego/v4/providers/http/webroot"
|
||||
|
@ -67,6 +68,18 @@ func setupHTTPProvider(ctx *cli.Context) challenge.Provider {
|
|||
log.Fatal(err)
|
||||
}
|
||||
return ps
|
||||
case ctx.IsSet(flgHTTPFrostFSEndpoint):
|
||||
ps, err := frostfs.NewHTTPProvider(
|
||||
ctx.String(flgHTTPFrostFSEndpoint),
|
||||
ctx.String(flgHTTPFrostFSContainer),
|
||||
ctx.String(flgHTTPFrostFSWallet),
|
||||
ctx.String(flgHTTPFrostFSWalletAccount),
|
||||
ctx.String(flgHTTPFrostFSWalletPass),
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return ps
|
||||
case ctx.IsSet(flgHTTPPort):
|
||||
iface := ctx.String(flgHTTPPort)
|
||||
if !strings.Contains(iface, ":") {
|
||||
|
|
|
@ -36,6 +36,11 @@ GLOBAL OPTIONS:
|
|||
--http.webroot value Set the webroot folder to use for HTTP-01 based challenges to write directly to the .well-known/acme-challenge file. This disables the built-in server and expects the given directory to be publicly served with access to .well-known/acme-challenge
|
||||
--http.memcached-host value [ --http.memcached-host value ] Set the memcached host(s) to use for HTTP-01 based challenges. Challenges will be written to all specified hosts.
|
||||
--http.s3-bucket value Set the S3 bucket name to use for HTTP-01 based challenges. Challenges will be written to the S3 bucket.
|
||||
--http.frostfs-endpoint value Set FrostFS endpoint to use for HTTP-01 based challenges. Challenges will be written to FrostFS container [$FROSTFS_ENDPOINT]
|
||||
--http.frostfs-container value Set FrostFS container ID to use for HTTP-01 based challenges. Challenges will be written to FrostFS container [$FROSTFS_CONTAINER]
|
||||
--http.frostfs-wallet value Path to NEO wallet to use for interaction with FrostFS. If no wallet is provided an ephemeral one will be generated. Such key will only work for publicly writable containers and will significantly reduce security: any attacker with knowledge of FrostFS endpoint and CID will be able to obtain certificates for your domain [$FROSTFS_WALLET]
|
||||
--http.frostfs-wallet-account value Wallet account to use for interaction with FrostFS. If not set, the first account from wallet will be used [$FROSTFS_WALLET_ACCOUNT]
|
||||
--http.frostfs-wallet-password value Account password to decrypt the wallet. If not set, an empty password is assumed [$FROSTFS_WALLET_PASSWORD]
|
||||
--tls Use the TLS-ALPN-01 challenge to solve challenges. Can be mixed with other types of challenges. (default: false)
|
||||
--tls.port value Set the port and interface to use for TLS-ALPN-01 based challenges to listen on. Supported: interface:port or :port. (default: ":443")
|
||||
--dns value Solve a DNS-01 challenge using the specified provider. Can be mixed with other types of challenges. Run 'lego dnshelp' for help on usage.
|
||||
|
|
24
go.mod
24
go.mod
|
@ -4,6 +4,7 @@ go 1.22.0
|
|||
|
||||
require (
|
||||
cloud.google.com/go/compute/metadata v0.5.1
|
||||
git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20241011121723-d7872061f859
|
||||
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0
|
||||
|
@ -56,6 +57,7 @@ require (
|
|||
github.com/nrdcg/namesilo v0.2.1
|
||||
github.com/nrdcg/nodion v0.1.0
|
||||
github.com/nrdcg/porkbun v0.4.0
|
||||
github.com/nspcc-dev/neo-go v0.106.2
|
||||
github.com/nzdjb/go-metaname v1.0.0
|
||||
github.com/oracle/oci-go-sdk/v65 v65.73.0
|
||||
github.com/ovh/go-ovh v1.6.0
|
||||
|
@ -90,6 +92,11 @@ require (
|
|||
require (
|
||||
cloud.google.com/go/auth v0.9.3 // indirect
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.4 // indirect
|
||||
git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20241011114054-f0fc40e116d1 // indirect
|
||||
git.frostfs.info/TrueCloudLab/frostfs-crypto v0.6.0 // indirect
|
||||
git.frostfs.info/TrueCloudLab/hrw v1.2.1 // indirect
|
||||
git.frostfs.info/TrueCloudLab/rfc6979 v0.4.0 // indirect
|
||||
git.frostfs.info/TrueCloudLab/tzhash v1.8.0 // indirect
|
||||
github.com/AdamSLevy/jsonrpc2/v14 v14.1.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
|
||||
|
@ -99,6 +106,8 @@ require (
|
|||
github.com/Azure/go-autorest/logger v0.2.1 // indirect
|
||||
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 // indirect
|
||||
github.com/VictoriaMetrics/easyproto v0.1.4 // indirect
|
||||
github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.13 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.17 // indirect
|
||||
|
@ -116,6 +125,7 @@ require (
|
|||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
|
||||
github.com/dimchansky/utfbom v1.1.1 // indirect
|
||||
github.com/fatih/structs v1.1.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
|
@ -142,8 +152,10 @@ require (
|
|||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/hashicorp/go-uuid v1.0.3 // indirect
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 // indirect
|
||||
github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b // indirect
|
||||
|
@ -152,10 +164,14 @@ require (
|
|||
github.com/leodido/go-urn v1.2.4 // indirect
|
||||
github.com/liquidweb/liquidweb-cli v0.6.9 // indirect
|
||||
github.com/magiconair/properties v1.8.7 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // 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
|
||||
github.com/mr-tron/base58 v1.2.0 // indirect
|
||||
github.com/nspcc-dev/go-ordered-json v0.0.0-20240301084351-0246b013f8b2 // indirect
|
||||
github.com/nspcc-dev/rfc6979 v0.2.1 // indirect
|
||||
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
|
||||
|
@ -181,6 +197,7 @@ require (
|
|||
github.com/stretchr/objx v0.5.2 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/tjfoc/gmsm v1.4.1 // indirect
|
||||
github.com/twmb/murmur3 v1.1.8 // indirect
|
||||
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
|
||||
go.mongodb.org/mongo-driver v1.12.0 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
|
@ -188,10 +205,9 @@ require (
|
|||
go.opentelemetry.io/otel v1.29.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.29.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.29.0 // indirect
|
||||
go.uber.org/atomic v1.9.0 // indirect
|
||||
go.uber.org/multierr v1.9.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/ratelimit v0.3.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240213143201-ec583247a57a // indirect
|
||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect
|
||||
golang.org/x/mod v0.21.0 // indirect
|
||||
golang.org/x/sync v0.8.0 // indirect
|
||||
golang.org/x/sys v0.25.0 // indirect
|
||||
|
@ -200,7 +216,7 @@ require (
|
|||
google.golang.org/genproto v0.0.0-20240903143218-8af14fe29dc1 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240827150818-7e3bb234dfed // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect
|
||||
google.golang.org/grpc v1.66.1 // indirect
|
||||
google.golang.org/grpc v1.66.2 // indirect
|
||||
google.golang.org/protobuf v1.34.2 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
|
|
59
go.sum
59
go.sum
|
@ -17,6 +17,18 @@ cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqCl
|
|||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20241011114054-f0fc40e116d1 h1:ivcdxQeQDnx4srF2ezoaeVlF0FAycSAztwfIUJnUI4s=
|
||||
git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20241011114054-f0fc40e116d1/go.mod h1:F5GS7hRb62PUy5sTYDC4ajVdeffoAfjHSSHTKUJEaYU=
|
||||
git.frostfs.info/TrueCloudLab/frostfs-crypto v0.6.0 h1:FxqFDhQYYgpe41qsIHVOcdzSVCB8JNSfPG7Uk4r2oSk=
|
||||
git.frostfs.info/TrueCloudLab/frostfs-crypto v0.6.0/go.mod h1:RUIKZATQLJ+TaYQa60X2fTDwfuhMfm8Ar60bQ5fr+vU=
|
||||
git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20241011121723-d7872061f859 h1:XXlKY9bseTzggafcgP3LTryCSCZ4chCUqHuNApRhsi0=
|
||||
git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20241011121723-d7872061f859/go.mod h1:3txOjFJ8M/JFs01h7xOrnQHVn6hZgDNA16ivyUlu1iU=
|
||||
git.frostfs.info/TrueCloudLab/hrw v1.2.1 h1:ccBRK21rFvY5R1WotI6LNoPlizk7qSvdfD8lNIRudVc=
|
||||
git.frostfs.info/TrueCloudLab/hrw v1.2.1/go.mod h1:C1Ygde2n843yTZEQ0FP69jYiuaYV0kriLvP4zm8JuvM=
|
||||
git.frostfs.info/TrueCloudLab/rfc6979 v0.4.0 h1:M2KR3iBj7WpY3hP10IevfIB9MURr4O9mwVfJ+SjT3HA=
|
||||
git.frostfs.info/TrueCloudLab/rfc6979 v0.4.0/go.mod h1:okpbKfVYf/BpejtfFTfhZqFP+sZ8rsHrP8Rr/jYPNRc=
|
||||
git.frostfs.info/TrueCloudLab/tzhash v1.8.0 h1:UFMnUIk0Zh17m8rjGHJMqku2hCgaXDqjqZzS4gsb4UA=
|
||||
git.frostfs.info/TrueCloudLab/tzhash v1.8.0/go.mod h1:dhY+oy274hV8wGvGL4MwwMpdL3GYvaX1a8GQZQHvlF8=
|
||||
github.com/AdamSLevy/jsonrpc2/v14 v14.1.0 h1:Dy3M9aegiI7d7PF1LUdjbVigJReo+QOceYsMyFh9qoE=
|
||||
github.com/AdamSLevy/jsonrpc2/v14 v14.1.0/go.mod h1:ZakZtbCXxCz82NJvq7MoREtiQesnDfrtF6RFUGzQfLo=
|
||||
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU=
|
||||
|
@ -70,6 +82,8 @@ github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXY
|
|||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87 h1:xPMsUicZ3iosVPSIP7bW5EcGUzjiiMl1OYTe14y/R24=
|
||||
github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87/go.mod h1:iGLljf5n9GjT6kc0HBvyI1nOKnGQbNB66VzSNbK5iks=
|
||||
github.com/VictoriaMetrics/easyproto v0.1.4 h1:r8cNvo8o6sR4QShBXQd1bKw/VVLSQma/V2KhTBPf+Sc=
|
||||
github.com/VictoriaMetrics/easyproto v0.1.4/go.mod h1:QlGlzaJnDfFd8Lk6Ci/fuLxfTo3/GThPs2KH23mv710=
|
||||
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
|
||||
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2 h1:F1j7z+/DKEsYqZNoxC6wvfmaiDneLsQOFQmuq9NADSY=
|
||||
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2/go.mod h1:QlXr/TrICfQ/ANa76sLeQyhAJyNR9sEcfNuZBkY9jgY=
|
||||
|
@ -78,6 +92,8 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF
|
|||
github.com/aliyun/alibaba-cloud-sdk-go v1.63.15 h1:r2uwBUQhLhcPzaWz9tRJqc8MjYwHb+oF2+Q6467BF14=
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.63.15/go.mod h1:SOSDHfe1kX91v3W5QiBsWSLqeLxImobbMX1mxrFHsVQ=
|
||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ=
|
||||
github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
|
@ -164,6 +180,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
|||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U=
|
||||
|
@ -268,6 +286,7 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
|
|||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
|
@ -291,8 +310,9 @@ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/
|
|||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20230817174616-7a8ec2ada47b h1:h9U78+dx9a4BKdQkBBos92HalKpaGKHrp+3Uo6yTodo=
|
||||
github.com/google/pprof v0.0.0-20230817174616-7a8ec2ada47b/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM=
|
||||
github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA=
|
||||
|
@ -315,6 +335,8 @@ github.com/gophercloud/utils v0.0.0-20231010081019-80377eca5d56/go.mod h1:VSalo4
|
|||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
|
||||
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
|
@ -347,6 +369,8 @@ github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b
|
|||
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||
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/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||
|
@ -370,6 +394,8 @@ github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHW
|
|||
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
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.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
|
@ -414,6 +440,8 @@ github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czP
|
|||
github.com/magiconair/properties v1.8.4/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||
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/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
|
@ -459,6 +487,8 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb
|
|||
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/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
|
||||
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
|
||||
github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04 h1:o6uBwrhM5C8Ll3MAAxrQxRHEu7FkapwTuI2WmL1rw4g=
|
||||
github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04/go.mod h1:5sN+Lt1CaY4wsPvgQH/jsuJi4XO2ssZbdsIizr4CVC8=
|
||||
|
@ -484,6 +514,12 @@ github.com/nrdcg/nodion v0.1.0 h1:zLKaqTn2X0aDuBHHfyA1zFgeZfiCpmu/O9DM73okavw=
|
|||
github.com/nrdcg/nodion v0.1.0/go.mod h1:inbuh3neCtIWlMPZHtEpe43TmRXxHV6+hk97iCZicms=
|
||||
github.com/nrdcg/porkbun v0.4.0 h1:rWweKlwo1PToQ3H+tEO9gPRW0wzzgmI/Ob3n2Guticw=
|
||||
github.com/nrdcg/porkbun v0.4.0/go.mod h1:/QMskrHEIM0IhC/wY7iTCUgINsxdT2WcOphktJ9+Q54=
|
||||
github.com/nspcc-dev/go-ordered-json v0.0.0-20240301084351-0246b013f8b2 h1:mD9hU3v+zJcnHAVmHnZKt3I++tvn30gBj2rP2PocZMk=
|
||||
github.com/nspcc-dev/go-ordered-json v0.0.0-20240301084351-0246b013f8b2/go.mod h1:U5VfmPNM88P4RORFb6KSUVBdJBDhlqggJZYGXGPxOcc=
|
||||
github.com/nspcc-dev/neo-go v0.106.2 h1:KXSJ2J5Oacc7LrX3r4jvnC8ihKqHs5NB21q4f2S3r9o=
|
||||
github.com/nspcc-dev/neo-go v0.106.2/go.mod h1:Ojwfx3/lv0VTeEHMpQ17g0wTnXcCSoFQVq5GEeCZmGo=
|
||||
github.com/nspcc-dev/rfc6979 v0.2.1 h1:8wWxkamHWFmO790GsewSoKUSJjVnL1fmdRpokU/RgRM=
|
||||
github.com/nspcc-dev/rfc6979 v0.2.1/go.mod h1:Tk7h5kyUWkhjyO3zUgFFhy1v2vQv3BvQEntakdtqrWc=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
|
@ -642,6 +678,8 @@ github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8
|
|||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954 h1:xQdMZ1WLrgkkvOZ/LDQxjVxMLdby7osSh4ZEVa5sIjs=
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954/go.mod h1:u2MKkTVTVJWe5D1rCvame8WqhBd88EuIwODJZ1VHCPM=
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1002 h1:RE84sHFFx6t24DJvSnF9fS1DzBNv9OpctzHK3t7AY+I=
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1002/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1002 h1:QwE0dRkAAbdf+eACnkNULgDn9ZKUJpPWRyXdqJolP5E=
|
||||
|
@ -651,6 +689,8 @@ github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVc
|
|||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/transip/gotransip/v6 v6.26.0 h1:Aejfvh8rSp8Mj2GX/RpdBjMCv+Iy/DmgfNgczPDP550=
|
||||
github.com/transip/gotransip/v6 v6.26.0/go.mod h1:x0/RWGRK/zob817O3tfO2xhFoP1vu8YOHORx6Jpk80s=
|
||||
github.com/twmb/murmur3 v1.1.8 h1:8Yt9taO/WN3l08xErzjeschgZU2QSrwm1kclYq+0aRg=
|
||||
github.com/twmb/murmur3 v1.1.8/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ=
|
||||
github.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaOOb6ThwMmTEbhRwtKR97o=
|
||||
github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
|
||||
github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVKhn2Um6rjCsSsg=
|
||||
|
@ -681,6 +721,8 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec
|
|||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI=
|
||||
go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE=
|
||||
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=
|
||||
|
@ -700,11 +742,13 @@ go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
|||
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
|
||||
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
|
||||
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
|
||||
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/ratelimit v0.3.0 h1:IdZd9wqvFXnvLvSEBo0KPcGfkoBGNkpTHlrE3Rcjkjw=
|
||||
go.uber.org/ratelimit v0.3.0/go.mod h1:So5LG7CV1zWpY1sHe+DXTJqQvOx+FFPFaAs2SnoyBaI=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
|
@ -733,8 +777,8 @@ golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL
|
|||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||
golang.org/x/exp v0.0.0-20240213143201-ec583247a57a h1:HinSgX1tJRX3KsL//Gxynpw5CTOAIPhgL4W8PNiIpVE=
|
||||
golang.org/x/exp v0.0.0-20240213143201-ec583247a57a/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc=
|
||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM=
|
||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
|
||||
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
|
@ -844,6 +888,7 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
|
@ -965,8 +1010,8 @@ google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv
|
|||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
|
||||
google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k=
|
||||
google.golang.org/grpc v1.66.1 h1:hO5qAXR19+/Z44hmvIM4dQFMSYX9XcWsByfoxutBpAM=
|
||||
google.golang.org/grpc v1.66.1/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y=
|
||||
google.golang.org/grpc v1.66.2 h1:3QdXkuq3Bkh7w+ywLdLvM56cmGvQHUMZpiCzt6Rqaoo=
|
||||
google.golang.org/grpc v1.66.2/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
|
|
275
providers/http/frostfs/client.go
Normal file
275
providers/http/frostfs/client.go
Normal file
|
@ -0,0 +1,275 @@
|
|||
package frostfs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
|
||||
status "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||
containerid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
||||
objectid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
||||
"github.com/nspcc-dev/neo-go/pkg/encoding/base58"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||
)
|
||||
|
||||
// Storage provides barebones API client for a single FrostFS container.
|
||||
type Storage struct {
|
||||
endpoint string
|
||||
container containerid.ID
|
||||
key *ecdsa.PrivateKey
|
||||
user user.ID
|
||||
epoch epochCalc
|
||||
}
|
||||
|
||||
const storageRequestTimeout = 10 * time.Second
|
||||
|
||||
// Open FrostFS client for working with a single storage container.
|
||||
func Open(endpoint, containerID string, key *ecdsa.PrivateKey) (*Storage, error) {
|
||||
var container containerid.ID
|
||||
err := container.DecodeString(containerID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid container ID: %w", err)
|
||||
}
|
||||
var owner user.ID
|
||||
user.IDFromKey(&owner, key.PublicKey)
|
||||
return &Storage{
|
||||
endpoint: endpoint,
|
||||
key: key,
|
||||
user: owner,
|
||||
container: container,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Save byte slice to FrostFS object
|
||||
//
|
||||
// Large objects must not be created this way. Depending on FrostFS network configuration,
|
||||
// attempting to send byte slices larger than ~64MB will always return an error.
|
||||
func (s *Storage) Save(ctx context.Context, data []byte, attr ...string) (oid string, err error) {
|
||||
attributes, err := keyval2attrs(attr...)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("invalid attributes: %w", err)
|
||||
}
|
||||
|
||||
obj := object.New()
|
||||
object.InitCreation(obj, object.RequiredFields{
|
||||
Container: s.container,
|
||||
Owner: s.user,
|
||||
})
|
||||
obj.SetAttributes(attributes...)
|
||||
obj.SetPayload(data)
|
||||
obj.SetPayloadSize(uint64(len(data)))
|
||||
object.CalculateAndSetPayloadChecksum(obj)
|
||||
err = object.CalculateAndSetID(obj)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("object ID: %w", err)
|
||||
}
|
||||
err = object.CalculateAndSetSignature(*s.key, obj)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("signing object: %w", err)
|
||||
}
|
||||
if !obj.VerifyIDSignature() {
|
||||
return "", errors.New("signing object: invalid signature was generated")
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(ctx, storageRequestTimeout)
|
||||
defer cancel()
|
||||
|
||||
c, err := s.dial(ctx)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("connecting to storage node: %w", err)
|
||||
}
|
||||
put := client.PrmObjectPutSingle{
|
||||
Key: s.key,
|
||||
Object: obj,
|
||||
}
|
||||
res, err := c.ObjectPutSingle(ctx, put)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("sending object to storage: %w", err)
|
||||
}
|
||||
stat := res.Status()
|
||||
if !status.IsSuccessful(stat) {
|
||||
return "", fmt.Errorf("saving object to storage: %w", stat.(error))
|
||||
}
|
||||
id, _ := obj.ID()
|
||||
return id.String(), nil
|
||||
}
|
||||
|
||||
// Delete object from container.
|
||||
func (s *Storage) Delete(ctx context.Context, oid string) error {
|
||||
var obj objectid.ID
|
||||
err := obj.DecodeString(oid)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid object id: %w", err)
|
||||
}
|
||||
c, err := s.dial(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("connecting to storage node: %w", err)
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(ctx, storageRequestTimeout)
|
||||
defer cancel()
|
||||
res, err := c.ObjectDelete(ctx, client.PrmObjectDelete{
|
||||
ContainerID: &s.container,
|
||||
ObjectID: &obj,
|
||||
Key: s.key,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("delete request (%s): %w", oid, err)
|
||||
}
|
||||
stat := res.Status()
|
||||
if !status.IsSuccessful(stat) {
|
||||
return fmt.Errorf("delete object (%s): %w", oid, stat.(error))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Open new connection to FrostFS storage node.
|
||||
func (s *Storage) dial(ctx context.Context) (*client.Client, error) {
|
||||
var w client.PrmInit
|
||||
w.Key = *s.key
|
||||
|
||||
var c client.Client
|
||||
c.Init(w)
|
||||
|
||||
var endpoint client.PrmDial
|
||||
endpoint.Endpoint = s.endpoint
|
||||
|
||||
err := c.Dial(ctx, endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &c, nil
|
||||
}
|
||||
|
||||
// Compose object attributes slice.
|
||||
func keyval2attrs(attr ...string) ([]object.Attribute, error) {
|
||||
if len(attr)%2 != 0 {
|
||||
return nil, fmt.Errorf("odd number of key-value strings: %d (must be even)", len(attr))
|
||||
}
|
||||
attributes := make([]object.Attribute, 0, len(attr)/2)
|
||||
var current *object.Attribute
|
||||
for index, text := range attr {
|
||||
if index%2 == 0 {
|
||||
current = object.NewAttribute()
|
||||
current.SetKey(text)
|
||||
} else {
|
||||
current.SetValue(text)
|
||||
attributes = append(attributes, *current)
|
||||
}
|
||||
}
|
||||
return attributes, nil
|
||||
}
|
||||
|
||||
// Load private key from wallet file.
|
||||
func getKey(walletPath, walletAccount, walletPassword string) (*ecdsa.PrivateKey, error) { //nolint:gocyclo
|
||||
if walletPath == "" {
|
||||
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("generating ephemeral key: %w", err)
|
||||
}
|
||||
return key, nil
|
||||
}
|
||||
|
||||
// This function intentionally omits calls to w.Close() and account.Close()
|
||||
// because that would destroy the underlying ecdsa.PrivateKey.
|
||||
w, err := wallet.NewWalletFromFile(walletPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(w.Accounts) == 0 {
|
||||
return nil, fmt.Errorf("no accounts in wallet: %s", walletPath)
|
||||
}
|
||||
account := w.Accounts[0]
|
||||
if walletAccount != "" {
|
||||
decode, err := base58.CheckDecode(walletAccount) //nolint:govet // err shadow declaration
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid account address: %w", err)
|
||||
}
|
||||
if len(decode) != 21 {
|
||||
return nil, fmt.Errorf("invalid account address length: %d bytes", len(decode))
|
||||
}
|
||||
if decode[0] != 0x35 {
|
||||
return nil, fmt.Errorf("invalid account address first byte: %s -> %#x", walletAccount, decode[0])
|
||||
}
|
||||
hash, err := util.Uint160DecodeBytesBE(decode[1:])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid account hash: %w", err)
|
||||
}
|
||||
account = w.GetAccount(hash)
|
||||
if account == nil {
|
||||
return nil, fmt.Errorf("account not found: %s", walletAccount)
|
||||
}
|
||||
}
|
||||
if account.PrivateKey() == nil {
|
||||
err = account.Decrypt(walletPassword, w.Scrypt)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decrypt wallet: %w", err)
|
||||
}
|
||||
}
|
||||
key := account.PrivateKey().PrivateKey
|
||||
return &key, nil
|
||||
}
|
||||
|
||||
// Epoch converts human time value into FrostFS epoch that is expected to be
|
||||
// current at that time.
|
||||
//
|
||||
// Due to nonlinear nature of FrostFS time these calculations are approximate
|
||||
// for the future and are likely wrong for the past.
|
||||
func (s *Storage) Epoch(ctx context.Context, t time.Time) (epoch uint64, err error) {
|
||||
if !s.epoch.Ready() {
|
||||
var cancel context.CancelFunc
|
||||
ctx, cancel = context.WithTimeout(ctx, storageRequestTimeout)
|
||||
defer cancel()
|
||||
c, err := s.dial(ctx)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("connecting to storage node: %w", err)
|
||||
}
|
||||
res, err := c.NetworkInfo(ctx, client.PrmNetworkInfo{})
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("network info request: %w", err)
|
||||
}
|
||||
stat := res.Status()
|
||||
if !status.IsSuccessful(stat) {
|
||||
return 0, fmt.Errorf("network info: %w", stat.(error))
|
||||
}
|
||||
info := res.Info()
|
||||
s.epoch = epochCalc{
|
||||
timestamp: time.Now(),
|
||||
epoch: info.CurrentEpoch(),
|
||||
blockPerEpoch: info.EpochDuration(),
|
||||
msPerBlock: info.MsPerBlock(),
|
||||
}
|
||||
}
|
||||
if !s.epoch.Ready() {
|
||||
return 0, errors.New("failed to initialize epoch calculator")
|
||||
}
|
||||
return s.epoch.At(t), nil
|
||||
}
|
||||
|
||||
type epochCalc struct {
|
||||
timestamp time.Time
|
||||
epoch uint64
|
||||
msPerBlock int64
|
||||
blockPerEpoch uint64
|
||||
}
|
||||
|
||||
func (e epochCalc) At(t time.Time) uint64 {
|
||||
return uint64(
|
||||
int64(e.epoch) +
|
||||
int64(math.Ceil(
|
||||
float64(t.Sub(e.timestamp).Milliseconds())/
|
||||
float64(e.msPerBlock)/
|
||||
float64(e.blockPerEpoch))))
|
||||
}
|
||||
|
||||
func (e epochCalc) Ready() bool {
|
||||
return !e.timestamp.IsZero() && e.epoch != 0 && e.msPerBlock != 0 && e.blockPerEpoch != 0
|
||||
}
|
211
providers/http/frostfs/client_test.go
Normal file
211
providers/http/frostfs/client_test.go
Normal file
|
@ -0,0 +1,211 @@
|
|||
package frostfs
|
||||
|
||||
// Tests for our FrostFS client code
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"crypto/sha256"
|
||||
"os"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||
)
|
||||
|
||||
// Initialize storage backend for tests.
|
||||
func openStorage(t *testing.T) *Storage {
|
||||
t.Helper()
|
||||
cid := os.Getenv("FROSTFS_CONTAINER") // sample: "348WWfBKbS79Wbmm38MRE3oBoEDM5Ga1XXbGKGNyisDM"
|
||||
endpoint := os.Getenv("FROSTFS_ENDPOINT") // sample: "grpc://localhost:8802"
|
||||
if cid == "" || endpoint == "" {
|
||||
t.Skipf("one or more environment variables not set: FROSTFS_ENDPOINT, FROSTFS_CONTAINER")
|
||||
}
|
||||
key, err := getKey(
|
||||
os.Getenv("FROSTFS_WALLET"),
|
||||
os.Getenv("FROSTFS_WALLET_ACCOUNT"),
|
||||
os.Getenv("FROSTFS_WALLET_PASSWORD"),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
storage, err := Open(endpoint, cid, key)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return storage
|
||||
}
|
||||
|
||||
// Save some bytes to FrostFS and clean up after.
|
||||
func TestObjectPutDelete(t *testing.T) {
|
||||
payload := []byte("Hello FrostFS!\n")
|
||||
storage := openStorage(t)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
t.Cleanup(cancel)
|
||||
oid, err := storage.Save(ctx, payload, "FileName", "hello_from_sdk.txt", "Tag", "foobar")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Logf("saved object: %s", oid)
|
||||
err = storage.Delete(ctx, oid)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Logf("deleted object: %s", oid)
|
||||
}
|
||||
|
||||
// Check that FrostFS is in fact a content addressable storage.
|
||||
func TestMulipleObjects(t *testing.T) {
|
||||
payload := []byte("Multiple objects with same content should get the same object ID")
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
t.Cleanup(cancel)
|
||||
var wg sync.WaitGroup
|
||||
|
||||
objects := make(chan string)
|
||||
go func() {
|
||||
baseline := ""
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case obj, ok := <-objects:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if baseline == "" {
|
||||
baseline = obj
|
||||
continue
|
||||
}
|
||||
if obj != baseline {
|
||||
t.Errorf("non-identical object id: %s != %s", baseline, obj)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
const testCount = 5
|
||||
storage := openStorage(t)
|
||||
for i := 0; i < testCount; i++ { //nolint:intrange
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
oid, err := storage.Save(ctx, payload, "FileName", "CAS.txt", "Tag", "test")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
err = storage.Delete(ctx, oid)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
select {
|
||||
case objects <- oid:
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
t.Log(oid)
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
// Check opening wallet from file system.
|
||||
func TestLoadWallet(t *testing.T) {
|
||||
const (
|
||||
walletPath = "client_test_wallet.json"
|
||||
walletAccount = "NWZnjbTKbzwtX6w1q5R3kbEKrnJ5bp1kn7"
|
||||
)
|
||||
key, err := getKey(walletPath, "", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if key2addr(key) != walletAccount {
|
||||
t.Fatalf("incorrect address for default account: want %s, got %s", walletAccount, key2addr(key))
|
||||
}
|
||||
key, err = getKey(walletPath, walletAccount, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if key2addr(key) != walletAccount {
|
||||
t.Fatalf("incorrect address for specific account: want %s, got %s", walletAccount, key2addr(key))
|
||||
}
|
||||
}
|
||||
|
||||
func key2addr(k *ecdsa.PrivateKey) string {
|
||||
var owner user.ID
|
||||
user.IDFromKey(&owner, k.PublicKey)
|
||||
return owner.String()
|
||||
}
|
||||
|
||||
// Check that loaded wallet key generates valid signature.
|
||||
func TestLoadWalletAndSign(t *testing.T) {
|
||||
const (
|
||||
walletPath = "client_test_wallet.json"
|
||||
walletAccount = "NWZnjbTKbzwtX6w1q5R3kbEKrnJ5bp1kn7"
|
||||
walletAccountPassword = ""
|
||||
walletAccountIndex = 0
|
||||
dataString = "some data to sign in this test"
|
||||
)
|
||||
w, err := wallet.NewWalletFromFile(walletPath)
|
||||
if err != nil {
|
||||
t.Fatalf("wallet from file: %v", err)
|
||||
}
|
||||
accountFromWallet := w.Accounts[walletAccountIndex]
|
||||
err = accountFromWallet.Decrypt(walletAccountPassword, w.Scrypt)
|
||||
if err != nil {
|
||||
t.Fatalf("wallet decrypt: %v", err)
|
||||
}
|
||||
|
||||
key, err := getKey(walletPath, walletAccount, walletAccountPassword)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
wrappedKey := &keys.PrivateKey{PrivateKey: *key}
|
||||
accountFromKey := wallet.NewAccountFromPrivateKey(wrappedKey)
|
||||
|
||||
if !accountFromKey.PublicKey().Equal(accountFromWallet.PublicKey()) {
|
||||
t.Fatalf("corrupted key: want %s, got %s", accountFromWallet.PublicKey().Address(), accountFromKey.PublicKey().Address())
|
||||
}
|
||||
data := []byte(dataString)
|
||||
sig := accountFromKey.PrivateKey().Sign(data)
|
||||
hash := sha256.Sum256(data)
|
||||
hashSig := accountFromKey.PrivateKey().SignHash(hash)
|
||||
if !bytes.Equal(sig, hashSig) {
|
||||
t.Fatalf("different signatures for data (%x) and for hash (%x)", sig, hashSig)
|
||||
}
|
||||
ok := accountFromKey.PublicKey().Verify(sig, hash[:])
|
||||
if !ok {
|
||||
t.Errorf("signature %x (%d bytes): check failed: signing key", sig, len(sig))
|
||||
}
|
||||
ok = accountFromWallet.PublicKey().Verify(sig, hash[:])
|
||||
if !ok {
|
||||
t.Errorf("signature %x (%d bytes): check failed: wallet key", sig, len(sig))
|
||||
}
|
||||
}
|
||||
|
||||
// Show epoch values for nearby times.
|
||||
//
|
||||
// This test is barely useful when run unattended: it will only validate "no
|
||||
// errors, no panic" result.
|
||||
// Human operator is required to check if epoch calculations actually make
|
||||
// sense for network configuration being used.
|
||||
func TestEpoch(t *testing.T) {
|
||||
storage := openStorage(t)
|
||||
now := time.Now()
|
||||
var i time.Duration
|
||||
for i = -10; i < 10; i++ {
|
||||
timestamp := now.Add(i * time.Minute) //nolint:durationcheck
|
||||
epoch, err := storage.Epoch(context.Background(), timestamp)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Logf("%s: %d", timestamp, epoch)
|
||||
}
|
||||
}
|
1
providers/http/frostfs/client_test_wallet.json
Normal file
1
providers/http/frostfs/client_test_wallet.json
Normal file
|
@ -0,0 +1 @@
|
|||
{"version":"1.0","accounts":[{"address":"NWZnjbTKbzwtX6w1q5R3kbEKrnJ5bp1kn7","key":"6PYNiUNC8ss4jfvhfTeXkboUaw8rF1Fycvi8sGfuKWkApJyxJwwQhJYUMS","label":"notused","contract":{"script":"DCECqsn8tWyTP/7yDX76x5POd+QJPma2QaEaUK5FMo+1K+NBVuezJw==","parameters":[{"name":"parameter0","type":"Signature"}],"deployed":false},"lock":false,"isDefault":false}],"scrypt":{"n":16384,"r":8,"p":8},"extra":{"Tokens":null}}
|
85
providers/http/frostfs/frostfs.go
Normal file
85
providers/http/frostfs/frostfs.go
Normal file
|
@ -0,0 +1,85 @@
|
|||
// Package frostfs provides HTTP-01 solver that saves challenge token to
|
||||
// FrostFS to make it available to multiple hosts at once.
|
||||
// Useful for deploying FrostFS gateways (HTTP or S3)
|
||||
package frostfs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/challenge"
|
||||
)
|
||||
|
||||
const (
|
||||
// Challenge token will be garbage collected sometime after this interval
|
||||
// even if Cleanup() call fails for whatever reason.
|
||||
tokenLifetime = 1 * time.Hour
|
||||
)
|
||||
|
||||
// HTTPProvider is a custom solver for HTTP-01 challenge that saves token to FrostFS.
|
||||
type HTTPProvider struct {
|
||||
frostfs *Storage
|
||||
oid string
|
||||
}
|
||||
|
||||
var _ challenge.Provider = new(HTTPProvider)
|
||||
|
||||
func NewHTTPProvider(endpoint, cid, walletPath, walletAccount, walletPassword string) (*HTTPProvider, error) {
|
||||
if endpoint == "" {
|
||||
return nil, errors.New("empty endpoint")
|
||||
}
|
||||
if cid == "" {
|
||||
return nil, errors.New("empty container id")
|
||||
}
|
||||
key, err := getKey(walletPath, walletAccount, walletPassword)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
storage, err := Open(endpoint, cid, key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &HTTPProvider{frostfs: storage}, nil
|
||||
}
|
||||
|
||||
func (w *HTTPProvider) Present(domain, token, keyAuth string) error {
|
||||
if strings.Contains(token, "/") {
|
||||
return fmt.Errorf("token with slash character is not supported: %s", token)
|
||||
}
|
||||
if w.oid != "" {
|
||||
return fmt.Errorf("%T is not safe to re-enter: object was saved and not yet cleaned up: %s", w, w.oid)
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
var err error
|
||||
expires, err := w.frostfs.Epoch(ctx, time.Now().Add(tokenLifetime))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to calculate token expiration: %w", err)
|
||||
}
|
||||
w.oid, err = w.frostfs.Save(
|
||||
ctx,
|
||||
[]byte(keyAuth),
|
||||
"FileName", token,
|
||||
"ACME", token,
|
||||
"__SYSTEM__EXPIRATION_EPOCH", strconv.FormatUint(expires, 10),
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
func (w *HTTPProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
if w.oid == "" {
|
||||
panic("Cleanup() called before Present()")
|
||||
}
|
||||
err := w.frostfs.Delete(context.TODO(), w.oid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
w.oid = ""
|
||||
return nil
|
||||
}
|
Loading…
Reference in a new issue