diff --git a/cmd/provider-services/cmd/grpc.go b/cmd/provider-services/cmd/grpc.go new file mode 100644 index 000000000..f88cc8e28 --- /dev/null +++ b/cmd/provider-services/cmd/grpc.go @@ -0,0 +1,21 @@ +package cmd + +import ( + "fmt" + "net" + "net/url" +) + +func grpcURI(hostURI string) (string, error) { + u, err := url.Parse(hostURI) + if err != nil { + return "", fmt.Errorf("url parse: %w", err) + } + + h, _, err := net.SplitHostPort(u.Host) + if err != nil { + return "", fmt.Errorf("split host port: %w", err) + } + + return net.JoinHostPort(h, "8444"), nil +} diff --git a/cmd/provider-services/cmd/manifest.go b/cmd/provider-services/cmd/manifest.go index 56379dfe6..a0b41d73f 100644 --- a/cmd/provider-services/cmd/manifest.go +++ b/cmd/provider-services/cmd/manifest.go @@ -2,9 +2,11 @@ package cmd import ( "bytes" + "context" "crypto/tls" "encoding/json" "fmt" + "time" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -14,16 +16,17 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" dtypes "github.com/akash-network/akash-api/go/node/deployment/v1beta3" + ptypes "github.com/akash-network/akash-api/go/node/provider/v1beta3" + leasev1 "github.com/akash-network/akash-api/go/provider/lease/v1" "github.com/akash-network/node/sdl" cutils "github.com/akash-network/node/x/cert/utils" aclient "github.com/akash-network/provider/client" + gwgrpc "github.com/akash-network/provider/gateway/grpc" gwrest "github.com/akash-network/provider/gateway/rest" ) -var ( - errSubmitManifestFailed = errors.New("submit manifest to some providers has been failed") -) +var errSubmitManifestFailed = errors.New("submit manifest to some providers has been failed") // SendManifestCmd looks up the Providers blockchain information, // and POSTs the SDL file to the Gateway address. @@ -94,32 +97,68 @@ func doSendManifest(cmd *cobra.Command, sdlpath string) error { ErrorMessage string `json:"errorMessage,omitempty" yaml:"errorMessage,omitempty"` } - results := make([]result, len(leases)) - - submitFailed := false + var ( + results = make([]result, len(leases)) + submitFailed = false + ) for i, lid := range leases { - prov, _ := sdk.AccAddressFromBech32(lid.Provider) - gclient, err := gwrest.NewClient(cl, prov, []tls.Certificate{cert}) - if err != nil { - return err - } + err = func() error { + ctx, cancel := context.WithTimeout(ctx, 5*time.Second) + defer cancel() + + provAddr, _ := sdk.AccAddressFromBech32(lid.Provider) + prov, err := cl.Provider(ctx, &ptypes.QueryProviderRequest{Owner: provAddr.String()}) + if err != nil { + return fmt.Errorf("query client provider: %w", err) + } - err = gclient.SubmitManifest(cmd.Context(), dseq, mani) - res := result{ - Provider: prov, - Status: "PASS", - } - if err != nil { - res.Error = err.Error() - if e, valid := err.(gwrest.ClientResponseError); valid { - res.ErrorMessage = e.Message + hostURIgRPC, err := grpcURI(prov.GetProvider().HostURI) + if err != nil { + return fmt.Errorf("grpc uri: %w", err) } - res.Status = "FAIL" - submitFailed = true - } - results[i] = res + res := result{ + Provider: provAddr, + Status: "PASS", + } + + c, err := gwgrpc.NewClient(ctx, hostURIgRPC, cert, cl) + if err == nil { + defer c.Close() + + if _, err = c.SendManifest(ctx, &leasev1.SendManifestRequest{ + LeaseId: lid, + Manifest: mani, + }); err != nil { + res.Error = err.Error() + res.Status = "FAIL" + submitFailed = true + } + } else { + gclient, err := gwrest.NewClient(cl, provAddr, []tls.Certificate{cert}) + if err != nil { + return fmt.Errorf("gwrest new client: %w", err) + } + + err = gclient.SubmitManifest(cmd.Context(), dseq, mani) + if err != nil { + res.Error = err.Error() + if e, valid := err.(gwrest.ClientResponseError); valid { + res.ErrorMessage = e.Message + } + res.Status = "FAIL" + submitFailed = true + } + } + + results[i] = res + + return nil + }() + if err != nil { + return err + } } buf := &bytes.Buffer{} @@ -146,7 +185,6 @@ func doSendManifest(cmd *cobra.Command, sdlpath string) error { } _, err = fmt.Fprint(cmd.OutOrStdout(), buf.String()) - if err != nil { return err } diff --git a/cmd/provider-services/cmd/run.go b/cmd/provider-services/cmd/run.go index 5eeec75e8..bec77b05c 100644 --- a/cmd/provider-services/cmd/run.go +++ b/cmd/provider-services/cmd/run.go @@ -11,7 +11,6 @@ import ( "strings" "time" - cltypes "github.com/akash-network/akash-api/go/node/client/types" sdkclient "github.com/cosmos/cosmos-sdk/client" "github.com/pkg/errors" "github.com/shopspring/decimal" @@ -21,6 +20,8 @@ import ( "golang.org/x/sync/errgroup" "k8s.io/client-go/kubernetes" + cltypes "github.com/akash-network/akash-api/go/node/client/types" + "github.com/tendermint/tendermint/libs/log" "github.com/cosmos/cosmos-sdk/client/flags" @@ -109,9 +110,7 @@ const ( serviceHostnameOperator = "hostname-operator" ) -var ( - errInvalidConfig = errors.New("Invalid configuration") -) +var errInvalidConfig = errors.New("Invalid configuration") // RunCmd launches the Akash Provider service func RunCmd() *cobra.Command { @@ -206,7 +205,7 @@ func RunCmd() *cobra.Command { panic(err) } - cmd.Flags().String(FlagGatewayGRPCListenAddress, "0.0.0.0:8444", "Gateway listen address") + cmd.Flags().String(FlagGatewayGRPCListenAddress, "0.0.0.0:8444", "Gateway gRPC listen address") if err := viper.BindPFlag(FlagGatewayGRPCListenAddress, cmd.Flags().Lookup(FlagGatewayGRPCListenAddress)); err != nil { panic(err) } @@ -420,9 +419,11 @@ var allowedBidPricingStrategies = [...]string{ bidPricingStrategyShellScript, } -var errNoSuchBidPricingStrategy = fmt.Errorf("No such bid pricing strategy. Allowed: %v", allowedBidPricingStrategies) -var errInvalidValueForBidPrice = errors.New("not a valid bid price") -var errBidPriceNegative = errors.New("Bid price cannot be a negative number") +var ( + errNoSuchBidPricingStrategy = fmt.Errorf("No such bid pricing strategy. Allowed: %v", allowedBidPricingStrategies) + errInvalidValueForBidPrice = errors.New("not a valid bid price") + errBidPriceNegative = errors.New("Bid price cannot be a negative number") +) func strToBidPriceScale(val string) (decimal.Decimal, error) { v, err := decimal.NewFromString(val) @@ -746,7 +747,9 @@ func doRunCmd(ctx context.Context, cmd *cobra.Command, _ []string) error { return err } - err = gwgrpc.NewServer(ctx, grpcaddr, []tls.Certificate{tlsCert}, service) + ctx = gwgrpc.ContextWithQueryClient(ctx, cl.Query()) + + err = gwgrpc.Serve(ctx, grpcaddr, []tls.Certificate{tlsCert}, service) if err != nil { return err } diff --git a/gateway/grpc/client.go b/gateway/grpc/client.go new file mode 100644 index 000000000..af657f3d9 --- /dev/null +++ b/gateway/grpc/client.go @@ -0,0 +1,57 @@ +package grpc + +import ( + "context" + "crypto/tls" + "crypto/x509" + "fmt" + + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" + + ctypes "github.com/akash-network/akash-api/go/node/cert/v1beta3" + leasev1 "github.com/akash-network/akash-api/go/provider/lease/v1" + providerv1 "github.com/akash-network/akash-api/go/provider/v1" + + "github.com/akash-network/provider/gateway/utils" +) + +type Client struct { + providerv1.ProviderRPCClient + leasev1.LeaseRPCClient + + conn *grpc.ClientConn +} + +func (c *Client) Close() error { + return c.conn.Close() +} + +func NewClient(ctx context.Context, addr string, cert tls.Certificate, cquery ctypes.QueryClient) (*Client, error) { + tlsConfig := tls.Config{ + InsecureSkipVerify: true, + Certificates: []tls.Certificate{cert}, + MinVersion: tls.VersionTLS13, + VerifyPeerCertificate: func(certificates [][]byte, _ [][]*x509.Certificate) error { + if _, err := utils.VerifyOwnerCertBytes(ctx, certificates, "", x509.ExtKeyUsageClientAuth, cquery); err != nil { + return err + } + return nil + }, + } + + conn, err := grpc.DialContext(ctx, addr, + grpc.WithBlock(), + grpc.WithTransportCredentials(credentials.NewTLS(&tlsConfig)), + ) + if err != nil { + return nil, fmt.Errorf("grpc dial context %s: %w", addr, err) + } + + return &Client{ + ProviderRPCClient: providerv1.NewProviderRPCClient(conn), + LeaseRPCClient: leasev1.NewLeaseRPCClient(conn), + + conn: conn, + }, nil +} diff --git a/gateway/grpc/context.go b/gateway/grpc/context.go new file mode 100644 index 000000000..f697c818e --- /dev/null +++ b/gateway/grpc/context.go @@ -0,0 +1,41 @@ +package grpc + +import ( + "context" + + ctypes "github.com/akash-network/akash-api/go/node/cert/v1beta3" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +type ContextKey string + +const ( + ContextKeyQueryClient = ContextKey("query-client") + ContextKeyOwner = ContextKey("owner") +) + +func ContextWithQueryClient(ctx context.Context, c ctypes.QueryClient) context.Context { + return context.WithValue(ctx, ContextKeyQueryClient, c) +} + +func MustQueryClientFromCtx(ctx context.Context) ctypes.QueryClient { + val := ctx.Value(ContextKeyQueryClient) + if val == nil { + panic("context does not have query client set") + } + + return val.(ctypes.QueryClient) +} + +func ContextWithOwner(ctx context.Context, address sdk.Address) context.Context { + return context.WithValue(ctx, ContextKeyOwner, address) +} + +func OwnerFromCtx(ctx context.Context) sdk.Address { + val := ctx.Value(ContextKeyOwner) + if val == nil { + return sdk.AccAddress{} + } + + return val.(sdk.Address) +} diff --git a/gateway/grpc/lease.go b/gateway/grpc/lease.go new file mode 100644 index 000000000..329f514ee --- /dev/null +++ b/gateway/grpc/lease.go @@ -0,0 +1,69 @@ +package grpc + +import ( + "context" + "errors" + + manifestValidation "github.com/akash-network/akash-api/go/manifest/v2beta2" + leasev1 "github.com/akash-network/akash-api/go/provider/lease/v1" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + types "github.com/akash-network/akash-api/go/node/types/v1beta3" + + "github.com/akash-network/provider" + pmanifest "github.com/akash-network/provider/manifest" +) + +type leaseV1 struct { + c provider.Client + ctx context.Context +} + +func (l *leaseV1) SendManifest(ctx context.Context, r *leasev1.SendManifestRequest) (*leasev1.SendManifestResponse, error) { + var ( + id = r.GetLeaseId().DeploymentID() + m = r.GetManifest() + ) + + // HACK(andrewhare): Existing manifests expected service resource endpoints + // to be JSON serialized as [] instead of null when determining the manifest + // version hash. This forces Go to do the right thing. + for g := range m { + for s := range m[g].Services { + if len(m[g].Services[s].Resources.Endpoints) == 0 { + m[g].Services[s].Resources.Endpoints = make(types.Endpoints, 0) + } + } + } + + err := l.c.Manifest().Submit(ctx, id, m) + if err == nil { + return &leasev1.SendManifestResponse{}, nil + } + + switch { + case errors.Is(err, manifestValidation.ErrInvalidManifest): + return nil, status.Error(codes.InvalidArgument, "invalid manifest") + case errors.Is(err, pmanifest.ErrNoLeaseForDeployment): + return nil, status.Error(codes.NotFound, "no lease for deployment") + } + + return nil, status.Errorf(codes.Internal, "manifest submit: %v", err) +} + +func (l *leaseV1) ServiceLogs(context.Context, *leasev1.ServiceLogsRequest) (*leasev1.ServiceLogsResponse, error) { + panic("unimplemented") +} + +func (l *leaseV1) ServiceStatus(context.Context, *leasev1.ServiceStatusRequest) (*leasev1.ServiceStatusResponse, error) { + panic("unimplemented") +} + +func (l *leaseV1) StreamServiceLogs(*leasev1.ServiceLogsRequest, leasev1.LeaseRPC_StreamServiceLogsServer) error { + panic("unimplemented") +} + +func (l *leaseV1) StreamServiceStatus(*leasev1.ServiceStatusRequest, leasev1.LeaseRPC_StreamServiceStatusServer) error { + panic("unimplemented") +} diff --git a/gateway/grpc/provider.go b/gateway/grpc/provider.go new file mode 100644 index 000000000..1f96a58dd --- /dev/null +++ b/gateway/grpc/provider.go @@ -0,0 +1,42 @@ +package grpc + +import ( + providerv1 "github.com/akash-network/akash-api/go/provider/v1" + "github.com/akash-network/provider" + "github.com/akash-network/provider/tools/fromctx" + ptypes "github.com/akash-network/provider/types" + "golang.org/x/net/context" + "google.golang.org/protobuf/types/known/emptypb" +) + +type providerV1 struct { + ctx context.Context + c provider.Client +} + +func (p *providerV1) GetStatus(ctx context.Context, _ *emptypb.Empty) (*providerv1.Status, error) { + return p.c.StatusV1(ctx) +} + +func (p *providerV1) StreamStatus(_ *emptypb.Empty, stream providerv1.ProviderRPC_StreamStatusServer) error { + bus, err := fromctx.PubSubFromCtx(p.ctx) + if err != nil { + return err + } + + events := bus.Sub(ptypes.PubSubTopicProviderStatus) + + for { + select { + case <-p.ctx.Done(): + return p.ctx.Err() + case <-stream.Context().Done(): + return stream.Context().Err() + case evt := <-events: + val := evt.(providerv1.Status) + if err := stream.Send(&val); err != nil { + return err + } + } + } +} diff --git a/gateway/grpc/server.go b/gateway/grpc/server.go index 941f7cc8d..e11f81814 100644 --- a/gateway/grpc/server.go +++ b/gateway/grpc/server.go @@ -1,96 +1,48 @@ package grpc import ( + "context" "crypto/tls" "crypto/x509" - "errors" "fmt" "net" "time" - "golang.org/x/net/context" "google.golang.org/grpc" "google.golang.org/grpc/credentials" "google.golang.org/grpc/keepalive" "google.golang.org/grpc/peer" - "google.golang.org/protobuf/types/known/emptypb" - - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/akash-network/akash-api/go/grpc/gogoreflection" ctypes "github.com/akash-network/akash-api/go/node/cert/v1beta3" + leasev1 "github.com/akash-network/akash-api/go/provider/lease/v1" providerv1 "github.com/akash-network/akash-api/go/provider/v1" + cmblog "github.com/tendermint/tendermint/libs/log" "github.com/akash-network/provider" + "github.com/akash-network/provider/gateway/utils" "github.com/akash-network/provider/tools/fromctx" - ptypes "github.com/akash-network/provider/types" ) -type ContextKey string - -const ( - ContextKeyQueryClient = ContextKey("query-client") - ContextKeyOwner = ContextKey("owner") +var ( + _ providerv1.ProviderRPCServer = (*server)(nil) + _ leasev1.LeaseRPCServer = (*server)(nil) ) -type grpcProviderV1 struct { - ctx context.Context - client provider.StatusClient -} - -var _ providerv1.ProviderRPCServer = (*grpcProviderV1)(nil) - -func QueryClientFromCtx(ctx context.Context) ctypes.QueryClient { - val := ctx.Value(ContextKeyQueryClient) - if val == nil { - panic("context does not have pubsub set") - } - - return val.(ctypes.QueryClient) -} - -func ContextWithOwner(ctx context.Context, address sdk.Address) context.Context { - return context.WithValue(ctx, ContextKeyOwner, address) -} - -func OwnerFromCtx(ctx context.Context) sdk.Address { - val := ctx.Value(ContextKeyOwner) - if val == nil { - return sdk.AccAddress{} - } - - return val.(sdk.Address) +type server struct { + *providerV1 + *leaseV1 } -func NewServer(ctx context.Context, endpoint string, certs []tls.Certificate, client provider.StatusClient) error { - // InsecureSkipVerify is set to true due to inability to use normal TLS verification - // certificate validation and authentication performed later in mtlsHandler - tlsConfig := &tls.Config{ - Certificates: certs, - ClientAuth: tls.RequestClientCert, - InsecureSkipVerify: true, // nolint: gosec - MinVersion: tls.VersionTLS13, - } - +func Serve(ctx context.Context, endpoint string, certs []tls.Certificate, c provider.Client) error { group, err := fromctx.ErrGroupFromCtx(ctx) if err != nil { return err } - log := fromctx.LogcFromCtx(ctx) - - grpcSrv := grpc.NewServer(grpc.Creds(credentials.NewTLS(tlsConfig)), grpc.KeepaliveEnforcementPolicy(keepalive.EnforcementPolicy{ - MinTime: 30 * time.Second, - PermitWithoutStream: false, - }), grpc.ChainUnaryInterceptor(mtlsInterceptor())) - - pRPC := &grpcProviderV1{ - ctx: ctx, - client: client, - } + grpcSrv := newServer(ctx, certs, c) - providerv1.RegisterProviderRPCServer(grpcSrv, pRPC) - gogoreflection.Register(grpcSrv) + log := fromctx.LogcFromCtx(ctx) group.Go(func() error { grpcLis, err := net.Listen("tcp", endpoint) @@ -114,102 +66,79 @@ func NewServer(ctx context.Context, endpoint string, certs []tls.Certificate, cl return nil } -func mtlsInterceptor() grpc.UnaryServerInterceptor { - return func(ctx context.Context, req interface{}, _ *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) { +func newServer(ctx context.Context, certs []tls.Certificate, c provider.Client) *grpc.Server { + // InsecureSkipVerify is set to true due to inability to use normal TLS verification + // certificate validation and authentication performed later in mtlsHandler + tlsConfig := &tls.Config{ + Certificates: certs, + ClientAuth: tls.RequestClientCert, + InsecureSkipVerify: true, // nolint: gosec + MinVersion: tls.VersionTLS13, + } + + cquery := MustQueryClientFromCtx(ctx) + + g := grpc.NewServer( + grpc.Creds( + credentials.NewTLS(tlsConfig), + ), + grpc.KeepaliveEnforcementPolicy(keepalive.EnforcementPolicy{ + MinTime: 30 * time.Second, + PermitWithoutStream: false, + }), + grpc.ChainUnaryInterceptor( + mtlsInterceptor(cquery), + errorLogInterceptor(fromctx.LogcFromCtx(ctx)), + ), + ) + + s := &server{ + providerV1: &providerV1{ + ctx: ctx, + c: c, + }, + leaseV1: &leaseV1{ + ctx: ctx, + c: c, + }, + } + + providerv1.RegisterProviderRPCServer(g, s) + leasev1.RegisterLeaseRPCServer(g, s) + gogoreflection.Register(g) + + return g +} + +func mtlsInterceptor(cquery ctypes.QueryClient) grpc.UnaryServerInterceptor { + return func(ctx context.Context, req any, _ *grpc.UnaryServerInfo, next grpc.UnaryHandler) (any, error) { if p, ok := peer.FromContext(ctx); ok { if mtls, ok := p.AuthInfo.(credentials.TLSInfo); ok { - certificates := mtls.State.PeerCertificates - - if len(certificates) > 0 { - if len(certificates) != 1 { - return nil, fmt.Errorf("tls: invalid certificate chain") // nolint: goerr113 - } - - cquery := QueryClientFromCtx(ctx) - - cert := certificates[0] - - // validation - var owner sdk.Address - if owner, err = sdk.AccAddressFromBech32(cert.Subject.CommonName); err != nil { - return nil, fmt.Errorf("tls: invalid certificate's subject common name: %w", err) - } - - // 1. CommonName in issuer and Subject must match and be as Bech32 format - if cert.Subject.CommonName != cert.Issuer.CommonName { - return nil, fmt.Errorf("tls: invalid certificate's issuer common name: %w", err) - } - - // 2. serial number must be in - if cert.SerialNumber == nil { - return nil, fmt.Errorf("tls: invalid certificate serial number: %w", err) - } - - // 3. look up certificate on chain - var resp *ctypes.QueryCertificatesResponse - resp, err = cquery.Certificates( - ctx, - &ctypes.QueryCertificatesRequest{ - Filter: ctypes.CertificateFilter{ - Owner: owner.String(), - Serial: cert.SerialNumber.String(), - State: "valid", - }, - }, - ) - if err != nil { - return nil, fmt.Errorf("tls: unable to fetch certificate from chain: %w", err) - } - if (len(resp.Certificates) != 1) || !resp.Certificates[0].Certificate.IsState(ctypes.CertificateValid) { - return nil, errors.New("tls: attempt to use non-existing or revoked certificate") // nolint: goerr113 - } - - clientCertPool := x509.NewCertPool() - clientCertPool.AddCert(cert) - - opts := x509.VerifyOptions{ - Roots: clientCertPool, - CurrentTime: time.Now(), - KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, - MaxConstraintComparisions: 0, - } - - if _, err = cert.Verify(opts); err != nil { - return nil, fmt.Errorf("tls: unable to verify certificate: %w", err) - } + owner, err := utils.VerifyOwnerCert(ctx, mtls.State.PeerCertificates, "", x509.ExtKeyUsageClientAuth, cquery) + if err != nil { + return nil, fmt.Errorf("verify cert chain: %w", err) + } + if owner != nil { ctx = ContextWithOwner(ctx, owner) } } } - return handler(ctx, req) + return next(ctx, req) } } -func (gm *grpcProviderV1) GetStatus(ctx context.Context, _ *emptypb.Empty) (*providerv1.Status, error) { - return gm.client.StatusV1(ctx) -} - -func (gm *grpcProviderV1) StreamStatus(_ *emptypb.Empty, stream providerv1.ProviderRPC_StreamStatusServer) error { - bus, err := fromctx.PubSubFromCtx(gm.ctx) - if err != nil { - return err - } - - events := bus.Sub(ptypes.PubSubTopicProviderStatus) - - for { - select { - case <-gm.ctx.Done(): - return gm.ctx.Err() - case <-stream.Context().Done(): - return stream.Context().Err() - case evt := <-events: - val := evt.(providerv1.Status) - if err := stream.Send(&val); err != nil { - return err - } +// TODO(andrewhare): Possibly replace this with +// https://github.com/grpc-ecosystem/go-grpc-middleware/tree/main/interceptors/logging +// to get full request/response logging? +func errorLogInterceptor(l cmblog.Logger) grpc.UnaryServerInterceptor { + return func(ctx context.Context, req any, i *grpc.UnaryServerInfo, next grpc.UnaryHandler) (any, error) { + resp, err := next(ctx, req) + if err != nil { + l.Error(i.FullMethod, "err", err) } + + return resp, err } } diff --git a/gateway/grpc/server_test.go b/gateway/grpc/server_test.go new file mode 100644 index 000000000..e516e2bb4 --- /dev/null +++ b/gateway/grpc/server_test.go @@ -0,0 +1,347 @@ +package grpc + +import ( + "context" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/tls" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "errors" + "math/big" + "net" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/credentials" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/types/known/emptypb" + + manifestValidation "github.com/akash-network/akash-api/go/manifest/v2beta2" + types "github.com/akash-network/akash-api/go/node/cert/v1beta3" + qmock "github.com/akash-network/akash-api/go/node/client/v1beta2/mocks" + leasev1 "github.com/akash-network/akash-api/go/provider/lease/v1" + providerv1 "github.com/akash-network/akash-api/go/provider/v1" + "github.com/akash-network/node/testutil" + + pmanifest "github.com/akash-network/provider/manifest" + mmocks "github.com/akash-network/provider/manifest/mocks" + "github.com/akash-network/provider/mocks" +) + +type asserter interface { + AssertExpectations(mock.TestingT) bool +} + +type client struct { + p providerv1.ProviderRPCClient + l leasev1.LeaseRPCClient +} + +func TestRPCs(t *testing.T) { + var ( + qclient = &qmock.QueryClient{} + com = testutil.CertificateOptionMocks(qclient) + cod = testutil.CertificateOptionDomains([]string{"localhost", "127.0.0.1"}) + ) + + var ( + crt1 = testutil.Certificate(t, testutil.AccAddress(t), com, cod) + crt2 = testutil.Certificate(t, testutil.AccAddress(t), com, cod) + ) + + qclient.EXPECT().Certificates(mock.Anything, mock.Anything).Return(&types.QueryCertificatesResponse{ + Certificates: types.CertificatesResponse{ + types.CertificateResponse{ + Certificate: types.Certificate{ + State: types.CertificateValid, + Cert: crt2.PEM.Cert, + Pubkey: crt2.PEM.Pub, + }, + Serial: crt2.Serial.String(), + }, + }, + }, nil) + + cases := []struct { + desc string + mocks func() (*mocks.Client, []asserter) + run func(context.Context, *testing.T, client) + }{ + { + desc: "GetStatus", + mocks: func() (*mocks.Client, []asserter) { + var c mocks.Client + c.EXPECT().StatusV1(mock.Anything).Return(&providerv1.Status{}, nil) + return &c, nil + }, + run: func(ctx context.Context, t *testing.T, c client) { + _, err := c.p.GetStatus(ctx, &emptypb.Empty{}) + assert.NoError(t, err) + }, + }, + { + desc: "SendManifest", + mocks: func() (*mocks.Client, []asserter) { + var ( + c mocks.Client + mc mmocks.Client + ) + + mc.EXPECT().Submit(mock.Anything, mock.Anything, mock.Anything).Return(nil) + c.EXPECT().Manifest().Return(&mc) + + return &c, []asserter{&mc} + }, + run: func(ctx context.Context, t *testing.T, c client) { + _, err := c.l.SendManifest(ctx, &leasev1.SendManifestRequest{}) + assert.NoError(t, err) + }, + }, + { + desc: "SendManifest invalid", + mocks: func() (*mocks.Client, []asserter) { + var ( + c mocks.Client + mc mmocks.Client + ) + + mc.EXPECT().Submit(mock.Anything, mock.Anything, mock.Anything).Return(manifestValidation.ErrInvalidManifest) + c.EXPECT().Manifest().Return(&mc) + + return &c, []asserter{&mc} + }, + run: func(ctx context.Context, t *testing.T, c client) { + _, err := c.l.SendManifest(ctx, &leasev1.SendManifestRequest{}) + assert.ErrorContains(t, err, "invalid manifest") + + s, ok := status.FromError(err) + assert.True(t, ok) + assert.Equal(t, codes.InvalidArgument, s.Code()) + }, + }, + { + desc: "SendManifest no lease", + mocks: func() (*mocks.Client, []asserter) { + var ( + c mocks.Client + mc mmocks.Client + ) + + mc.EXPECT().Submit(mock.Anything, mock.Anything, mock.Anything).Return(pmanifest.ErrNoLeaseForDeployment) + c.EXPECT().Manifest().Return(&mc) + + return &c, []asserter{&mc} + }, + run: func(ctx context.Context, t *testing.T, c client) { + _, err := c.l.SendManifest(ctx, &leasev1.SendManifestRequest{}) + assert.ErrorContains(t, err, "no lease") + + s, ok := status.FromError(err) + assert.True(t, ok) + assert.Equal(t, codes.NotFound, s.Code()) + }, + }, + { + desc: "SendManifest internal", + mocks: func() (*mocks.Client, []asserter) { + var ( + c mocks.Client + mc mmocks.Client + ) + + mc.EXPECT().Submit(mock.Anything, mock.Anything, mock.Anything).Return(errors.New("boom")) + c.EXPECT().Manifest().Return(&mc) + + return &c, []asserter{&mc} + }, + run: func(ctx context.Context, t *testing.T, c client) { + _, err := c.l.SendManifest(ctx, &leasev1.SendManifestRequest{}) + assert.ErrorContains(t, err, "boom") + + s, ok := status.FromError(err) + assert.True(t, ok) + assert.Equal(t, codes.Internal, s.Code()) + }, + }, + } + + for _, c := range cases { + c := c + + t.Run(c.desc, func(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + ctx = ContextWithQueryClient(ctx, qclient) + + mc, as := c.mocks() + defer mc.AssertExpectations(t) + + for _, a := range as { + defer a.AssertExpectations(t) + } + + s := newServer(ctx, crt1.Cert, mc) + defer s.Stop() + + l, err := net.Listen("tcp", ":0") + require.NoError(t, err) + + go func() { + require.NoError(t, s.Serve(l)) + }() + + tlsConfig := tls.Config{ + InsecureSkipVerify: true, + Certificates: crt2.Cert, + } + + conn, err := grpc.DialContext(ctx, l.Addr().String(), + grpc.WithTransportCredentials(credentials.NewTLS(&tlsConfig))) + require.NoError(t, err) + + defer conn.Close() + + c.run(ctx, t, client{ + p: providerv1.NewProviderRPCClient(conn), + l: leasev1.NewLeaseRPCClient(conn), + }) + }) + } +} + +func TestMTLS(t *testing.T) { + var ( + qclient = &qmock.QueryClient{} + com = testutil.CertificateOptionMocks(qclient) + cod = testutil.CertificateOptionDomains([]string{"localhost", "127.0.0.1"}) + ) + + crt := testutil.Certificate(t, testutil.AccAddress(t), com, cod) + + qclient.EXPECT().Certificates(mock.Anything, mock.Anything).Return(&types.QueryCertificatesResponse{ + Certificates: types.CertificatesResponse{ + types.CertificateResponse{ + Certificate: types.Certificate{ + State: types.CertificateValid, + Cert: crt.PEM.Cert, + Pubkey: crt.PEM.Pub, + }, + Serial: crt.Serial.String(), + }, + }, + }, nil) + + cases := []struct { + desc string + cert func(*testing.T) tls.Certificate + errContains string + }{ + { + desc: "good cert", + cert: func(*testing.T) tls.Certificate { + return testutil.Certificate(t, testutil.AccAddress(t), com, cod).Cert[0] + }, + }, + { + desc: "empty chain", + cert: func(*testing.T) tls.Certificate { + return tls.Certificate{} + }, + errContains: "empty chain", + }, + { + desc: "invalid subject", + cert: func(t *testing.T) tls.Certificate { + t.Helper() + + priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + require.NoError(t, err) + + template := x509.Certificate{ + SerialNumber: new(big.Int).SetInt64(time.Now().UTC().UnixNano()), + Subject: pkix.Name{ + CommonName: "badcert", + }, + BasicConstraintsValid: true, + } + + certDer, err := x509.CreateCertificate(rand.Reader, &template, &template, priv.Public(), priv) + require.NoError(t, err) + + keyDer, err := x509.MarshalPKCS8PrivateKey(priv) + require.NoError(t, err) + + certBytes := pem.EncodeToMemory(&pem.Block{ + Type: types.PemBlkTypeCertificate, + Bytes: certDer, + }) + privBytes := pem.EncodeToMemory(&pem.Block{ + Type: types.PemBlkTypeECPrivateKey, + Bytes: keyDer, + }) + + cert, err := tls.X509KeyPair(certBytes, privBytes) + require.NoError(t, err) + + return cert + }, + errContains: "invalid certificate's subject", + }, + } + + for _, c := range cases { + c := c + + t.Run(c.desc, func(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + ctx = ContextWithQueryClient(ctx, qclient) + + var ( + m mocks.Client + mc mmocks.Client + ) + + mc.EXPECT().Submit(mock.Anything, mock.Anything, mock.Anything).Return(nil) + m.EXPECT().Manifest().Return(&mc) + + s := newServer(ctx, crt.Cert, &m) + defer s.Stop() + + l, err := net.Listen("tcp", ":0") + require.NoError(t, err) + + go func() { + require.NoError(t, s.Serve(l)) + }() + + tlsConfig := tls.Config{ + InsecureSkipVerify: true, + Certificates: []tls.Certificate{c.cert(t)}, + } + + conn, err := grpc.DialContext(ctx, l.Addr().String(), + grpc.WithTransportCredentials(credentials.NewTLS(&tlsConfig))) + require.NoError(t, err) + + defer conn.Close() + + _, err = leasev1.NewLeaseRPCClient(conn).SendManifest(ctx, &leasev1.SendManifestRequest{}) + if c.errContains != "" { + assert.ErrorContains(t, err, c.errContains) + } else { + assert.NoError(t, err) + } + }) + } +} diff --git a/gateway/rest/client.go b/gateway/rest/client.go index 44b9f80dc..133603078 100644 --- a/gateway/rest/client.go +++ b/gateway/rest/client.go @@ -35,6 +35,7 @@ import ( "github.com/akash-network/provider" cltypes "github.com/akash-network/provider/cluster/types/v1beta3" + "github.com/akash-network/provider/gateway/utils" ) const ( @@ -212,8 +213,10 @@ type ClaimsV1 struct { CertSerialNumber string `json:"cert_serial_number"` } -var errRequiredCertSerialNum = errors.New("cert_serial_number must be present in claims") -var errNonNumericCertSerialNum = errors.New("cert_serial_number must be numeric in claims") +var ( + errRequiredCertSerialNum = errors.New("cert_serial_number must be present in claims") + errNonNumericCertSerialNum = errors.New("cert_serial_number must be numeric in claims") +) func (c *ClientCustomClaims) Valid() error { _, err := sdk.AccAddressFromBech32(c.Subject) @@ -293,63 +296,18 @@ func (c *client) verifyPeerCertificate(certificates [][]byte, _ [][]*x509.Certif return errors.Errorf("tls: invalid certificate chain") } - cert, err := x509.ParseCertificate(certificates[0]) - if err != nil { - return errors.Wrap(err, "tls: failed to parse certificate") - } - - // validation - var prov sdk.Address - if prov, err = sdk.AccAddressFromBech32(cert.Subject.CommonName); err != nil { - return errors.Wrap(err, "tls: invalid certificate's subject common name") - } - - // 1. CommonName in issuer and Subject must be the same - if cert.Subject.CommonName != cert.Issuer.CommonName { - return errors.Wrap(err, "tls: invalid certificate's issuer common name") - } - - if !c.addr.Equals(prov) { - return errors.Errorf("tls: hijacked certificate") - } - - // 2. serial number must be in - if cert.SerialNumber == nil { - return errors.Wrap(err, "tls: invalid certificate serial number") - } - - // 3. look up certificate on chain. it must not be revoked - var resp *ctypes.QueryCertificatesResponse - resp, err = c.cclient.Certificates( + prov, err := utils.VerifyOwnerCertBytes( context.Background(), - &ctypes.QueryCertificatesRequest{ - Filter: ctypes.CertificateFilter{ - Owner: prov.String(), - Serial: cert.SerialNumber.String(), - State: "valid", - }, - }, - ) + certificates, + c.host.Hostname(), + x509.ExtKeyUsageServerAuth, + c.cclient) if err != nil { - return errors.Wrap(err, "tls: unable to fetch certificate from chain") - } - if (len(resp.Certificates) != 1) || !resp.Certificates[0].Certificate.IsState(ctypes.CertificateValid) { - return errors.New("tls: attempt to use non-existing or revoked certificate") - } - - certPool := x509.NewCertPool() - certPool.AddCert(cert) - - opts := x509.VerifyOptions{ - DNSName: c.host.Hostname(), - Roots: certPool, - CurrentTime: time.Now(), - KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, - MaxConstraintComparisions: 0, + return err } - if _, err = cert.Verify(opts); err != nil { - return errors.Wrap(err, "tls: unable to verify certificate") + if !c.addr.Equals(prov) { + return errors.New("tls: hijacked certificate") } return nil @@ -713,8 +671,8 @@ func (c *client) LeaseLogs(ctx context.Context, id mtypes.LeaseID, services string, follow bool, - tailLines int64) (*ServiceLogs, error) { - + tailLines int64, +) (*ServiceLogs, error) { endpoint, err := url.Parse(c.host.String() + "/" + serviceLogsPath(id)) if err != nil { return nil, err diff --git a/gateway/utils/utils.go b/gateway/utils/utils.go index e030e4c69..596e548e1 100644 --- a/gateway/utils/utils.go +++ b/gateway/utils/utils.go @@ -4,6 +4,7 @@ import ( "context" "crypto/tls" "crypto/x509" + "fmt" "time" "github.com/pkg/errors" @@ -22,64 +23,8 @@ func NewServerTLSConfig(ctx context.Context, certs []tls.Certificate, cquery cty InsecureSkipVerify: true, // nolint: gosec MinVersion: tls.VersionTLS13, VerifyPeerCertificate: func(certificates [][]byte, _ [][]*x509.Certificate) error { - if len(certificates) > 0 { - if len(certificates) != 1 { - return errors.Errorf("tls: invalid certificate chain") - } - - cert, err := x509.ParseCertificate(certificates[0]) - if err != nil { - return errors.Wrap(err, "tls: failed to parse certificate") - } - - // validation - var owner sdk.Address - if owner, err = sdk.AccAddressFromBech32(cert.Subject.CommonName); err != nil { - return errors.Wrap(err, "tls: invalid certificate's subject common name") - } - - // 1. CommonName in issuer and Subject must match and be as Bech32 format - if cert.Subject.CommonName != cert.Issuer.CommonName { - return errors.Wrap(err, "tls: invalid certificate's issuer common name") - } - - // 2. serial number must be in - if cert.SerialNumber == nil { - return errors.Wrap(err, "tls: invalid certificate serial number") - } - - // 3. look up certificate on chain - var resp *ctypes.QueryCertificatesResponse - resp, err = cquery.Certificates( - ctx, - &ctypes.QueryCertificatesRequest{ - Filter: ctypes.CertificateFilter{ - Owner: owner.String(), - Serial: cert.SerialNumber.String(), - State: "valid", - }, - }, - ) - if err != nil { - return errors.Wrap(err, "tls: unable to fetch certificate from chain") - } - if (len(resp.Certificates) != 1) || !resp.Certificates[0].Certificate.IsState(ctypes.CertificateValid) { - return errors.New("tls: attempt to use non-existing or revoked certificate") - } - - clientCertPool := x509.NewCertPool() - clientCertPool.AddCert(cert) - - opts := x509.VerifyOptions{ - Roots: clientCertPool, - CurrentTime: time.Now(), - KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, - MaxConstraintComparisions: 0, - } - - if _, err = cert.Verify(opts); err != nil { - return errors.Wrap(err, "tls: unable to verify certificate") - } + if _, err := VerifyOwnerCertBytes(ctx, certificates, "", x509.ExtKeyUsageClientAuth, cquery); err != nil { + return err } return nil }, @@ -87,3 +32,84 @@ func NewServerTLSConfig(ctx context.Context, certs []tls.Certificate, cquery cty return cfg, nil } + +func VerifyOwnerCertBytes(ctx context.Context, chain [][]byte, dnsName string, usage x509.ExtKeyUsage, cquery ctypes.QueryClient) (sdk.Address, error) { + if len(chain) == 0 { + return nil, nil + } + + if len(chain) > 1 { + return nil, errors.Errorf("tls: invalid certificate chain") + } + + c, err := x509.ParseCertificate(chain[0]) + if err != nil { + return nil, fmt.Errorf("tls: failed to parse certificate: %w", err) + } + + return VerifyOwnerCert(ctx, []*x509.Certificate{c}, dnsName, usage, cquery) +} + +func VerifyOwnerCert(ctx context.Context, chain []*x509.Certificate, dnsName string, usage x509.ExtKeyUsage, cquery ctypes.QueryClient) (sdk.Address, error) { + if len(chain) == 0 { + return nil, errors.Errorf("tls: empty chain") + } + + if len(chain) > 1 { + return nil, errors.Errorf("tls: invalid certificate chain") + } + + c := chain[0] + + // validation + owner, err := sdk.AccAddressFromBech32(c.Subject.CommonName) + if err != nil { + return nil, fmt.Errorf("tls: invalid certificate's subject common name: %w", err) + } + + // 1. CommonName in issuer and Subject must match and be as Bech32 format + if c.Subject.CommonName != c.Issuer.CommonName { + return nil, fmt.Errorf("tls: invalid certificate's issuer common name: %w", err) + } + + // 2. serial number must be in + if c.SerialNumber == nil { + return nil, fmt.Errorf("tls: invalid certificate serial number: %w", err) + } + + // 3. look up certificate on chain + var resp *ctypes.QueryCertificatesResponse + resp, err = cquery.Certificates( + ctx, + &ctypes.QueryCertificatesRequest{ + Filter: ctypes.CertificateFilter{ + Owner: owner.String(), + Serial: c.SerialNumber.String(), + State: "valid", + }, + }, + ) + if err != nil { + return nil, fmt.Errorf("tls: unable to fetch certificate from chain: %w", err) + } + if (len(resp.Certificates) != 1) || !resp.Certificates[0].Certificate.IsState(ctypes.CertificateValid) { + return nil, fmt.Errorf("tls: attempt to use non-existing or revoked certificate: %w", err) + } + + clientCertPool := x509.NewCertPool() + clientCertPool.AddCert(c) + + opts := x509.VerifyOptions{ + DNSName: dnsName, + Roots: clientCertPool, + CurrentTime: time.Now(), + KeyUsages: []x509.ExtKeyUsage{usage}, + MaxConstraintComparisions: 0, + } + + if _, err = c.Verify(opts); err != nil { + return nil, fmt.Errorf("tls: unable to verify certificate: %w", err) + } + + return owner, nil +} diff --git a/go.mod b/go.mod index 409bdb218..815eba16e 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( github.com/go-logr/logr v1.2.4 github.com/go-logr/zapr v1.2.4 github.com/golang-jwt/jwt/v4 v4.5.0 - github.com/google/uuid v1.4.0 + github.com/google/uuid v1.6.0 github.com/gorilla/context v1.1.1 github.com/gorilla/mux v1.8.0 github.com/gorilla/websocket v1.5.0 @@ -36,10 +36,10 @@ require ( github.com/troian/pubsub v0.1.1 github.com/vektra/mockery/v2 v2.40.2 go.uber.org/zap v1.24.0 - golang.org/x/net v0.20.0 + golang.org/x/net v0.22.0 golang.org/x/sync v0.6.0 - google.golang.org/grpc v1.61.0 - google.golang.org/protobuf v1.32.0 + google.golang.org/grpc v1.62.1 + google.golang.org/protobuf v1.33.0 gopkg.in/yaml.v3 v3.0.1 k8s.io/api v0.26.1 k8s.io/apimachinery v0.26.1 @@ -73,7 +73,7 @@ require ( cosmossdk.io/depinject v1.0.0-alpha.3 // indirect filippo.io/edwards25519 v1.0.0 // indirect github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect - github.com/99designs/keyring v1.2.1 // indirect + github.com/99designs/keyring v1.2.2 // indirect github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/BurntSushi/toml v1.2.1 // indirect github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d // indirect @@ -87,6 +87,7 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect + github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 // indirect github.com/cenkalti/backoff/v3 v3.2.2 // indirect github.com/cespare/xxhash v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect @@ -109,14 +110,14 @@ require ( github.com/cosmos/ibc-go/v4 v4.4.2 // indirect github.com/cosmos/ledger-cosmos-go v0.12.2 // indirect github.com/creachadair/taskgroup v0.3.2 // indirect - github.com/danieljoos/wincred v1.1.2 // indirect + github.com/danieljoos/wincred v1.2.1 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect github.com/dgraph-io/badger/v2 v2.2007.4 // indirect github.com/dgraph-io/ristretto v0.0.3 // indirect github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect github.com/dustin/go-humanize v1.0.0 // indirect - github.com/dvsekhvalnov/jose2go v1.5.0 // indirect + github.com/dvsekhvalnov/jose2go v1.6.0 // indirect github.com/edwingeng/deque/v2 v2.1.1 // indirect github.com/emicklei/go-restful/v3 v3.10.0 // indirect github.com/evanphx/json-patch v5.6.0+incompatible // indirect @@ -134,7 +135,7 @@ require ( github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect github.com/gogo/gateway v1.1.0 // indirect github.com/gogo/protobuf v1.3.3 // indirect - github.com/golang/protobuf v1.5.3 // indirect + github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/btree v1.1.2 // indirect github.com/google/gnostic v0.6.9 // indirect @@ -175,7 +176,7 @@ require ( github.com/hdevalence/ed25519consensus v0.0.0-20220222234857-c00d1f31bab3 // indirect github.com/huandu/xstrings v1.4.0 // indirect github.com/iancoleman/strcase v0.2.0 // indirect - github.com/imdario/mergo v0.3.13 // indirect + github.com/imdario/mergo v0.3.16 // indirect github.com/improbable-eng/grpc-web v0.14.1 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jaypipes/pcidb v1.0.0 // indirect @@ -248,20 +249,20 @@ require ( go.step.sm/crypto v0.34.0 // indirect go.uber.org/atomic v1.10.0 // indirect go.uber.org/multierr v1.9.0 // indirect - golang.org/x/crypto v0.18.0 // indirect + golang.org/x/crypto v0.21.0 // indirect golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect golang.org/x/mod v0.14.0 // indirect - golang.org/x/oauth2 v0.15.0 // indirect - golang.org/x/sys v0.16.0 // indirect - golang.org/x/term v0.16.0 // indirect + golang.org/x/oauth2 v0.16.0 // indirect + golang.org/x/sys v0.18.0 // indirect + golang.org/x/term v0.18.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.17.0 // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect google.golang.org/appengine v1.6.8 // indirect - google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f // indirect + google.golang.org/genproto v0.0.0-20240318140521-94a12d6c2237 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240325203815-454cdb8f5daa // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/square/go-jose.v2 v2.6.0 // indirect diff --git a/go.sum b/go.sum index 0af2fda9b..1db273e92 100644 --- a/go.sum +++ b/go.sum @@ -22,7 +22,7 @@ cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmW cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= -cloud.google.com/go v0.110.10 h1:LXy9GEO+timppncPIAZoOj3l58LIU9k+kn48AN7IO3Y= +cloud.google.com/go v0.112.1 h1:uJSeirPke5UNZHIb4SxfZklVSiWWVqW4oXlETwZziwM= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= @@ -30,19 +30,19 @@ cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUM cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= cloud.google.com/go/bigtable v1.2.0/go.mod h1:JcVAOl45lrTmQfLj7T6TxyMzIN/3FGGcFm+2xVAli2o= -cloud.google.com/go/compute v1.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiVlk= -cloud.google.com/go/compute v1.23.3/go.mod h1:VCgBUoMnIVIR0CscqQiPJLAG25E3ZRZMzcFZeQ+h8CI= +cloud.google.com/go/compute v1.25.1 h1:ZRpHJedLtTpKgr3RV1Fx23NuaAEN1Zfx9hw1u4aJdjU= +cloud.google.com/go/compute v1.25.1/go.mod h1:oopOIR53ly6viBYxaDhBfJwzUAxf1zE//uf3IB011ls= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= -cloud.google.com/go/iam v1.1.5 h1:1jTsCu4bcsNsE4iiqNT5SHwrDRCfRmIaaaVFhRveTJI= -cloud.google.com/go/iam v1.1.5/go.mod h1:rB6P/Ic3mykPbFio+vo7403drjlgvoWfYpJhMXEbzv8= -cloud.google.com/go/kms v1.15.5 h1:pj1sRfut2eRbD9pFRjNnPNg/CzJPuQAzUujMIM1vVeM= -cloud.google.com/go/kms v1.15.5/go.mod h1:cU2H5jnp6G2TDpUGZyqTCoy1n16fbubHZjmVXSMtwDI= -cloud.google.com/go/monitoring v1.16.3 h1:mf2SN9qSoBtIgiMA4R/y4VADPWZA7VCNJA079qLaZQ8= -cloud.google.com/go/monitoring v1.16.3/go.mod h1:KwSsX5+8PnXv5NJnICZzW2R8pWTis8ypC4zmdRD63Tw= +cloud.google.com/go/iam v1.1.7 h1:z4VHOhwKLF/+UYXAJDFwGtNF0b6gjsW1Pk9Ml0U/IoM= +cloud.google.com/go/iam v1.1.7/go.mod h1:J4PMPg8TtyurAUvSmPj8FF3EDgY1SPRZxcUGrn7WXGA= +cloud.google.com/go/kms v1.15.8 h1:szIeDCowID8th2i8XE4uRev5PMxQFqW+JjwYxL9h6xs= +cloud.google.com/go/kms v1.15.8/go.mod h1:WoUHcDjD9pluCg7pNds131awnH429QGvRM3N/4MyoVs= +cloud.google.com/go/monitoring v1.18.1 h1:0yvFXK+xQd95VKo6thndjwnJMno7c7Xw1CwMByg0B+8= +cloud.google.com/go/monitoring v1.18.1/go.mod h1:52hTzJ5XOUMRm7jYi7928aEdVxBEmGwA0EjNJXIBvt8= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -306,8 +306,9 @@ github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY github.com/btcsuite/btcd/btcutil v1.1.2 h1:XLMbX8JQEiwMcYft2EGi8zPUkoa0abKIU6/BJSRsjzQ= github.com/btcsuite/btcd/btcutil v1.1.2/go.mod h1:UR7dsSJzJUfMmFiiLlIrMq1lS9jh9EdCV7FStZSnpi0= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= -github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 h1:59Kx4K6lzOW5w6nFlA0v5+lk/6sjybR934QNHSJZPTQ= +github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= github.com/btcsuite/btcutil v0.0.0-20190207003914-4c204d697803/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= @@ -454,8 +455,8 @@ github.com/cucumber/common/gherkin/go/v22 v22.0.0/go.mod h1:3mJT10B2GGn3MvVPd3Fw github.com/cucumber/common/messages/go/v17 v17.1.1 h1:RNqopvIFyLWnKv0LfATh34SWBhXeoFTJnSrgm9cT/Ts= github.com/cucumber/common/messages/go/v17 v17.1.1/go.mod h1:bpGxb57tDE385Rb2EohgUadLkAbhoC4IyCFi89u/JQI= github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= -github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0= -github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0= +github.com/danieljoos/wincred v1.2.1 h1:dl9cBrupW8+r5250DYkYxocLeZ1Y4vB1kxgtjxw8GQs= +github.com/danieljoos/wincred v1.2.1/go.mod h1:uGaFL9fDn3OLTvzCGulzE+SzjEe5NGlh5FdCcyfPwps= github.com/dave/jennifer v1.2.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -520,8 +521,8 @@ github.com/duosecurity/duo_api_golang v0.0.0-20190308151101-6c680f768e74/go.mod github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/dvsekhvalnov/jose2go v1.5.0 h1:3j8ya4Z4kMCwT5nXIKFSV84YS+HdqSSO0VsTQxaLAeM= -github.com/dvsekhvalnov/jose2go v1.5.0/go.mod h1:QsHjhyTlD/lAVqn/NSbVZmSCGeDehTB/mPZadG+mhXU= +github.com/dvsekhvalnov/jose2go v1.6.0 h1:Y9gnSnP4qEI0+/uQkHvFXeD2PLPJeXEL+ySMEA2EjTY= +github.com/dvsekhvalnov/jose2go v1.6.0/go.mod h1:QsHjhyTlD/lAVqn/NSbVZmSCGeDehTB/mPZadG+mhXU= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= @@ -826,8 +827,8 @@ 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.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +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.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -899,8 +900,8 @@ github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= -github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= @@ -1186,8 +1187,8 @@ github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1: github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= -github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= +github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= +github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= github.com/improbable-eng/grpc-web v0.14.1 h1:NrN4PY71A6tAz2sKDvC5JCauENWp0ykG8Oq1H3cpFvw= github.com/improbable-eng/grpc-web v0.14.1/go.mod h1:zEjGHa8DAlkoOXmswrNvhUGEYQA9UI7DhrGeHR1DMGU= @@ -2047,8 +2048,8 @@ golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/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-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= -golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -2166,8 +2167,8 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= -golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= +golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190130055435-99b60b757ec1/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -2187,8 +2188,8 @@ golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.15.0 h1:s8pnnxNVzjWyrvYdFUQq5llS1PX2zhPXmccZv99h7uQ= -golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM= +golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ= +golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -2298,7 +2299,6 @@ golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210819135213-f52c844e1c1c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210909193231-528a39cd75f3/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -2312,14 +2312,14 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= -golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.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.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= -golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= +golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= +golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -2540,12 +2540,12 @@ google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaE google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 h1:wpZ8pe2x1Q3f2KyT5f8oP/fa9rHAKgFPr/HZdNuS+PQ= -google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:J7XzRzVy1+IPwWHZUzoD0IccYZIrXILAQpc+Qy9CMhY= -google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 h1:JpwMPBpFN3uKhdaekDpiNlImDdkUAyiJ6ez/uxGaUSo= -google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:0xJLfVdJqpAPl8tDg1ujOCGzx6LFLttXT5NhllGOXY4= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f h1:ultW7fxlIvee4HYrtnaRPon9HpEgFk5zYpmfMgtKB5I= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f/go.mod h1:L9KNLi232K1/xB6f7AlSX692koaRnKaWSR0stBki0Yc= +google.golang.org/genproto v0.0.0-20240318140521-94a12d6c2237 h1:PgNlNSx2Nq2/j4juYzQBG0/Zdr+WP4z5N01Vk4VYBCY= +google.golang.org/genproto v0.0.0-20240318140521-94a12d6c2237/go.mod h1:9sVD8c25Af3p0rGs7S7LLsxWKFiJt/65LdSyqXBkX/Y= +google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 h1:RFiFrvy37/mpSpdySBDrUdipW/dHwsRwh3J3+A9VgT4= +google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237/go.mod h1:Z5Iiy3jtmioajWHDGFk7CeugTyHtPvMHA4UTmUkyalE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240325203815-454cdb8f5daa h1:RBgMaUMP+6soRkik4VoN8ojR2nex2TqZwjSSogic+eo= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240325203815-454cdb8f5daa/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= @@ -2581,8 +2581,8 @@ google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQ google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= 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.61.0 h1:TOvOcuXn30kRao+gfcvsebNEa5iZIiLkisYEkf7R7o0= -google.golang.org/grpc v1.61.0/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs= +google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk= +google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= 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= @@ -2597,8 +2597,8 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= -google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -2656,7 +2656,6 @@ gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=