From 7cefd27449e34a1edae52f48b22a12bf925f2b66 Mon Sep 17 00:00:00 2001 From: Andrew Hare Date: Mon, 4 Mar 2024 17:54:57 -0700 Subject: [PATCH 01/14] chore: Add gRPC test harness --- cmd/provider-services/cmd/run.go | 2 +- gateway/grpc/server.go | 55 ++++++++++-------- gateway/grpc/server_test.go | 83 +++++++++++++++++++++++++++ gateway/grpc/testdata/ca.crt | 10 ++++ gateway/grpc/testdata/ca.key | 5 ++ gateway/grpc/testdata/localhost_1.crt | 22 +++++++ gateway/grpc/testdata/localhost_1.key | 5 ++ gateway/grpc/testdata/localhost_2.crt | 22 +++++++ gateway/grpc/testdata/localhost_2.key | 5 ++ 9 files changed, 184 insertions(+), 25 deletions(-) create mode 100644 gateway/grpc/server_test.go create mode 100644 gateway/grpc/testdata/ca.crt create mode 100644 gateway/grpc/testdata/ca.key create mode 100644 gateway/grpc/testdata/localhost_1.crt create mode 100644 gateway/grpc/testdata/localhost_1.key create mode 100644 gateway/grpc/testdata/localhost_2.crt create mode 100644 gateway/grpc/testdata/localhost_2.key diff --git a/cmd/provider-services/cmd/run.go b/cmd/provider-services/cmd/run.go index 5eeec75e..bd680306 100644 --- a/cmd/provider-services/cmd/run.go +++ b/cmd/provider-services/cmd/run.go @@ -746,7 +746,7 @@ func doRunCmd(ctx context.Context, cmd *cobra.Command, _ []string) error { return err } - err = gwgrpc.NewServer(ctx, grpcaddr, []tls.Certificate{tlsCert}, service) + err = gwgrpc.Serve(ctx, grpcaddr, []tls.Certificate{tlsCert}, service) if err != nil { return err } diff --git a/gateway/grpc/server.go b/gateway/grpc/server.go index 941f7cc8..3b708edd 100644 --- a/gateway/grpc/server.go +++ b/gateway/grpc/server.go @@ -62,35 +62,16 @@ func OwnerFromCtx(ctx context.Context) sdk.Address { return val.(sdk.Address) } -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, client provider.StatusClient) 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, - } - - providerv1.RegisterProviderRPCServer(grpcSrv, pRPC) - gogoreflection.Register(grpcSrv) + var ( + grpcSrv = newServer(ctx, certs, client) + log = fromctx.LogcFromCtx(ctx) + ) group.Go(func() error { grpcLis, err := net.Listen("tcp", endpoint) @@ -114,6 +95,32 @@ func NewServer(ctx context.Context, endpoint string, certs []tls.Certificate, cl return nil } +func newServer(ctx context.Context, certs []tls.Certificate, client provider.StatusClient) *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, + } + + 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, + } + + providerv1.RegisterProviderRPCServer(grpcSrv, pRPC) + gogoreflection.Register(grpcSrv) + + return grpcSrv +} + func mtlsInterceptor() grpc.UnaryServerInterceptor { return func(ctx context.Context, req interface{}, _ *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) { if p, ok := peer.FromContext(ctx); ok { diff --git a/gateway/grpc/server_test.go b/gateway/grpc/server_test.go new file mode 100644 index 00000000..0cc4e1e9 --- /dev/null +++ b/gateway/grpc/server_test.go @@ -0,0 +1,83 @@ +package grpc + +import ( + "context" + "crypto/tls" + "net" + "testing" + + providerv1 "github.com/akash-network/akash-api/go/provider/v1" + "github.com/akash-network/provider/mocks" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" + "google.golang.org/protobuf/types/known/emptypb" +) + +func TestRPCs(t *testing.T) { + var ( + certsServer = make([]tls.Certificate, 1) + err error + ) + + certsServer[0], err = tls.LoadX509KeyPair("testdata/localhost_1.crt", "testdata/localhost_1.key") + require.NoError(t, err) + + cases := []struct { + desc string + statusClient func() *mocks.StatusClient + run func(context.Context, *testing.T, providerv1.ProviderRPCClient) + }{ + { + desc: "GetStatus", + statusClient: func() *mocks.StatusClient { + var m mocks.StatusClient + m.EXPECT().StatusV1(mock.AnythingOfType("context.Context")).Return(nil, nil) + return &m + }, + run: func(ctx context.Context, t *testing.T, c providerv1.ProviderRPCClient) { + _, err := c.GetStatus(ctx, &emptypb.Empty{}) + assert.NoError(t, err) + }, + }, + } + + for _, c := range cases { + c := c + + t.Run(c.desc, func(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + statusClient := c.statusClient() + statusClient.AssertExpectations(t) + + s := newServer(ctx, certsServer, statusClient) + defer s.Stop() + + l, err := net.Listen("tcp", ":0") + require.NoError(t, err) + + go func() { + require.NoError(t, s.Serve(l)) + }() + + cert, err := tls.LoadX509KeyPair("testdata/localhost_2.crt", "testdata/localhost_2.key") + require.NoError(t, err) + + tlsConfig := tls.Config{ + Certificates: []tls.Certificate{cert}, + } + + conn, err := grpc.Dial(l.Addr().String(), + grpc.WithTransportCredentials(credentials.NewTLS(&tlsConfig))) + require.NoError(t, err) + + defer conn.Close() + + c.run(ctx, t, providerv1.NewProviderRPCClient(conn)) + }) + } +} diff --git a/gateway/grpc/testdata/ca.crt b/gateway/grpc/testdata/ca.crt new file mode 100644 index 00000000..fc5ba2d2 --- /dev/null +++ b/gateway/grpc/testdata/ca.crt @@ -0,0 +1,10 @@ +-----BEGIN CERTIFICATE----- +MIIBaTCCAQ+gAwIBAgIQZXGUkLcub53Qnh0bpy6b1zAKBggqhkjOPQQDAjATMREw +DwYDVQQDEwh0ZXN0ZGF0YTAeFw0yNDAzMDUwMDEzMDVaFw0zNDAzMDMwMDEzMDVa +MBMxETAPBgNVBAMTCHRlc3RkYXRhMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE +q19fwjNtBZFUXeOjjq+Pubszzf1MwV2jSofDtUvtDmKmUv9BTsEUWOzrTcc0BwvV +A9xbfaq28wgkr6xkY203QaNFMEMwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQI +MAYBAf8CAQEwHQYDVR0OBBYEFFzmFO6rfa6QsRTjtYxbM3EWzWTIMAoGCCqGSM49 +BAMCA0gAMEUCIC84Ul3CojB8Y8QMxqdvM/aRmt27x34HB7G4GQe73gAbAiEA404N +LIt+sym48xAiuN3YuDjkW+5YvizUjvppT6qmpbw= +-----END CERTIFICATE----- diff --git a/gateway/grpc/testdata/ca.key b/gateway/grpc/testdata/ca.key new file mode 100644 index 00000000..cc6fc664 --- /dev/null +++ b/gateway/grpc/testdata/ca.key @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIOBQMWVASjkv7VNxByOlgjUxvLVTQgaju+r2YnWZyE5aoAoGCCqGSM49 +AwEHoUQDQgAEq19fwjNtBZFUXeOjjq+Pubszzf1MwV2jSofDtUvtDmKmUv9BTsEU +WOzrTcc0BwvVA9xbfaq28wgkr6xkY203QQ== +-----END EC PRIVATE KEY----- diff --git a/gateway/grpc/testdata/localhost_1.crt b/gateway/grpc/testdata/localhost_1.crt new file mode 100644 index 00000000..ddf65d4b --- /dev/null +++ b/gateway/grpc/testdata/localhost_1.crt @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIBrzCCAVWgAwIBAgIRALa1PJ/v16DDcv/VqDo6kqAwCgYIKoZIzj0EAwIwEzER +MA8GA1UEAxMIdGVzdGRhdGEwHhcNMjQwMzA1MDAxNDIzWhcNMjUwMzA1MDAxNDIz +WjAUMRIwEAYDVQQDEwlsb2NhbGhvc3QwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC +AASRV1TLHrUacJpE+jrz4KQn3XNuvPyiPEANpgjhbeCmOIaiNMsPIfHCJet9BGNS +xLkd6No+aanqzb2ZWl2fVXHHo4GIMIGFMA4GA1UdDwEB/wQEAwIHgDAdBgNVHSUE +FjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwHQYDVR0OBBYEFI0BUK4dwxuvpIgxJEOL +dZK15eZoMB8GA1UdIwQYMBaAFFzmFO6rfa6QsRTjtYxbM3EWzWTIMBQGA1UdEQQN +MAuCCWxvY2FsaG9zdDAKBggqhkjOPQQDAgNIADBFAiAJQTKC/wkLyMFn+YzOIEoF +AAvWjLCjtnZ5sWNUzKl1GAIhAM1UtEyaUuEfr7l/Wg8SZpQmpZA2A3DjiRQhlc7/ ++9sM +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIBaTCCAQ+gAwIBAgIQZXGUkLcub53Qnh0bpy6b1zAKBggqhkjOPQQDAjATMREw +DwYDVQQDEwh0ZXN0ZGF0YTAeFw0yNDAzMDUwMDEzMDVaFw0zNDAzMDMwMDEzMDVa +MBMxETAPBgNVBAMTCHRlc3RkYXRhMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE +q19fwjNtBZFUXeOjjq+Pubszzf1MwV2jSofDtUvtDmKmUv9BTsEUWOzrTcc0BwvV +A9xbfaq28wgkr6xkY203QaNFMEMwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQI +MAYBAf8CAQEwHQYDVR0OBBYEFFzmFO6rfa6QsRTjtYxbM3EWzWTIMAoGCCqGSM49 +BAMCA0gAMEUCIC84Ul3CojB8Y8QMxqdvM/aRmt27x34HB7G4GQe73gAbAiEA404N +LIt+sym48xAiuN3YuDjkW+5YvizUjvppT6qmpbw= +-----END CERTIFICATE----- diff --git a/gateway/grpc/testdata/localhost_1.key b/gateway/grpc/testdata/localhost_1.key new file mode 100644 index 00000000..5e519883 --- /dev/null +++ b/gateway/grpc/testdata/localhost_1.key @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIF9QnnHUoHXafvTiJSivJvTkoTuvJu6+wEJY9L7HsjAwoAoGCCqGSM49 +AwEHoUQDQgAEkVdUyx61GnCaRPo68+CkJ91zbrz8ojxADaYI4W3gpjiGojTLDyHx +wiXrfQRjUsS5HejaPmmp6s29mVpdn1Vxxw== +-----END EC PRIVATE KEY----- diff --git a/gateway/grpc/testdata/localhost_2.crt b/gateway/grpc/testdata/localhost_2.crt new file mode 100644 index 00000000..f5bab01b --- /dev/null +++ b/gateway/grpc/testdata/localhost_2.crt @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIBrzCCAVWgAwIBAgIRAJXUyPlarUQ7njPT6Rs7QpswCgYIKoZIzj0EAwIwEzER +MA8GA1UEAxMIdGVzdGRhdGEwHhcNMjQwMzA1MDAxMzM3WhcNMjUwMzA1MDAxMzM3 +WjAUMRIwEAYDVQQDEwlsb2NhbGhvc3QwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC +AAQtATl/23HGTognrijFk2mWw4m6FimCumcRZCDNa5CQqMQfU4cefK/Npk4GBwot +GgMkvyPVeRcJft0vnJtYLzoYo4GIMIGFMA4GA1UdDwEB/wQEAwIHgDAdBgNVHSUE +FjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwHQYDVR0OBBYEFPpfKTcFnRU2CaawAVUK +ug0HOGlWMB8GA1UdIwQYMBaAFFzmFO6rfa6QsRTjtYxbM3EWzWTIMBQGA1UdEQQN +MAuCCWxvY2FsaG9zdDAKBggqhkjOPQQDAgNIADBFAiBOZxdeydP72cgCW+u2Vtqd +ojXT/m047JW2kQEvXW+UywIhAIra7u4SEWhJWVJHWIn0NpZUprz4M6Hyqo78ZbZ4 +0RPA +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIBaTCCAQ+gAwIBAgIQZXGUkLcub53Qnh0bpy6b1zAKBggqhkjOPQQDAjATMREw +DwYDVQQDEwh0ZXN0ZGF0YTAeFw0yNDAzMDUwMDEzMDVaFw0zNDAzMDMwMDEzMDVa +MBMxETAPBgNVBAMTCHRlc3RkYXRhMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE +q19fwjNtBZFUXeOjjq+Pubszzf1MwV2jSofDtUvtDmKmUv9BTsEUWOzrTcc0BwvV +A9xbfaq28wgkr6xkY203QaNFMEMwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQI +MAYBAf8CAQEwHQYDVR0OBBYEFFzmFO6rfa6QsRTjtYxbM3EWzWTIMAoGCCqGSM49 +BAMCA0gAMEUCIC84Ul3CojB8Y8QMxqdvM/aRmt27x34HB7G4GQe73gAbAiEA404N +LIt+sym48xAiuN3YuDjkW+5YvizUjvppT6qmpbw= +-----END CERTIFICATE----- diff --git a/gateway/grpc/testdata/localhost_2.key b/gateway/grpc/testdata/localhost_2.key new file mode 100644 index 00000000..8a8381be --- /dev/null +++ b/gateway/grpc/testdata/localhost_2.key @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIBaCHwSNWNC8v860eLN691PJ1RROz8U7EaRmLx/5nP1+oAoGCCqGSM49 +AwEHoUQDQgAELQE5f9txxk6IJ64oxZNplsOJuhYpgrpnEWQgzWuQkKjEH1OHHnyv +zaZOBgcKLRoDJL8j1XkXCX7dL5ybWC86GA== +-----END EC PRIVATE KEY----- From cffdf08fd865d8bedc5fd4a85ebbdae21234bdec Mon Sep 17 00:00:00 2001 From: Andrew Hare Date: Thu, 7 Mar 2024 16:10:42 -0700 Subject: [PATCH 02/14] fix: Refactor mtls logic --- cmd/provider-services/cmd/run.go | 2 +- gateway/grpc/server.go | 86 ++++------------ gateway/grpc/server_test.go | 25 +++-- gateway/grpc/testdata/ca.crt | 10 -- gateway/grpc/testdata/ca.key | 5 - gateway/grpc/testdata/localhost_1.crt | 22 ---- gateway/grpc/testdata/localhost_1.key | 5 - gateway/grpc/testdata/localhost_2.crt | 22 ---- gateway/grpc/testdata/localhost_2.key | 5 - gateway/rest/client.go | 64 +----------- gateway/utils/utils.go | 138 +++++++++++++++----------- 11 files changed, 116 insertions(+), 268 deletions(-) delete mode 100644 gateway/grpc/testdata/ca.crt delete mode 100644 gateway/grpc/testdata/ca.key delete mode 100644 gateway/grpc/testdata/localhost_1.crt delete mode 100644 gateway/grpc/testdata/localhost_1.key delete mode 100644 gateway/grpc/testdata/localhost_2.crt delete mode 100644 gateway/grpc/testdata/localhost_2.key diff --git a/cmd/provider-services/cmd/run.go b/cmd/provider-services/cmd/run.go index bd680306..d8a69b60 100644 --- a/cmd/provider-services/cmd/run.go +++ b/cmd/provider-services/cmd/run.go @@ -746,7 +746,7 @@ func doRunCmd(ctx context.Context, cmd *cobra.Command, _ []string) error { return err } - err = gwgrpc.Serve(ctx, grpcaddr, []tls.Certificate{tlsCert}, service) + err = gwgrpc.Serve(ctx, grpcaddr, []tls.Certificate{tlsCert}, service, cl.Query()) if err != nil { return err } diff --git a/gateway/grpc/server.go b/gateway/grpc/server.go index 3b708edd..a7ad4925 100644 --- a/gateway/grpc/server.go +++ b/gateway/grpc/server.go @@ -2,8 +2,6 @@ package grpc import ( "crypto/tls" - "crypto/x509" - "errors" "fmt" "net" "time" @@ -22,6 +20,7 @@ import ( providerv1 "github.com/akash-network/akash-api/go/provider/v1" "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" ) @@ -62,14 +61,14 @@ func OwnerFromCtx(ctx context.Context) sdk.Address { return val.(sdk.Address) } -func Serve(ctx context.Context, endpoint string, certs []tls.Certificate, client provider.StatusClient) error { +func Serve(ctx context.Context, endpoint string, certs []tls.Certificate, client provider.StatusClient, cquery ctypes.QueryClient) error { group, err := fromctx.ErrGroupFromCtx(ctx) if err != nil { return err } var ( - grpcSrv = newServer(ctx, certs, client) + grpcSrv = newServer(ctx, certs, client, cquery) log = fromctx.LogcFromCtx(ctx) ) @@ -95,7 +94,7 @@ func Serve(ctx context.Context, endpoint string, certs []tls.Certificate, client return nil } -func newServer(ctx context.Context, certs []tls.Certificate, client provider.StatusClient) *grpc.Server { +func newServer(ctx context.Context, certs []tls.Certificate, client provider.StatusClient, cquery ctypes.QueryClient) *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{ @@ -105,10 +104,13 @@ func newServer(ctx context.Context, certs []tls.Certificate, client provider.Sta MinVersion: tls.VersionTLS13, } - grpcSrv := grpc.NewServer(grpc.Creds(credentials.NewTLS(tlsConfig)), grpc.KeepaliveEnforcementPolicy(keepalive.EnforcementPolicy{ - MinTime: 30 * time.Second, - PermitWithoutStream: false, - }), grpc.ChainUnaryInterceptor(mtlsInterceptor())) + grpcSrv := grpc.NewServer(grpc.Creds( + credentials.NewTLS(tlsConfig)), + grpc.KeepaliveEnforcementPolicy(keepalive.EnforcementPolicy{ + MinTime: 30 * time.Second, + PermitWithoutStream: false, + }), + grpc.ChainUnaryInterceptor(mtlsInterceptor(cquery))) pRPC := &grpcProviderV1{ ctx: ctx, @@ -121,70 +123,16 @@ func newServer(ctx context.Context, certs []tls.Certificate, client provider.Sta return grpcSrv } -func mtlsInterceptor() grpc.UnaryServerInterceptor { +func mtlsInterceptor(cquery ctypes.QueryClient) grpc.UnaryServerInterceptor { return func(ctx context.Context, req interface{}, _ *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err 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.VerifyCertChain(ctx, mtls.State.PeerCertificates, cquery) + if err != nil { + return nil, fmt.Errorf("verify cert chain: %w", err) + } + if owner != nil { ctx = ContextWithOwner(ctx, owner) } } diff --git a/gateway/grpc/server_test.go b/gateway/grpc/server_test.go index 0cc4e1e9..17dbd12b 100644 --- a/gateway/grpc/server_test.go +++ b/gateway/grpc/server_test.go @@ -6,7 +6,9 @@ import ( "net" "testing" + qmock "github.com/akash-network/akash-api/go/node/client/v1beta2/mocks" providerv1 "github.com/akash-network/akash-api/go/provider/v1" + "github.com/akash-network/node/testutil" "github.com/akash-network/provider/mocks" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" @@ -18,12 +20,15 @@ import ( func TestRPCs(t *testing.T) { var ( - certsServer = make([]tls.Certificate, 1) - err error + qclient qmock.QueryClient + com = testutil.CertificateOptionMocks(&qclient) + cod = testutil.CertificateOptionDomains([]string{"localhost", "127.0.0.1"}) ) - certsServer[0], err = tls.LoadX509KeyPair("testdata/localhost_1.crt", "testdata/localhost_1.key") - require.NoError(t, err) + var ( + crt1 = testutil.Certificate(t, testutil.AccAddress(t), com, cod) + crt2 = testutil.Certificate(t, testutil.AccAddress(t), com, cod) + ) cases := []struct { desc string @@ -51,10 +56,12 @@ func TestRPCs(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() + ctx = context.WithValue(ctx, ContextKeyQueryClient, &qclient) + statusClient := c.statusClient() statusClient.AssertExpectations(t) - s := newServer(ctx, certsServer, statusClient) + s := newServer(ctx, crt1.Cert, statusClient, &qclient) defer s.Stop() l, err := net.Listen("tcp", ":0") @@ -64,14 +71,12 @@ func TestRPCs(t *testing.T) { require.NoError(t, s.Serve(l)) }() - cert, err := tls.LoadX509KeyPair("testdata/localhost_2.crt", "testdata/localhost_2.key") - require.NoError(t, err) - tlsConfig := tls.Config{ - Certificates: []tls.Certificate{cert}, + InsecureSkipVerify: true, + Certificates: crt2.Cert, } - conn, err := grpc.Dial(l.Addr().String(), + conn, err := grpc.DialContext(ctx, l.Addr().String(), grpc.WithTransportCredentials(credentials.NewTLS(&tlsConfig))) require.NoError(t, err) diff --git a/gateway/grpc/testdata/ca.crt b/gateway/grpc/testdata/ca.crt deleted file mode 100644 index fc5ba2d2..00000000 --- a/gateway/grpc/testdata/ca.crt +++ /dev/null @@ -1,10 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIBaTCCAQ+gAwIBAgIQZXGUkLcub53Qnh0bpy6b1zAKBggqhkjOPQQDAjATMREw -DwYDVQQDEwh0ZXN0ZGF0YTAeFw0yNDAzMDUwMDEzMDVaFw0zNDAzMDMwMDEzMDVa -MBMxETAPBgNVBAMTCHRlc3RkYXRhMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE -q19fwjNtBZFUXeOjjq+Pubszzf1MwV2jSofDtUvtDmKmUv9BTsEUWOzrTcc0BwvV -A9xbfaq28wgkr6xkY203QaNFMEMwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQI -MAYBAf8CAQEwHQYDVR0OBBYEFFzmFO6rfa6QsRTjtYxbM3EWzWTIMAoGCCqGSM49 -BAMCA0gAMEUCIC84Ul3CojB8Y8QMxqdvM/aRmt27x34HB7G4GQe73gAbAiEA404N -LIt+sym48xAiuN3YuDjkW+5YvizUjvppT6qmpbw= ------END CERTIFICATE----- diff --git a/gateway/grpc/testdata/ca.key b/gateway/grpc/testdata/ca.key deleted file mode 100644 index cc6fc664..00000000 --- a/gateway/grpc/testdata/ca.key +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN EC PRIVATE KEY----- -MHcCAQEEIOBQMWVASjkv7VNxByOlgjUxvLVTQgaju+r2YnWZyE5aoAoGCCqGSM49 -AwEHoUQDQgAEq19fwjNtBZFUXeOjjq+Pubszzf1MwV2jSofDtUvtDmKmUv9BTsEU -WOzrTcc0BwvVA9xbfaq28wgkr6xkY203QQ== ------END EC PRIVATE KEY----- diff --git a/gateway/grpc/testdata/localhost_1.crt b/gateway/grpc/testdata/localhost_1.crt deleted file mode 100644 index ddf65d4b..00000000 --- a/gateway/grpc/testdata/localhost_1.crt +++ /dev/null @@ -1,22 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIBrzCCAVWgAwIBAgIRALa1PJ/v16DDcv/VqDo6kqAwCgYIKoZIzj0EAwIwEzER -MA8GA1UEAxMIdGVzdGRhdGEwHhcNMjQwMzA1MDAxNDIzWhcNMjUwMzA1MDAxNDIz -WjAUMRIwEAYDVQQDEwlsb2NhbGhvc3QwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC -AASRV1TLHrUacJpE+jrz4KQn3XNuvPyiPEANpgjhbeCmOIaiNMsPIfHCJet9BGNS -xLkd6No+aanqzb2ZWl2fVXHHo4GIMIGFMA4GA1UdDwEB/wQEAwIHgDAdBgNVHSUE -FjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwHQYDVR0OBBYEFI0BUK4dwxuvpIgxJEOL -dZK15eZoMB8GA1UdIwQYMBaAFFzmFO6rfa6QsRTjtYxbM3EWzWTIMBQGA1UdEQQN -MAuCCWxvY2FsaG9zdDAKBggqhkjOPQQDAgNIADBFAiAJQTKC/wkLyMFn+YzOIEoF -AAvWjLCjtnZ5sWNUzKl1GAIhAM1UtEyaUuEfr7l/Wg8SZpQmpZA2A3DjiRQhlc7/ -+9sM ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIBaTCCAQ+gAwIBAgIQZXGUkLcub53Qnh0bpy6b1zAKBggqhkjOPQQDAjATMREw -DwYDVQQDEwh0ZXN0ZGF0YTAeFw0yNDAzMDUwMDEzMDVaFw0zNDAzMDMwMDEzMDVa -MBMxETAPBgNVBAMTCHRlc3RkYXRhMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE -q19fwjNtBZFUXeOjjq+Pubszzf1MwV2jSofDtUvtDmKmUv9BTsEUWOzrTcc0BwvV -A9xbfaq28wgkr6xkY203QaNFMEMwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQI -MAYBAf8CAQEwHQYDVR0OBBYEFFzmFO6rfa6QsRTjtYxbM3EWzWTIMAoGCCqGSM49 -BAMCA0gAMEUCIC84Ul3CojB8Y8QMxqdvM/aRmt27x34HB7G4GQe73gAbAiEA404N -LIt+sym48xAiuN3YuDjkW+5YvizUjvppT6qmpbw= ------END CERTIFICATE----- diff --git a/gateway/grpc/testdata/localhost_1.key b/gateway/grpc/testdata/localhost_1.key deleted file mode 100644 index 5e519883..00000000 --- a/gateway/grpc/testdata/localhost_1.key +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN EC PRIVATE KEY----- -MHcCAQEEIF9QnnHUoHXafvTiJSivJvTkoTuvJu6+wEJY9L7HsjAwoAoGCCqGSM49 -AwEHoUQDQgAEkVdUyx61GnCaRPo68+CkJ91zbrz8ojxADaYI4W3gpjiGojTLDyHx -wiXrfQRjUsS5HejaPmmp6s29mVpdn1Vxxw== ------END EC PRIVATE KEY----- diff --git a/gateway/grpc/testdata/localhost_2.crt b/gateway/grpc/testdata/localhost_2.crt deleted file mode 100644 index f5bab01b..00000000 --- a/gateway/grpc/testdata/localhost_2.crt +++ /dev/null @@ -1,22 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIBrzCCAVWgAwIBAgIRAJXUyPlarUQ7njPT6Rs7QpswCgYIKoZIzj0EAwIwEzER -MA8GA1UEAxMIdGVzdGRhdGEwHhcNMjQwMzA1MDAxMzM3WhcNMjUwMzA1MDAxMzM3 -WjAUMRIwEAYDVQQDEwlsb2NhbGhvc3QwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC -AAQtATl/23HGTognrijFk2mWw4m6FimCumcRZCDNa5CQqMQfU4cefK/Npk4GBwot -GgMkvyPVeRcJft0vnJtYLzoYo4GIMIGFMA4GA1UdDwEB/wQEAwIHgDAdBgNVHSUE -FjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwHQYDVR0OBBYEFPpfKTcFnRU2CaawAVUK -ug0HOGlWMB8GA1UdIwQYMBaAFFzmFO6rfa6QsRTjtYxbM3EWzWTIMBQGA1UdEQQN -MAuCCWxvY2FsaG9zdDAKBggqhkjOPQQDAgNIADBFAiBOZxdeydP72cgCW+u2Vtqd -ojXT/m047JW2kQEvXW+UywIhAIra7u4SEWhJWVJHWIn0NpZUprz4M6Hyqo78ZbZ4 -0RPA ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIBaTCCAQ+gAwIBAgIQZXGUkLcub53Qnh0bpy6b1zAKBggqhkjOPQQDAjATMREw -DwYDVQQDEwh0ZXN0ZGF0YTAeFw0yNDAzMDUwMDEzMDVaFw0zNDAzMDMwMDEzMDVa -MBMxETAPBgNVBAMTCHRlc3RkYXRhMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE -q19fwjNtBZFUXeOjjq+Pubszzf1MwV2jSofDtUvtDmKmUv9BTsEUWOzrTcc0BwvV -A9xbfaq28wgkr6xkY203QaNFMEMwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQI -MAYBAf8CAQEwHQYDVR0OBBYEFFzmFO6rfa6QsRTjtYxbM3EWzWTIMAoGCCqGSM49 -BAMCA0gAMEUCIC84Ul3CojB8Y8QMxqdvM/aRmt27x34HB7G4GQe73gAbAiEA404N -LIt+sym48xAiuN3YuDjkW+5YvizUjvppT6qmpbw= ------END CERTIFICATE----- diff --git a/gateway/grpc/testdata/localhost_2.key b/gateway/grpc/testdata/localhost_2.key deleted file mode 100644 index 8a8381be..00000000 --- a/gateway/grpc/testdata/localhost_2.key +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN EC PRIVATE KEY----- -MHcCAQEEIBaCHwSNWNC8v860eLN691PJ1RROz8U7EaRmLx/5nP1+oAoGCCqGSM49 -AwEHoUQDQgAELQE5f9txxk6IJ64oxZNplsOJuhYpgrpnEWQgzWuQkKjEH1OHHnyv -zaZOBgcKLRoDJL8j1XkXCX7dL5ybWC86GA== ------END EC PRIVATE KEY----- diff --git a/gateway/rest/client.go b/gateway/rest/client.go index 44b9f80d..0000574a 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 ( @@ -289,67 +290,8 @@ func (err ClientResponseError) ClientError() string { } func (c *client) verifyPeerCertificate(certificates [][]byte, _ [][]*x509.Certificate) error { - 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 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( - context.Background(), - &ctypes.QueryCertificatesRequest{ - Filter: ctypes.CertificateFilter{ - Owner: prov.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") - } - - 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, - } - - if _, err = cert.Verify(opts); err != nil { - return errors.Wrap(err, "tls: unable to verify certificate") + if _, err := utils.VerifyCertChain(context.TODO(), certificates, c.cclient); err != nil { + return fmt.Errorf("verify cert chain: %w", err) } return nil diff --git a/gateway/utils/utils.go b/gateway/utils/utils.go index e030e4c6..429452ef 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 := VerifyCertChain(ctx, certificates, cquery); err != nil { + return fmt.Errorf("verify cert chain: %w", err) } return nil }, @@ -87,3 +32,80 @@ func NewServerTLSConfig(ctx context.Context, certs []tls.Certificate, cquery cty return cfg, nil } + +type certChain interface { + *x509.Certificate | []byte +} + +func VerifyCertChain[T certChain](ctx context.Context, chain []T, 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") + } + + var cert *x509.Certificate + + switch t := any(chain).(type) { + case []*x509.Certificate: + cert = t[0] + case [][]byte: + var err error + if cert, err = x509.ParseCertificate(t[0]); err != nil { + return nil, fmt.Errorf("tls: failed to parse certificate: %w", err) + } + } + + // validation + owner, err := sdk.AccAddressFromBech32(cert.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 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, fmt.Errorf("tls: attempt to use non-existing or revoked certificate: %w", err) + } + + 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) + } + + return owner, nil +} From 4b08c9dcf0885fe27abae6a81fd03be817bd7f91 Mon Sep 17 00:00:00 2001 From: Andrew Hare Date: Thu, 14 Mar 2024 15:16:32 -0600 Subject: [PATCH 03/14] feat: SendManifest --- cmd/provider-services/cmd/run.go | 4 +- gateway/grpc/context.go | 41 ++++++++ gateway/grpc/lease.go | 56 +++++++++++ gateway/grpc/provider.go | 42 ++++++++ gateway/grpc/server.go | 129 ++++++++++-------------- gateway/grpc/server_test.go | 163 +++++++++++++++++++++++++++---- 6 files changed, 336 insertions(+), 99 deletions(-) create mode 100644 gateway/grpc/context.go create mode 100644 gateway/grpc/lease.go create mode 100644 gateway/grpc/provider.go diff --git a/cmd/provider-services/cmd/run.go b/cmd/provider-services/cmd/run.go index d8a69b60..65244a5c 100644 --- a/cmd/provider-services/cmd/run.go +++ b/cmd/provider-services/cmd/run.go @@ -746,7 +746,9 @@ func doRunCmd(ctx context.Context, cmd *cobra.Command, _ []string) error { return err } - err = gwgrpc.Serve(ctx, grpcaddr, []tls.Certificate{tlsCert}, service, cl.Query()) + 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/context.go b/gateway/grpc/context.go new file mode 100644 index 00000000..f697c818 --- /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 00000000..78a2bcb4 --- /dev/null +++ b/gateway/grpc/lease.go @@ -0,0 +1,56 @@ +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" + + "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() + ) + + 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 00000000..1f96a58d --- /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 a7ad4925..a045fa1e 100644 --- a/gateway/grpc/server.go +++ b/gateway/grpc/server.go @@ -1,74 +1,35 @@ package grpc import ( + "context" "crypto/tls" "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" "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") ) -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) -} - -func Serve(ctx context.Context, endpoint string, certs []tls.Certificate, client provider.StatusClient, cquery ctypes.QueryClient) error { +func Serve(ctx context.Context, endpoint string, certs []tls.Certificate, c provider.Client) error { group, err := fromctx.ErrGroupFromCtx(ctx) if err != nil { return err } var ( - grpcSrv = newServer(ctx, certs, client, cquery) + grpcSrv = newServer(ctx, certs, c) log = fromctx.LogcFromCtx(ctx) ) @@ -94,7 +55,17 @@ func Serve(ctx context.Context, endpoint string, certs []tls.Certificate, client return nil } -func newServer(ctx context.Context, certs []tls.Certificate, client provider.StatusClient, cquery ctypes.QueryClient) *grpc.Server { +var ( + _ providerv1.ProviderRPCServer = (*server)(nil) + _ leasev1.LeaseRPCServer = (*server)(nil) +) + +type server struct { + *providerV1 + *leaseV1 +} + +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{ @@ -104,27 +75,42 @@ func newServer(ctx context.Context, certs []tls.Certificate, client provider.Sta MinVersion: tls.VersionTLS13, } - grpcSrv := grpc.NewServer(grpc.Creds( - credentials.NewTLS(tlsConfig)), + 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))) + grpc.ChainUnaryInterceptor( + mtlsInterceptor(cquery), + errorLogInterceptor(), + ), + ) - pRPC := &grpcProviderV1{ - ctx: ctx, - client: client, + s := &server{ + providerV1: &providerV1{ + ctx: ctx, + c: c, + }, + leaseV1: &leaseV1{ + ctx: ctx, + c: c, + }, } - providerv1.RegisterProviderRPCServer(grpcSrv, pRPC) - gogoreflection.Register(grpcSrv) + providerv1.RegisterProviderRPCServer(g, s) + leasev1.RegisterLeaseRPCServer(g, s) + gogoreflection.Register(g) - return grpcSrv + return g } func mtlsInterceptor(cquery ctypes.QueryClient) grpc.UnaryServerInterceptor { - return func(ctx context.Context, req interface{}, _ *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) { + return func(ctx context.Context, req any, _ *grpc.UnaryServerInfo, h grpc.UnaryHandler) (any, error) { if p, ok := peer.FromContext(ctx); ok { if mtls, ok := p.AuthInfo.(credentials.TLSInfo); ok { owner, err := utils.VerifyCertChain(ctx, mtls.State.PeerCertificates, cquery) @@ -138,33 +124,20 @@ func mtlsInterceptor(cquery ctypes.QueryClient) grpc.UnaryServerInterceptor { } } - return handler(ctx, req) + return h(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() grpc.UnaryServerInterceptor { + return func(ctx context.Context, req any, i *grpc.UnaryServerInfo, h grpc.UnaryHandler) (any, error) { + resp, err := h(ctx, req) + if err != nil { + fromctx.LogcFromCtx(ctx).Error(i.FullMethod, "err", err) } + + return resp, err } } diff --git a/gateway/grpc/server_test.go b/gateway/grpc/server_test.go index 17dbd12b..5bdbac5e 100644 --- a/gateway/grpc/server_test.go +++ b/gateway/grpc/server_test.go @@ -3,25 +3,44 @@ package grpc import ( "context" "crypto/tls" + "errors" "net" "testing" - qmock "github.com/akash-network/akash-api/go/node/client/v1beta2/mocks" - providerv1 "github.com/akash-network/akash-api/go/provider/v1" - "github.com/akash-network/node/testutil" - "github.com/akash-network/provider/mocks" "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) + qclient = &qmock.QueryClient{} + com = testutil.CertificateOptionMocks(qclient) cod = testutil.CertificateOptionDomains([]string{"localhost", "127.0.0.1"}) ) @@ -30,23 +49,120 @@ func TestRPCs(t *testing.T) { 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 - statusClient func() *mocks.StatusClient - run func(context.Context, *testing.T, providerv1.ProviderRPCClient) + desc string + mocks func() (*mocks.Client, []asserter) + run func(context.Context, *testing.T, client) }{ { desc: "GetStatus", - statusClient: func() *mocks.StatusClient { - var m mocks.StatusClient - m.EXPECT().StatusV1(mock.AnythingOfType("context.Context")).Return(nil, nil) - return &m + 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 providerv1.ProviderRPCClient) { - _, err := c.GetStatus(ctx, &emptypb.Empty{}) + 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 { @@ -56,12 +172,16 @@ func TestRPCs(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - ctx = context.WithValue(ctx, ContextKeyQueryClient, &qclient) + ctx = ContextWithQueryClient(ctx, qclient) - statusClient := c.statusClient() - statusClient.AssertExpectations(t) + mc, as := c.mocks() + defer mc.AssertExpectations(t) + + for _, a := range as { + defer a.AssertExpectations(t) + } - s := newServer(ctx, crt1.Cert, statusClient, &qclient) + s := newServer(ctx, crt1.Cert, mc) defer s.Stop() l, err := net.Listen("tcp", ":0") @@ -82,7 +202,10 @@ func TestRPCs(t *testing.T) { defer conn.Close() - c.run(ctx, t, providerv1.NewProviderRPCClient(conn)) + c.run(ctx, t, client{ + p: providerv1.NewProviderRPCClient(conn), + l: leasev1.NewLeaseRPCClient(conn), + }) }) } } From ffd6e53242cf11a01541f7ca0aac1eb74f134617 Mon Sep 17 00:00:00 2001 From: Andrew Hare Date: Mon, 18 Mar 2024 10:33:43 -0600 Subject: [PATCH 04/14] fix: akash-api v0.0.59 --- go.mod | 16 ++++++++-------- go.sum | 38 +++++++++++++++++++------------------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/go.mod b/go.mod index 409bdb21..d0b97900 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 @@ -38,8 +38,8 @@ require ( go.uber.org/zap v1.24.0 golang.org/x/net v0.20.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 @@ -134,7 +134,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 @@ -251,7 +251,7 @@ require ( golang.org/x/crypto v0.18.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/oauth2 v0.16.0 // indirect golang.org/x/sys v0.16.0 // indirect golang.org/x/term v0.16.0 // indirect golang.org/x/text v0.14.0 // indirect @@ -259,9 +259,9 @@ require ( 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-20240123012728-ef4313101c80 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 // 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 0af2fda9..15f1b994 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.0 h1:tpFCD7hpHFlQ8yPwT3x+QeXqc2T6+n6T+hmABHfDUSM= 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= @@ -41,8 +41,8 @@ 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/monitoring v1.17.0 h1:blrdvF0MkPPivSO041ihul7rFMhXdVp8Uq7F59DKXTU= +cloud.google.com/go/monitoring v1.17.0/go.mod h1:KwSsX5+8PnXv5NJnICZzW2R8pWTis8ypC4zmdRD63Tw= 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= @@ -826,8 +826,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 +899,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= @@ -2187,8 +2187,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= @@ -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-20240123012728-ef4313101c80 h1:KAeGQVN3M9nD0/bQXnr/ClcEMJ968gUXJQ9pwfSynuQ= +google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80/go.mod h1:cc8bqMqtv9gMOr0zHg2Vzff5ULhhL2IXP4sbcn32Dro= +google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80 h1:Lj5rbfG876hIAYFjqiJnPHfhXbv+nzTWfm04Fg/XSVU= +google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80/go.mod h1:4jWUdICTdgc3Ibxmr8nAJiiLHwQBY0UI0XZcEMaFKaA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 h1:AjyfHzEPEFp/NpvfN5g+KDla3EMojjhRVZc1i7cj+oM= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s= 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= From 1804bd90fa499ce012fa0654501cb28897693b1f Mon Sep 17 00:00:00 2001 From: Andrew Hare Date: Wed, 20 Mar 2024 13:27:55 -0600 Subject: [PATCH 05/14] fix: Make key usage a parameter --- gateway/grpc/server.go | 3 ++- gateway/rest/client.go | 24 ++++++++++++++++++------ gateway/utils/utils.go | 15 +++++++++++---- 3 files changed, 31 insertions(+), 11 deletions(-) diff --git a/gateway/grpc/server.go b/gateway/grpc/server.go index a045fa1e..038e0ee9 100644 --- a/gateway/grpc/server.go +++ b/gateway/grpc/server.go @@ -3,6 +3,7 @@ package grpc import ( "context" "crypto/tls" + "crypto/x509" "fmt" "net" "time" @@ -113,7 +114,7 @@ func mtlsInterceptor(cquery ctypes.QueryClient) grpc.UnaryServerInterceptor { return func(ctx context.Context, req any, _ *grpc.UnaryServerInfo, h grpc.UnaryHandler) (any, error) { if p, ok := peer.FromContext(ctx); ok { if mtls, ok := p.AuthInfo.(credentials.TLSInfo); ok { - owner, err := utils.VerifyCertChain(ctx, mtls.State.PeerCertificates, cquery) + owner, err := utils.VerifyCertChain(ctx, mtls.State.PeerCertificates, "", x509.ExtKeyUsageServerAuth, cquery) if err != nil { return nil, fmt.Errorf("verify cert chain: %w", err) } diff --git a/gateway/rest/client.go b/gateway/rest/client.go index 0000574a..93c70834 100644 --- a/gateway/rest/client.go +++ b/gateway/rest/client.go @@ -213,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) @@ -290,8 +292,18 @@ func (err ClientResponseError) ClientError() string { } func (c *client) verifyPeerCertificate(certificates [][]byte, _ [][]*x509.Certificate) error { - if _, err := utils.VerifyCertChain(context.TODO(), certificates, c.cclient); err != nil { - return fmt.Errorf("verify cert chain: %w", err) + prov, err := utils.VerifyCertChain( + context.Background(), + certificates, + c.host.Hostname(), + x509.ExtKeyUsageServerAuth, + c.cclient) + if err != nil { + return err + } + + if !c.addr.Equals(prov) { + return errors.New("tls: hijacked certificate") } return nil @@ -655,8 +667,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 429452ef..bd852ee3 100644 --- a/gateway/utils/utils.go +++ b/gateway/utils/utils.go @@ -23,8 +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 _, err := VerifyCertChain(ctx, certificates, cquery); err != nil { - return fmt.Errorf("verify cert chain: %w", err) + if _, err := VerifyCertChain(ctx, certificates, "", x509.ExtKeyUsageClientAuth, cquery); err != nil { + return err } return nil }, @@ -37,7 +37,13 @@ type certChain interface { *x509.Certificate | []byte } -func VerifyCertChain[T certChain](ctx context.Context, chain []T, cquery ctypes.QueryClient) (sdk.Address, error) { +func VerifyCertChain[T certChain]( + ctx context.Context, + chain []T, + dnsName string, + usage x509.ExtKeyUsage, + cquery ctypes.QueryClient, +) (sdk.Address, error) { if len(chain) == 0 { return nil, nil } @@ -97,9 +103,10 @@ func VerifyCertChain[T certChain](ctx context.Context, chain []T, cquery ctypes. clientCertPool.AddCert(cert) opts := x509.VerifyOptions{ + DNSName: dnsName, Roots: clientCertPool, CurrentTime: time.Now(), - KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, + KeyUsages: []x509.ExtKeyUsage{usage}, MaxConstraintComparisions: 0, } From 7f63b9daef52dc0aa35fdf839f205d4e878b86f8 Mon Sep 17 00:00:00 2001 From: Andrew Hare Date: Wed, 20 Mar 2024 13:31:11 -0600 Subject: [PATCH 06/14] fix: Update dependencies --- go.mod | 23 +++++++++++---------- go.sum | 63 +++++++++++++++++++++++++++++----------------------------- 2 files changed, 43 insertions(+), 43 deletions(-) diff --git a/go.mod b/go.mod index d0b97900..23d8b36e 100644 --- a/go.mod +++ b/go.mod @@ -36,7 +36,7 @@ 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.62.1 google.golang.org/protobuf v1.33.0 @@ -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 @@ -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.16.0 // indirect - golang.org/x/sys v0.16.0 // indirect - golang.org/x/term 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-20240123012728-ef4313101c80 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 // 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-20240318140521-94a12d6c2237 // 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 15f1b994..b2952cdd 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.112.0 h1:tpFCD7hpHFlQ8yPwT3x+QeXqc2T6+n6T+hmABHfDUSM= +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.17.0 h1:blrdvF0MkPPivSO041ihul7rFMhXdVp8Uq7F59DKXTU= -cloud.google.com/go/monitoring v1.17.0/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= @@ -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= @@ -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-20240123012728-ef4313101c80 h1:KAeGQVN3M9nD0/bQXnr/ClcEMJ968gUXJQ9pwfSynuQ= -google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80/go.mod h1:cc8bqMqtv9gMOr0zHg2Vzff5ULhhL2IXP4sbcn32Dro= -google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80 h1:Lj5rbfG876hIAYFjqiJnPHfhXbv+nzTWfm04Fg/XSVU= -google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80/go.mod h1:4jWUdICTdgc3Ibxmr8nAJiiLHwQBY0UI0XZcEMaFKaA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 h1:AjyfHzEPEFp/NpvfN5g+KDla3EMojjhRVZc1i7cj+oM= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s= +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-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/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= @@ -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= From 36dd3d05db3cfc4ca4ea589de5fefe03e304000c Mon Sep 17 00:00:00 2001 From: Andrew Hare Date: Wed, 20 Mar 2024 13:35:41 -0600 Subject: [PATCH 07/14] fix: Add specific check back in to rest client --- gateway/rest/client.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/gateway/rest/client.go b/gateway/rest/client.go index 93c70834..23b865b4 100644 --- a/gateway/rest/client.go +++ b/gateway/rest/client.go @@ -292,6 +292,10 @@ func (err ClientResponseError) ClientError() string { } func (c *client) verifyPeerCertificate(certificates [][]byte, _ [][]*x509.Certificate) error { + if len(certificates) != 1 { + return errors.Errorf("tls: invalid certificate chain") + } + prov, err := utils.VerifyCertChain( context.Background(), certificates, From 9f74f3d99a281ce36e7f948981926cf6a1777f43 Mon Sep 17 00:00:00 2001 From: Andrew Hare Date: Thu, 21 Mar 2024 14:58:19 -0600 Subject: [PATCH 08/14] fix: PR feedback --- gateway/grpc/server.go | 44 +++++++++++++++++++++--------------------- gateway/rest/client.go | 2 +- gateway/utils/utils.go | 24 +++++++++++------------ 3 files changed, 35 insertions(+), 35 deletions(-) diff --git a/gateway/grpc/server.go b/gateway/grpc/server.go index 038e0ee9..9bbfc5d7 100644 --- a/gateway/grpc/server.go +++ b/gateway/grpc/server.go @@ -17,22 +17,32 @@ import ( 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" ) +var ( + _ providerv1.ProviderRPCServer = (*server)(nil) + _ leasev1.LeaseRPCServer = (*server)(nil) +) + +type server struct { + *providerV1 + *leaseV1 +} + func Serve(ctx context.Context, endpoint string, certs []tls.Certificate, c provider.Client) error { group, err := fromctx.ErrGroupFromCtx(ctx) if err != nil { return err } - var ( - grpcSrv = newServer(ctx, certs, c) - log = fromctx.LogcFromCtx(ctx) - ) + grpcSrv := newServer(ctx, certs, c) + + log := fromctx.LogcFromCtx(ctx) group.Go(func() error { grpcLis, err := net.Listen("tcp", endpoint) @@ -56,16 +66,6 @@ func Serve(ctx context.Context, endpoint string, certs []tls.Certificate, c prov return nil } -var ( - _ providerv1.ProviderRPCServer = (*server)(nil) - _ leasev1.LeaseRPCServer = (*server)(nil) -) - -type server struct { - *providerV1 - *leaseV1 -} - 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 @@ -88,7 +88,7 @@ func newServer(ctx context.Context, certs []tls.Certificate, c provider.Client) }), grpc.ChainUnaryInterceptor( mtlsInterceptor(cquery), - errorLogInterceptor(), + errorLogInterceptor(fromctx.LogcFromCtx(ctx)), ), ) @@ -111,10 +111,10 @@ func newServer(ctx context.Context, certs []tls.Certificate, c provider.Client) } func mtlsInterceptor(cquery ctypes.QueryClient) grpc.UnaryServerInterceptor { - return func(ctx context.Context, req any, _ *grpc.UnaryServerInfo, h grpc.UnaryHandler) (any, error) { + 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 { - owner, err := utils.VerifyCertChain(ctx, mtls.State.PeerCertificates, "", x509.ExtKeyUsageServerAuth, cquery) + owner, err := utils.VerifyOwnerCert(ctx, mtls.State.PeerCertificates, "", x509.ExtKeyUsageServerAuth, cquery) if err != nil { return nil, fmt.Errorf("verify cert chain: %w", err) } @@ -125,18 +125,18 @@ func mtlsInterceptor(cquery ctypes.QueryClient) grpc.UnaryServerInterceptor { } } - return h(ctx, req) + return next(ctx, req) } } // 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() grpc.UnaryServerInterceptor { - return func(ctx context.Context, req any, i *grpc.UnaryServerInfo, h grpc.UnaryHandler) (any, error) { - resp, err := h(ctx, req) +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 { - fromctx.LogcFromCtx(ctx).Error(i.FullMethod, "err", err) + l.Error(i.FullMethod, "err", err) } return resp, err diff --git a/gateway/rest/client.go b/gateway/rest/client.go index 23b865b4..3e91070f 100644 --- a/gateway/rest/client.go +++ b/gateway/rest/client.go @@ -296,7 +296,7 @@ func (c *client) verifyPeerCertificate(certificates [][]byte, _ [][]*x509.Certif return errors.Errorf("tls: invalid certificate chain") } - prov, err := utils.VerifyCertChain( + prov, err := utils.VerifyOwnerCert( context.Background(), certificates, c.host.Hostname(), diff --git a/gateway/utils/utils.go b/gateway/utils/utils.go index bd852ee3..479c9bf8 100644 --- a/gateway/utils/utils.go +++ b/gateway/utils/utils.go @@ -23,7 +23,7 @@ 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 _, err := VerifyCertChain(ctx, certificates, "", x509.ExtKeyUsageClientAuth, cquery); err != nil { + if _, err := VerifyOwnerCert(ctx, certificates, "", x509.ExtKeyUsageClientAuth, cquery); err != nil { return err } return nil @@ -33,11 +33,11 @@ func NewServerTLSConfig(ctx context.Context, certs []tls.Certificate, cquery cty return cfg, nil } -type certChain interface { +type cert interface { *x509.Certificate | []byte } -func VerifyCertChain[T certChain]( +func VerifyOwnerCert[T cert]( ctx context.Context, chain []T, dnsName string, @@ -52,31 +52,31 @@ func VerifyCertChain[T certChain]( return nil, errors.Errorf("tls: invalid certificate chain") } - var cert *x509.Certificate + var c *x509.Certificate switch t := any(chain).(type) { case []*x509.Certificate: - cert = t[0] + c = t[0] case [][]byte: var err error - if cert, err = x509.ParseCertificate(t[0]); err != nil { + if c, err = x509.ParseCertificate(t[0]); err != nil { return nil, fmt.Errorf("tls: failed to parse certificate: %w", err) } } // validation - owner, err := sdk.AccAddressFromBech32(cert.Subject.CommonName) + 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 cert.Subject.CommonName != cert.Issuer.CommonName { + 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 cert.SerialNumber == nil { + if c.SerialNumber == nil { return nil, fmt.Errorf("tls: invalid certificate serial number: %w", err) } @@ -87,7 +87,7 @@ func VerifyCertChain[T certChain]( &ctypes.QueryCertificatesRequest{ Filter: ctypes.CertificateFilter{ Owner: owner.String(), - Serial: cert.SerialNumber.String(), + Serial: c.SerialNumber.String(), State: "valid", }, }, @@ -100,7 +100,7 @@ func VerifyCertChain[T certChain]( } clientCertPool := x509.NewCertPool() - clientCertPool.AddCert(cert) + clientCertPool.AddCert(c) opts := x509.VerifyOptions{ DNSName: dnsName, @@ -110,7 +110,7 @@ func VerifyCertChain[T certChain]( MaxConstraintComparisions: 0, } - if _, err = cert.Verify(opts); err != nil { + if _, err = c.Verify(opts); err != nil { return nil, fmt.Errorf("tls: unable to verify certificate: %w", err) } From 6af522c58edab51519855c7e935a23806d7ea4c9 Mon Sep 17 00:00:00 2001 From: Andrew Hare Date: Thu, 21 Mar 2024 15:44:09 -0600 Subject: [PATCH 09/14] fix: VerifyOwnerCertBytes wrapper function --- gateway/rest/client.go | 2 +- gateway/utils/utils.go | 39 ++++++++++++++++++--------------------- 2 files changed, 19 insertions(+), 22 deletions(-) diff --git a/gateway/rest/client.go b/gateway/rest/client.go index 3e91070f..13360307 100644 --- a/gateway/rest/client.go +++ b/gateway/rest/client.go @@ -296,7 +296,7 @@ func (c *client) verifyPeerCertificate(certificates [][]byte, _ [][]*x509.Certif return errors.Errorf("tls: invalid certificate chain") } - prov, err := utils.VerifyOwnerCert( + prov, err := utils.VerifyOwnerCertBytes( context.Background(), certificates, c.host.Hostname(), diff --git a/gateway/utils/utils.go b/gateway/utils/utils.go index 479c9bf8..7abe3831 100644 --- a/gateway/utils/utils.go +++ b/gateway/utils/utils.go @@ -23,7 +23,7 @@ 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 _, err := VerifyOwnerCert(ctx, certificates, "", x509.ExtKeyUsageClientAuth, cquery); err != nil { + if _, err := VerifyOwnerCertBytes(ctx, certificates, "", x509.ExtKeyUsageClientAuth, cquery); err != nil { return err } return nil @@ -33,17 +33,7 @@ func NewServerTLSConfig(ctx context.Context, certs []tls.Certificate, cquery cty return cfg, nil } -type cert interface { - *x509.Certificate | []byte -} - -func VerifyOwnerCert[T cert]( - ctx context.Context, - chain []T, - dnsName string, - usage x509.ExtKeyUsage, - cquery ctypes.QueryClient, -) (sdk.Address, error) { +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 } @@ -52,18 +42,25 @@ func VerifyOwnerCert[T cert]( return nil, errors.Errorf("tls: invalid certificate chain") } - var c *x509.Certificate + c, err := x509.ParseCertificate(chain[0]) + if err != nil { + return nil, fmt.Errorf("tls: failed to parse certificate: %w", err) + } - switch t := any(chain).(type) { - case []*x509.Certificate: - c = t[0] - case [][]byte: - var err error - if c, err = x509.ParseCertificate(t[0]); 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, nil + } + + 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 { From a38517f6133fb5ba13e216c1b9bf37c0755cd2e0 Mon Sep 17 00:00:00 2001 From: Andrew Hare Date: Thu, 28 Mar 2024 00:05:47 -0600 Subject: [PATCH 10/14] fix: gRPC client --- cmd/provider-services/cmd/manifest.go | 72 +++++++++++++++++---------- cmd/provider-services/cmd/run.go | 14 +++--- gateway/grpc/client.go | 56 +++++++++++++++++++++ 3 files changed, 108 insertions(+), 34 deletions(-) create mode 100644 gateway/grpc/client.go diff --git a/cmd/provider-services/cmd/manifest.go b/cmd/provider-services/cmd/manifest.go index 56379dfe..45378d68 100644 --- a/cmd/provider-services/cmd/manifest.go +++ b/cmd/provider-services/cmd/manifest.go @@ -2,9 +2,10 @@ package cmd import ( "bytes" - "crypto/tls" + "context" "encoding/json" "fmt" + "time" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -14,16 +15,16 @@ 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" - gwrest "github.com/akash-network/provider/gateway/rest" + gwgrpc "github.com/akash-network/provider/gateway/grpc" ) -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 +95,50 @@ 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(context.Background(), &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 + c, err := gwgrpc.NewClient(ctx, prov.GetProvider().HostURI, cert, cl) + if err != nil { + return fmt.Errorf("new grpc conn: %w", err) } - res.Status = "FAIL" - submitFailed = true - } - results[i] = res + defer c.Close() + + res := result{ + Provider: provAddr, + Status: "PASS", + } + + if _, err = c.SendManifest(ctx, &leasev1.SendManifestRequest{ + LeaseId: lid, + Manifest: mani, + }); err != nil { + res.Error = err.Error() + res.Status = "FAIL" + submitFailed = true + } + + results[i] = res + + return nil + }() + if err != nil { + return err + } } buf := &bytes.Buffer{} @@ -146,7 +165,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 65244a5c..19394bbd 100644 --- a/cmd/provider-services/cmd/run.go +++ b/cmd/provider-services/cmd/run.go @@ -109,9 +109,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 +204,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:8442", "Gateway gRPC listen address") if err := viper.BindPFlag(FlagGatewayGRPCListenAddress, cmd.Flags().Lookup(FlagGatewayGRPCListenAddress)); err != nil { panic(err) } @@ -420,9 +418,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) diff --git a/gateway/grpc/client.go b/gateway/grpc/client.go new file mode 100644 index 00000000..8da2ba21 --- /dev/null +++ b/gateway/grpc/client.go @@ -0,0 +1,56 @@ +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}, + 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 +} From 01090ff8f655c8f588ff25816dffbcd31c2a033f Mon Sep 17 00:00:00 2001 From: Andrew Hare Date: Sat, 30 Mar 2024 02:15:19 -0600 Subject: [PATCH 11/14] fix: REST fallback --- cmd/provider-services/cmd/grpc.go | 15 ++++++++++ cmd/provider-services/cmd/manifest.go | 42 ++++++++++++++++++++------- cmd/provider-services/cmd/run.go | 5 ++-- go.mod | 3 +- go.sum | 6 ++-- 5 files changed, 52 insertions(+), 19 deletions(-) create mode 100644 cmd/provider-services/cmd/grpc.go diff --git a/cmd/provider-services/cmd/grpc.go b/cmd/provider-services/cmd/grpc.go new file mode 100644 index 00000000..c3a62e8c --- /dev/null +++ b/cmd/provider-services/cmd/grpc.go @@ -0,0 +1,15 @@ +package cmd + +import ( + "fmt" + "net" +) + +func grpcURI(hostURI string) (string, error) { + host, _, err := net.SplitHostPort(hostURI) + if err != nil { + return "", fmt.Errorf("split host port: %w", err) + } + + return net.JoinHostPort(host, "8442"), nil +} diff --git a/cmd/provider-services/cmd/manifest.go b/cmd/provider-services/cmd/manifest.go index 45378d68..495a4575 100644 --- a/cmd/provider-services/cmd/manifest.go +++ b/cmd/provider-services/cmd/manifest.go @@ -3,6 +3,7 @@ package cmd import ( "bytes" "context" + "crypto/tls" "encoding/json" "fmt" "time" @@ -22,6 +23,7 @@ import ( 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") @@ -111,25 +113,43 @@ func doSendManifest(cmd *cobra.Command, sdlpath string) error { return fmt.Errorf("query client provider: %w", err) } - c, err := gwgrpc.NewClient(ctx, prov.GetProvider().HostURI, cert, cl) + hostURIgRPC, err := grpcURI(prov.GetProvider().HostURI) if err != nil { - return fmt.Errorf("new grpc conn: %w", err) + return fmt.Errorf("grpc uri: %w", err) } - defer c.Close() - res := result{ Provider: provAddr, Status: "PASS", } - if _, err = c.SendManifest(ctx, &leasev1.SendManifestRequest{ - LeaseId: lid, - Manifest: mani, - }); err != nil { - res.Error = err.Error() - res.Status = "FAIL" - submitFailed = true + 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 diff --git a/cmd/provider-services/cmd/run.go b/cmd/provider-services/cmd/run.go index 19394bbd..bec77b05 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" @@ -204,7 +205,7 @@ func RunCmd() *cobra.Command { panic(err) } - cmd.Flags().String(FlagGatewayGRPCListenAddress, "0.0.0.0:8442", "Gateway gRPC 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) } diff --git a/go.mod b/go.mod index 23d8b36e..ded3a1dc 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,6 @@ require ( github.com/cosmos/cosmos-sdk v0.45.16 github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f github.com/fsnotify/fsnotify v1.7.0 - github.com/go-andiamo/splitter v1.2.5 github.com/go-kit/kit v0.12.0 github.com/go-logr/logr v1.2.4 github.com/go-logr/zapr v1.2.4 @@ -262,7 +261,7 @@ require ( google.golang.org/appengine v1.6.8 // 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-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 b2952cdd..34a99512 100644 --- a/go.sum +++ b/go.sum @@ -629,8 +629,6 @@ github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0 github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= -github.com/go-andiamo/splitter v1.2.5 h1:P3NovWMY2V14TJJSolXBvlOmGSZo3Uz+LtTl2bsV/eY= -github.com/go-andiamo/splitter v1.2.5/go.mod h1:8WHU24t9hcMKU5FXDQb1hysSEC/GPuivIp0uKY1J8gw= github.com/go-asn1-ber/asn1-ber v1.3.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs= @@ -2544,8 +2542,8 @@ google.golang.org/genproto v0.0.0-20240318140521-94a12d6c2237 h1:PgNlNSx2Nq2/j4j 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-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= +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= From 4623e9367da7a3b6081ee005b67fba9f31754d3e Mon Sep 17 00:00:00 2001 From: Andrew Hare Date: Wed, 3 Apr 2024 22:22:57 -0600 Subject: [PATCH 12/14] fix: mod tidy --- go.mod | 1 + go.sum | 2 ++ 2 files changed, 3 insertions(+) diff --git a/go.mod b/go.mod index ded3a1dc..815eba16 100644 --- a/go.mod +++ b/go.mod @@ -11,6 +11,7 @@ require ( github.com/cosmos/cosmos-sdk v0.45.16 github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f github.com/fsnotify/fsnotify v1.7.0 + github.com/go-andiamo/splitter v1.2.5 github.com/go-kit/kit v0.12.0 github.com/go-logr/logr v1.2.4 github.com/go-logr/zapr v1.2.4 diff --git a/go.sum b/go.sum index 34a99512..1db273e9 100644 --- a/go.sum +++ b/go.sum @@ -629,6 +629,8 @@ github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0 github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= +github.com/go-andiamo/splitter v1.2.5 h1:P3NovWMY2V14TJJSolXBvlOmGSZo3Uz+LtTl2bsV/eY= +github.com/go-andiamo/splitter v1.2.5/go.mod h1:8WHU24t9hcMKU5FXDQb1hysSEC/GPuivIp0uKY1J8gw= github.com/go-asn1-ber/asn1-ber v1.3.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs= From 716f63b55aee00264c4aadd9b3a79f475a55082a Mon Sep 17 00:00:00 2001 From: Andrew Hare Date: Fri, 5 Apr 2024 18:41:27 -0600 Subject: [PATCH 13/14] fix: Misc TLS issues --- cmd/provider-services/cmd/grpc.go | 10 +- cmd/provider-services/cmd/manifest.go | 4 +- gateway/grpc/client.go | 1 + gateway/grpc/server.go | 2 +- gateway/grpc/server_test.go | 136 ++++++++++++++++++++++++++ gateway/utils/utils.go | 2 +- 6 files changed, 149 insertions(+), 6 deletions(-) diff --git a/cmd/provider-services/cmd/grpc.go b/cmd/provider-services/cmd/grpc.go index c3a62e8c..f88cc8e2 100644 --- a/cmd/provider-services/cmd/grpc.go +++ b/cmd/provider-services/cmd/grpc.go @@ -3,13 +3,19 @@ package cmd import ( "fmt" "net" + "net/url" ) func grpcURI(hostURI string) (string, error) { - host, _, err := net.SplitHostPort(hostURI) + 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(host, "8442"), nil + return net.JoinHostPort(h, "8444"), nil } diff --git a/cmd/provider-services/cmd/manifest.go b/cmd/provider-services/cmd/manifest.go index 495a4575..a0b41d73 100644 --- a/cmd/provider-services/cmd/manifest.go +++ b/cmd/provider-services/cmd/manifest.go @@ -103,12 +103,12 @@ func doSendManifest(cmd *cobra.Command, sdlpath string) error { ) for i, lid := range leases { - err := func() error { + err = func() error { ctx, cancel := context.WithTimeout(ctx, 5*time.Second) defer cancel() provAddr, _ := sdk.AccAddressFromBech32(lid.Provider) - prov, err := cl.Provider(context.Background(), &ptypes.QueryProviderRequest{Owner: provAddr.String()}) + prov, err := cl.Provider(ctx, &ptypes.QueryProviderRequest{Owner: provAddr.String()}) if err != nil { return fmt.Errorf("query client provider: %w", err) } diff --git a/gateway/grpc/client.go b/gateway/grpc/client.go index 8da2ba21..af657f3d 100644 --- a/gateway/grpc/client.go +++ b/gateway/grpc/client.go @@ -31,6 +31,7 @@ func NewClient(ctx context.Context, addr string, cert tls.Certificate, cquery ct 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 diff --git a/gateway/grpc/server.go b/gateway/grpc/server.go index 9bbfc5d7..e11f8181 100644 --- a/gateway/grpc/server.go +++ b/gateway/grpc/server.go @@ -114,7 +114,7 @@ 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 { - owner, err := utils.VerifyOwnerCert(ctx, mtls.State.PeerCertificates, "", x509.ExtKeyUsageServerAuth, cquery) + owner, err := utils.VerifyOwnerCert(ctx, mtls.State.PeerCertificates, "", x509.ExtKeyUsageClientAuth, cquery) if err != nil { return nil, fmt.Errorf("verify cert chain: %w", err) } diff --git a/gateway/grpc/server_test.go b/gateway/grpc/server_test.go index 5bdbac5e..e516e2bb 100644 --- a/gateway/grpc/server_test.go +++ b/gateway/grpc/server_test.go @@ -2,10 +2,18 @@ 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" @@ -209,3 +217,131 @@ func TestRPCs(t *testing.T) { }) } } + +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/utils/utils.go b/gateway/utils/utils.go index 7abe3831..596e548e 100644 --- a/gateway/utils/utils.go +++ b/gateway/utils/utils.go @@ -52,7 +52,7 @@ func VerifyOwnerCertBytes(ctx context.Context, chain [][]byte, dnsName string, u 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, nil + return nil, errors.Errorf("tls: empty chain") } if len(chain) > 1 { From 8abffc964fd43f6127eb9b43691b776f208284cb Mon Sep 17 00:00:00 2001 From: Andrew Hare Date: Wed, 10 Apr 2024 13:13:22 -0600 Subject: [PATCH 14/14] fix: Hack empty service resource endpoints --- gateway/grpc/lease.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/gateway/grpc/lease.go b/gateway/grpc/lease.go index 78a2bcb4..329f514e 100644 --- a/gateway/grpc/lease.go +++ b/gateway/grpc/lease.go @@ -9,6 +9,8 @@ import ( "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" ) @@ -24,6 +26,17 @@ func (l *leaseV1) SendManifest(ctx context.Context, r *leasev1.SendManifestReque 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