mirror of
https://github.com/fullstorydev/grpcurl.git
synced 2026-06-15 07:21:44 +03:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
13ca681bad |
34
grpcurl.go
34
grpcurl.go
@@ -20,6 +20,7 @@ import (
|
|||||||
"slices"
|
"slices"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/golang/protobuf/proto" //lint:ignore SA1019 we have to import these because some of their types appear in exported API
|
"github.com/golang/protobuf/proto" //lint:ignore SA1019 we have to import these because some of their types appear in exported API
|
||||||
"github.com/jhump/protoreflect/desc" //lint:ignore SA1019 same as above
|
"github.com/jhump/protoreflect/desc" //lint:ignore SA1019 same as above
|
||||||
@@ -568,9 +569,6 @@ func ClientTLSConfig(insecureSkipVerify bool, cacertFile, clientCertFile, client
|
|||||||
// client certs. The serverCertFile and serverKeyFile must both not be blank.
|
// client certs. The serverCertFile and serverKeyFile must both not be blank.
|
||||||
func ServerTransportCredentials(cacertFile, serverCertFile, serverKeyFile string, requireClientCerts bool) (credentials.TransportCredentials, error) {
|
func ServerTransportCredentials(cacertFile, serverCertFile, serverKeyFile string, requireClientCerts bool) (credentials.TransportCredentials, error) {
|
||||||
var tlsConf tls.Config
|
var tlsConf tls.Config
|
||||||
// TODO(jh): Remove this line once https://github.com/golang/go/issues/28779 is fixed
|
|
||||||
// in Go tip. Until then, the recently merged TLS 1.3 support breaks the TLS tests.
|
|
||||||
tlsConf.MaxVersion = tls.VersionTLS12
|
|
||||||
|
|
||||||
// Load the server certificates from disk
|
// Load the server certificates from disk
|
||||||
certificate, err := tls.LoadX509KeyPair(serverCertFile, serverKeyFile)
|
certificate, err := tls.LoadX509KeyPair(serverCertFile, serverKeyFile)
|
||||||
@@ -734,6 +732,34 @@ func (c *errSignalingCreds) ClientHandshake(ctx context.Context, addr string, ra
|
|||||||
conn, auth, err := c.TransportCredentials.ClientHandshake(ctx, addr, rawConn)
|
conn, auth, err := c.TransportCredentials.ClientHandshake(ctx, addr, rawConn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.writeResult(err)
|
c.writeResult(err)
|
||||||
}
|
|
||||||
return conn, auth, err
|
return conn, auth, err
|
||||||
|
}
|
||||||
|
// Wrap TLS connections to capture post-handshake errors. With TLS 1.3,
|
||||||
|
// client certificate rejection by the server happens after the client
|
||||||
|
// considers the handshake complete. The server's TLS alert surfaces on the
|
||||||
|
// first Read from the connection. Only TLS connections need this (plaintext
|
||||||
|
// connections don't have post-handshake alerts).
|
||||||
|
if _, isTLS := auth.(credentials.TLSInfo); isTLS {
|
||||||
|
conn = &errSignalingConn{Conn: conn, writeResult: c.writeResult}
|
||||||
|
}
|
||||||
|
return conn, auth, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// errSignalingConn wraps a net.Conn to capture the first read error and
|
||||||
|
// report it via writeResult. This allows BlockingDial to surface post-handshake
|
||||||
|
// errors.
|
||||||
|
type errSignalingConn struct {
|
||||||
|
net.Conn
|
||||||
|
writeResult func(res interface{})
|
||||||
|
once sync.Once
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *errSignalingConn) Read(b []byte) (int, error) {
|
||||||
|
n, err := c.Conn.Read(b)
|
||||||
|
if err != nil {
|
||||||
|
c.once.Do(func() {
|
||||||
|
c.writeResult(err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return n, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package grpcurl_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -10,6 +11,7 @@ import (
|
|||||||
|
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/credentials"
|
"google.golang.org/grpc/credentials"
|
||||||
|
"google.golang.org/grpc/peer"
|
||||||
|
|
||||||
. "github.com/fullstorydev/grpcurl"
|
. "github.com/fullstorydev/grpcurl"
|
||||||
grpcurl_testing "github.com/fullstorydev/grpcurl/internal/testing"
|
grpcurl_testing "github.com/fullstorydev/grpcurl/internal/testing"
|
||||||
@@ -101,6 +103,68 @@ func TestRequireClientCertTLS(t *testing.T) {
|
|||||||
simpleTest(t, e.cc)
|
simpleTest(t, e.cc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTLS12(t *testing.T) {
|
||||||
|
serverCreds, err := ServerTransportCredentials("", "internal/testing/tls/server.crt", "internal/testing/tls/server.key", false)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create server creds: %v", err)
|
||||||
|
}
|
||||||
|
tlsConf, err := ClientTLSConfig(false, "internal/testing/tls/ca.crt", "", "")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create client TLS config: %v", err)
|
||||||
|
}
|
||||||
|
tlsConf.MaxVersion = tls.VersionTLS12
|
||||||
|
|
||||||
|
e, err := createTestServerAndClient(serverCreds, credentials.NewTLS(tlsConf))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to setup server and client: %v", err)
|
||||||
|
}
|
||||||
|
defer e.Close()
|
||||||
|
|
||||||
|
tlsVersion := negotiatedTLSVersion(t, e.cc)
|
||||||
|
if tlsVersion != tls.VersionTLS12 {
|
||||||
|
t.Errorf("expected TLS 1.2, got 0x%04x", tlsVersion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTLS13(t *testing.T) {
|
||||||
|
serverCreds, err := ServerTransportCredentials("", "internal/testing/tls/server.crt", "internal/testing/tls/server.key", false)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create server creds: %v", err)
|
||||||
|
}
|
||||||
|
clientCreds, err := ClientTransportCredentials(false, "internal/testing/tls/ca.crt", "", "")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create client creds: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
e, err := createTestServerAndClient(serverCreds, clientCreds)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to setup server and client: %v", err)
|
||||||
|
}
|
||||||
|
defer e.Close()
|
||||||
|
|
||||||
|
tlsVersion := negotiatedTLSVersion(t, e.cc)
|
||||||
|
if tlsVersion != tls.VersionTLS13 {
|
||||||
|
t.Errorf("expected TLS 1.3, got 0x%04x", tlsVersion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func negotiatedTLSVersion(t *testing.T, cc *grpc.ClientConn) uint16 {
|
||||||
|
t.Helper()
|
||||||
|
cl := grpcurl_testing.NewTestServiceClient(cc)
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
var p peer.Peer
|
||||||
|
_, err := cl.UnaryCall(ctx, &grpcurl_testing.SimpleRequest{}, grpc.WaitForReady(true), grpc.Peer(&p))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("RPC failed: %v", err)
|
||||||
|
}
|
||||||
|
tlsInfo, ok := p.AuthInfo.(credentials.TLSInfo)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("expected TLS auth info, got %T", p.AuthInfo)
|
||||||
|
}
|
||||||
|
return tlsInfo.State.Version
|
||||||
|
}
|
||||||
|
|
||||||
func TestBrokenTLS_ClientPlainText(t *testing.T) {
|
func TestBrokenTLS_ClientPlainText(t *testing.T) {
|
||||||
serverCreds, err := ServerTransportCredentials("", "internal/testing/tls/server.crt", "internal/testing/tls/server.key", false)
|
serverCreds, err := ServerTransportCredentials("", "internal/testing/tls/server.crt", "internal/testing/tls/server.key", false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -253,12 +317,14 @@ func TestBrokenTLS_ClientNotTrusted(t *testing.T) {
|
|||||||
e.Close()
|
e.Close()
|
||||||
t.Fatal("expecting TLS failure setting up server and client")
|
t.Fatal("expecting TLS failure setting up server and client")
|
||||||
}
|
}
|
||||||
// Check for either the old error (Go <=1.24) or the new one (Go 1.25+)
|
// The exact TLS alert varies by Go version and TLS version negotiated:
|
||||||
// Go 1.24: "bad certificate"
|
// - TLS 1.2: "bad certificate" (Go <=1.24) or "handshake failure" (Go 1.25+)
|
||||||
// Go 1.25: "handshake failure"
|
// - TLS 1.3: "certificate required" (server rejects after handshake)
|
||||||
errMsg := err.Error()
|
errMsg := err.Error()
|
||||||
if !strings.Contains(errMsg, "bad certificate") && !strings.Contains(errMsg, "handshake failure") {
|
if !strings.Contains(errMsg, "bad certificate") &&
|
||||||
t.Fatalf("expecting a specific TLS certificate or handshake error, got: %v", err)
|
!strings.Contains(errMsg, "handshake failure") &&
|
||||||
|
!strings.Contains(errMsg, "certificate required") {
|
||||||
|
t.Fatalf("expecting a TLS certificate error, got: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -297,12 +363,14 @@ func TestBrokenTLS_RequireClientCertButNonePresented(t *testing.T) {
|
|||||||
e.Close()
|
e.Close()
|
||||||
t.Fatal("expecting TLS failure setting up server and client")
|
t.Fatal("expecting TLS failure setting up server and client")
|
||||||
}
|
}
|
||||||
// Check for either the old error (Go <=1.24) or the new one (Go 1.25+)
|
// The exact TLS alert varies by Go version and TLS version negotiated:
|
||||||
// Go 1.24: "bad certificate"
|
// - TLS 1.2: "bad certificate" (Go <=1.24) or "handshake failure" (Go 1.25+)
|
||||||
// Go 1.25: "handshake failure"
|
// - TLS 1.3: "certificate required" (server rejects after handshake)
|
||||||
errMsg := err.Error()
|
errMsg := err.Error()
|
||||||
if !strings.Contains(errMsg, "bad certificate") && !strings.Contains(errMsg, "handshake failure") {
|
if !strings.Contains(errMsg, "bad certificate") &&
|
||||||
t.Fatalf("expecting a specific TLS certificate or handshake error, got: %v", err)
|
!strings.Contains(errMsg, "handshake failure") &&
|
||||||
|
!strings.Contains(errMsg, "certificate required") {
|
||||||
|
t.Fatalf("expecting a TLS certificate error, got: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user