alts.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. /*
  2. *
  3. * Copyright 2018 gRPC authors.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. *
  17. */
  18. // Package alts implements the ALTS credential support by gRPC library, which
  19. // encapsulates all the state needed by a client to authenticate with a server
  20. // using ALTS and make various assertions, e.g., about the client's identity,
  21. // role, or whether it is authorized to make a particular call.
  22. // This package is experimental.
  23. package alts
  24. import (
  25. "context"
  26. "errors"
  27. "fmt"
  28. "net"
  29. "sync"
  30. "time"
  31. "google.golang.org/grpc/credentials"
  32. core "google.golang.org/grpc/credentials/alts/internal"
  33. "google.golang.org/grpc/credentials/alts/internal/handshaker"
  34. "google.golang.org/grpc/credentials/alts/internal/handshaker/service"
  35. altspb "google.golang.org/grpc/credentials/alts/internal/proto/grpc_gcp"
  36. "google.golang.org/grpc/grpclog"
  37. "google.golang.org/grpc/internal/googlecloud"
  38. )
  39. const (
  40. // hypervisorHandshakerServiceAddress represents the default ALTS gRPC
  41. // handshaker service address in the hypervisor.
  42. hypervisorHandshakerServiceAddress = "metadata.google.internal.:8080"
  43. // defaultTimeout specifies the server handshake timeout.
  44. defaultTimeout = 30.0 * time.Second
  45. // The following constants specify the minimum and maximum acceptable
  46. // protocol versions.
  47. protocolVersionMaxMajor = 2
  48. protocolVersionMaxMinor = 1
  49. protocolVersionMinMajor = 2
  50. protocolVersionMinMinor = 1
  51. )
  52. var (
  53. vmOnGCP bool
  54. once sync.Once
  55. maxRPCVersion = &altspb.RpcProtocolVersions_Version{
  56. Major: protocolVersionMaxMajor,
  57. Minor: protocolVersionMaxMinor,
  58. }
  59. minRPCVersion = &altspb.RpcProtocolVersions_Version{
  60. Major: protocolVersionMinMajor,
  61. Minor: protocolVersionMinMinor,
  62. }
  63. // ErrUntrustedPlatform is returned from ClientHandshake and
  64. // ServerHandshake is running on a platform where the trustworthiness of
  65. // the handshaker service is not guaranteed.
  66. ErrUntrustedPlatform = errors.New("ALTS: untrusted platform. ALTS is only supported on GCP")
  67. logger = grpclog.Component("alts")
  68. )
  69. // AuthInfo exposes security information from the ALTS handshake to the
  70. // application. This interface is to be implemented by ALTS. Users should not
  71. // need a brand new implementation of this interface. For situations like
  72. // testing, any new implementation should embed this interface. This allows
  73. // ALTS to add new methods to this interface.
  74. type AuthInfo interface {
  75. // ApplicationProtocol returns application protocol negotiated for the
  76. // ALTS connection.
  77. ApplicationProtocol() string
  78. // RecordProtocol returns the record protocol negotiated for the ALTS
  79. // connection.
  80. RecordProtocol() string
  81. // SecurityLevel returns the security level of the created ALTS secure
  82. // channel.
  83. SecurityLevel() altspb.SecurityLevel
  84. // PeerServiceAccount returns the peer service account.
  85. PeerServiceAccount() string
  86. // LocalServiceAccount returns the local service account.
  87. LocalServiceAccount() string
  88. // PeerRPCVersions returns the RPC version supported by the peer.
  89. PeerRPCVersions() *altspb.RpcProtocolVersions
  90. }
  91. // ClientOptions contains the client-side options of an ALTS channel. These
  92. // options will be passed to the underlying ALTS handshaker.
  93. type ClientOptions struct {
  94. // TargetServiceAccounts contains a list of expected target service
  95. // accounts.
  96. TargetServiceAccounts []string
  97. // HandshakerServiceAddress represents the ALTS handshaker gRPC service
  98. // address to connect to.
  99. HandshakerServiceAddress string
  100. }
  101. // DefaultClientOptions creates a new ClientOptions object with the default
  102. // values.
  103. func DefaultClientOptions() *ClientOptions {
  104. return &ClientOptions{
  105. HandshakerServiceAddress: hypervisorHandshakerServiceAddress,
  106. }
  107. }
  108. // ServerOptions contains the server-side options of an ALTS channel. These
  109. // options will be passed to the underlying ALTS handshaker.
  110. type ServerOptions struct {
  111. // HandshakerServiceAddress represents the ALTS handshaker gRPC service
  112. // address to connect to.
  113. HandshakerServiceAddress string
  114. }
  115. // DefaultServerOptions creates a new ServerOptions object with the default
  116. // values.
  117. func DefaultServerOptions() *ServerOptions {
  118. return &ServerOptions{
  119. HandshakerServiceAddress: hypervisorHandshakerServiceAddress,
  120. }
  121. }
  122. // altsTC is the credentials required for authenticating a connection using ALTS.
  123. // It implements credentials.TransportCredentials interface.
  124. type altsTC struct {
  125. info *credentials.ProtocolInfo
  126. side core.Side
  127. accounts []string
  128. hsAddress string
  129. }
  130. // NewClientCreds constructs a client-side ALTS TransportCredentials object.
  131. func NewClientCreds(opts *ClientOptions) credentials.TransportCredentials {
  132. return newALTS(core.ClientSide, opts.TargetServiceAccounts, opts.HandshakerServiceAddress)
  133. }
  134. // NewServerCreds constructs a server-side ALTS TransportCredentials object.
  135. func NewServerCreds(opts *ServerOptions) credentials.TransportCredentials {
  136. return newALTS(core.ServerSide, nil, opts.HandshakerServiceAddress)
  137. }
  138. func newALTS(side core.Side, accounts []string, hsAddress string) credentials.TransportCredentials {
  139. once.Do(func() {
  140. vmOnGCP = googlecloud.OnGCE()
  141. })
  142. if hsAddress == "" {
  143. hsAddress = hypervisorHandshakerServiceAddress
  144. }
  145. return &altsTC{
  146. info: &credentials.ProtocolInfo{
  147. SecurityProtocol: "alts",
  148. SecurityVersion: "1.0",
  149. },
  150. side: side,
  151. accounts: accounts,
  152. hsAddress: hsAddress,
  153. }
  154. }
  155. // ClientHandshake implements the client side handshake protocol.
  156. func (g *altsTC) ClientHandshake(ctx context.Context, addr string, rawConn net.Conn) (_ net.Conn, _ credentials.AuthInfo, err error) {
  157. if !vmOnGCP {
  158. return nil, nil, ErrUntrustedPlatform
  159. }
  160. // Connecting to ALTS handshaker service.
  161. hsConn, err := service.Dial(g.hsAddress)
  162. if err != nil {
  163. return nil, nil, err
  164. }
  165. // Do not close hsConn since it is shared with other handshakes.
  166. // Possible context leak:
  167. // The cancel function for the child context we create will only be
  168. // called a non-nil error is returned.
  169. var cancel context.CancelFunc
  170. ctx, cancel = context.WithCancel(ctx)
  171. defer func() {
  172. if err != nil {
  173. cancel()
  174. }
  175. }()
  176. opts := handshaker.DefaultClientHandshakerOptions()
  177. opts.TargetName = addr
  178. opts.TargetServiceAccounts = g.accounts
  179. opts.RPCVersions = &altspb.RpcProtocolVersions{
  180. MaxRpcVersion: maxRPCVersion,
  181. MinRpcVersion: minRPCVersion,
  182. }
  183. chs, err := handshaker.NewClientHandshaker(ctx, hsConn, rawConn, opts)
  184. if err != nil {
  185. return nil, nil, err
  186. }
  187. defer func() {
  188. if err != nil {
  189. chs.Close()
  190. }
  191. }()
  192. secConn, authInfo, err := chs.ClientHandshake(ctx)
  193. if err != nil {
  194. return nil, nil, err
  195. }
  196. altsAuthInfo, ok := authInfo.(AuthInfo)
  197. if !ok {
  198. return nil, nil, errors.New("client-side auth info is not of type alts.AuthInfo")
  199. }
  200. match, _ := checkRPCVersions(opts.RPCVersions, altsAuthInfo.PeerRPCVersions())
  201. if !match {
  202. return nil, nil, fmt.Errorf("server-side RPC versions are not compatible with this client, local versions: %v, peer versions: %v", opts.RPCVersions, altsAuthInfo.PeerRPCVersions())
  203. }
  204. return secConn, authInfo, nil
  205. }
  206. // ServerHandshake implements the server side ALTS handshaker.
  207. func (g *altsTC) ServerHandshake(rawConn net.Conn) (_ net.Conn, _ credentials.AuthInfo, err error) {
  208. if !vmOnGCP {
  209. return nil, nil, ErrUntrustedPlatform
  210. }
  211. // Connecting to ALTS handshaker service.
  212. hsConn, err := service.Dial(g.hsAddress)
  213. if err != nil {
  214. return nil, nil, err
  215. }
  216. // Do not close hsConn since it's shared with other handshakes.
  217. ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)
  218. defer cancel()
  219. opts := handshaker.DefaultServerHandshakerOptions()
  220. opts.RPCVersions = &altspb.RpcProtocolVersions{
  221. MaxRpcVersion: maxRPCVersion,
  222. MinRpcVersion: minRPCVersion,
  223. }
  224. shs, err := handshaker.NewServerHandshaker(ctx, hsConn, rawConn, opts)
  225. if err != nil {
  226. return nil, nil, err
  227. }
  228. defer func() {
  229. if err != nil {
  230. shs.Close()
  231. }
  232. }()
  233. secConn, authInfo, err := shs.ServerHandshake(ctx)
  234. if err != nil {
  235. return nil, nil, err
  236. }
  237. altsAuthInfo, ok := authInfo.(AuthInfo)
  238. if !ok {
  239. return nil, nil, errors.New("server-side auth info is not of type alts.AuthInfo")
  240. }
  241. match, _ := checkRPCVersions(opts.RPCVersions, altsAuthInfo.PeerRPCVersions())
  242. if !match {
  243. return nil, nil, fmt.Errorf("client-side RPC versions is not compatible with this server, local versions: %v, peer versions: %v", opts.RPCVersions, altsAuthInfo.PeerRPCVersions())
  244. }
  245. return secConn, authInfo, nil
  246. }
  247. func (g *altsTC) Info() credentials.ProtocolInfo {
  248. return *g.info
  249. }
  250. func (g *altsTC) Clone() credentials.TransportCredentials {
  251. info := *g.info
  252. var accounts []string
  253. if g.accounts != nil {
  254. accounts = make([]string, len(g.accounts))
  255. copy(accounts, g.accounts)
  256. }
  257. return &altsTC{
  258. info: &info,
  259. side: g.side,
  260. hsAddress: g.hsAddress,
  261. accounts: accounts,
  262. }
  263. }
  264. func (g *altsTC) OverrideServerName(serverNameOverride string) error {
  265. g.info.ServerName = serverNameOverride
  266. return nil
  267. }
  268. // compareRPCVersion returns 0 if v1 == v2, 1 if v1 > v2 and -1 if v1 < v2.
  269. func compareRPCVersions(v1, v2 *altspb.RpcProtocolVersions_Version) int {
  270. switch {
  271. case v1.GetMajor() > v2.GetMajor(),
  272. v1.GetMajor() == v2.GetMajor() && v1.GetMinor() > v2.GetMinor():
  273. return 1
  274. case v1.GetMajor() < v2.GetMajor(),
  275. v1.GetMajor() == v2.GetMajor() && v1.GetMinor() < v2.GetMinor():
  276. return -1
  277. }
  278. return 0
  279. }
  280. // checkRPCVersions performs a version check between local and peer rpc protocol
  281. // versions. This function returns true if the check passes which means both
  282. // parties agreed on a common rpc protocol to use, and false otherwise. The
  283. // function also returns the highest common RPC protocol version both parties
  284. // agreed on.
  285. func checkRPCVersions(local, peer *altspb.RpcProtocolVersions) (bool, *altspb.RpcProtocolVersions_Version) {
  286. if local == nil || peer == nil {
  287. logger.Error("invalid checkRPCVersions argument, either local or peer is nil.")
  288. return false, nil
  289. }
  290. // maxCommonVersion is MIN(local.max, peer.max).
  291. maxCommonVersion := local.GetMaxRpcVersion()
  292. if compareRPCVersions(local.GetMaxRpcVersion(), peer.GetMaxRpcVersion()) > 0 {
  293. maxCommonVersion = peer.GetMaxRpcVersion()
  294. }
  295. // minCommonVersion is MAX(local.min, peer.min).
  296. minCommonVersion := peer.GetMinRpcVersion()
  297. if compareRPCVersions(local.GetMinRpcVersion(), peer.GetMinRpcVersion()) > 0 {
  298. minCommonVersion = local.GetMinRpcVersion()
  299. }
  300. if compareRPCVersions(maxCommonVersion, minCommonVersion) < 0 {
  301. return false, nil
  302. }
  303. return true, maxCommonVersion
  304. }