credentials.go 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. // Copyright 2019 The etcd Authors
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. // Package credentials implements gRPC credential interface with etcd specific logic.
  15. // e.g., client handshake with custom authority parameter
  16. package credentials
  17. import (
  18. "context"
  19. "crypto/tls"
  20. "net"
  21. "sync"
  22. "go.etcd.io/etcd/clientv3/balancer/resolver/endpoint"
  23. "go.etcd.io/etcd/etcdserver/api/v3rpc/rpctypes"
  24. grpccredentials "google.golang.org/grpc/credentials"
  25. )
  26. // Config defines gRPC credential configuration.
  27. type Config struct {
  28. TLSConfig *tls.Config
  29. }
  30. // Bundle defines gRPC credential interface.
  31. type Bundle interface {
  32. grpccredentials.Bundle
  33. UpdateAuthToken(token string)
  34. }
  35. // NewBundle constructs a new gRPC credential bundle.
  36. func NewBundle(cfg Config) Bundle {
  37. return &bundle{
  38. tc: newTransportCredential(cfg.TLSConfig),
  39. rc: newPerRPCCredential(),
  40. }
  41. }
  42. // bundle implements "grpccredentials.Bundle" interface.
  43. type bundle struct {
  44. tc *transportCredential
  45. rc *perRPCCredential
  46. }
  47. func (b *bundle) TransportCredentials() grpccredentials.TransportCredentials {
  48. return b.tc
  49. }
  50. func (b *bundle) PerRPCCredentials() grpccredentials.PerRPCCredentials {
  51. return b.rc
  52. }
  53. func (b *bundle) NewWithMode(mode string) (grpccredentials.Bundle, error) {
  54. // no-op
  55. return nil, nil
  56. }
  57. // transportCredential implements "grpccredentials.TransportCredentials" interface.
  58. // transportCredential wraps TransportCredentials to track which
  59. // addresses are dialed for which endpoints, and then sets the authority when checking the endpoint's cert to the
  60. // hostname or IP of the dialed endpoint.
  61. // This is a workaround of a gRPC load balancer issue. gRPC uses the dialed target's service name as the authority when
  62. // checking all endpoint certs, which does not work for etcd servers using their hostname or IP as the Subject Alternative Name
  63. // in their TLS certs.
  64. // To enable, include both WithTransportCredentials(creds) and WithContextDialer(creds.Dialer)
  65. // when dialing.
  66. type transportCredential struct {
  67. gtc grpccredentials.TransportCredentials
  68. mu sync.Mutex
  69. // addrToEndpoint maps from the connection addresses that are dialed to the hostname or IP of the
  70. // endpoint provided to the dialer when dialing
  71. addrToEndpoint map[string]string
  72. }
  73. func newTransportCredential(cfg *tls.Config) *transportCredential {
  74. return &transportCredential{
  75. gtc: grpccredentials.NewTLS(cfg),
  76. addrToEndpoint: map[string]string{},
  77. }
  78. }
  79. func (tc *transportCredential) ClientHandshake(ctx context.Context, authority string, rawConn net.Conn) (net.Conn, grpccredentials.AuthInfo, error) {
  80. // Set the authority when checking the endpoint's cert to the hostname or IP of the dialed endpoint
  81. tc.mu.Lock()
  82. dialEp, ok := tc.addrToEndpoint[rawConn.RemoteAddr().String()]
  83. tc.mu.Unlock()
  84. if ok {
  85. _, host, _ := endpoint.ParseEndpoint(dialEp)
  86. authority = host
  87. }
  88. return tc.gtc.ClientHandshake(ctx, authority, rawConn)
  89. }
  90. // return true if given string is an IP.
  91. func isIP(ep string) bool {
  92. return net.ParseIP(ep) != nil
  93. }
  94. func (tc *transportCredential) ServerHandshake(rawConn net.Conn) (net.Conn, grpccredentials.AuthInfo, error) {
  95. return tc.gtc.ServerHandshake(rawConn)
  96. }
  97. func (tc *transportCredential) Info() grpccredentials.ProtocolInfo {
  98. return tc.gtc.Info()
  99. }
  100. func (tc *transportCredential) Clone() grpccredentials.TransportCredentials {
  101. copy := map[string]string{}
  102. tc.mu.Lock()
  103. for k, v := range tc.addrToEndpoint {
  104. copy[k] = v
  105. }
  106. tc.mu.Unlock()
  107. return &transportCredential{
  108. gtc: tc.gtc.Clone(),
  109. addrToEndpoint: copy,
  110. }
  111. }
  112. func (tc *transportCredential) OverrideServerName(serverNameOverride string) error {
  113. return tc.gtc.OverrideServerName(serverNameOverride)
  114. }
  115. func (tc *transportCredential) Dialer(ctx context.Context, dialEp string) (net.Conn, error) {
  116. // Keep track of which addresses are dialed for which endpoints
  117. conn, err := endpoint.Dialer(ctx, dialEp)
  118. if conn != nil {
  119. tc.mu.Lock()
  120. tc.addrToEndpoint[conn.RemoteAddr().String()] = dialEp
  121. tc.mu.Unlock()
  122. }
  123. return conn, err
  124. }
  125. // perRPCCredential implements "grpccredentials.PerRPCCredentials" interface.
  126. type perRPCCredential struct {
  127. authToken string
  128. authTokenMu sync.RWMutex
  129. }
  130. func newPerRPCCredential() *perRPCCredential { return &perRPCCredential{} }
  131. func (rc *perRPCCredential) RequireTransportSecurity() bool { return false }
  132. func (rc *perRPCCredential) GetRequestMetadata(ctx context.Context, s ...string) (map[string]string, error) {
  133. rc.authTokenMu.RLock()
  134. authToken := rc.authToken
  135. rc.authTokenMu.RUnlock()
  136. return map[string]string{rpctypes.TokenFieldNameGRPC: authToken}, nil
  137. }
  138. func (b *bundle) UpdateAuthToken(token string) {
  139. if b.rc == nil {
  140. return
  141. }
  142. b.rc.UpdateAuthToken(token)
  143. }
  144. func (rc *perRPCCredential) UpdateAuthToken(token string) {
  145. rc.authTokenMu.Lock()
  146. rc.authToken = token
  147. rc.authTokenMu.Unlock()
  148. }