utils.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  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
  19. import (
  20. "context"
  21. "errors"
  22. "fmt"
  23. "io"
  24. "io/ioutil"
  25. "log"
  26. "os"
  27. "os/exec"
  28. "regexp"
  29. "runtime"
  30. "strings"
  31. "google.golang.org/grpc/codes"
  32. "google.golang.org/grpc/peer"
  33. "google.golang.org/grpc/status"
  34. )
  35. const (
  36. linuxProductNameFile = "/sys/class/dmi/id/product_name"
  37. windowsCheckCommand = "powershell.exe"
  38. windowsCheckCommandArgs = "Get-WmiObject -Class Win32_BIOS"
  39. powershellOutputFilter = "Manufacturer"
  40. windowsManufacturerRegex = ":(.*)"
  41. )
  42. type platformError string
  43. func (k platformError) Error() string {
  44. return fmt.Sprintf("%s is not supported", string(k))
  45. }
  46. var (
  47. // The following two variables will be reassigned in tests.
  48. runningOS = runtime.GOOS
  49. manufacturerReader = func() (io.Reader, error) {
  50. switch runningOS {
  51. case "linux":
  52. return os.Open(linuxProductNameFile)
  53. case "windows":
  54. cmd := exec.Command(windowsCheckCommand, windowsCheckCommandArgs)
  55. out, err := cmd.Output()
  56. if err != nil {
  57. return nil, err
  58. }
  59. for _, line := range strings.Split(strings.TrimSuffix(string(out), "\n"), "\n") {
  60. if strings.HasPrefix(line, powershellOutputFilter) {
  61. re := regexp.MustCompile(windowsManufacturerRegex)
  62. name := re.FindString(line)
  63. name = strings.TrimLeft(name, ":")
  64. return strings.NewReader(name), nil
  65. }
  66. }
  67. return nil, errors.New("cannot determine the machine's manufacturer")
  68. default:
  69. return nil, platformError(runningOS)
  70. }
  71. }
  72. vmOnGCP bool
  73. )
  74. // isRunningOnGCP checks whether the local system, without doing a network request is
  75. // running on GCP.
  76. func isRunningOnGCP() bool {
  77. manufacturer, err := readManufacturer()
  78. if os.IsNotExist(err) {
  79. return false
  80. }
  81. if err != nil {
  82. log.Fatalf("failure to read manufacturer information: %v", err)
  83. }
  84. name := string(manufacturer)
  85. switch runningOS {
  86. case "linux":
  87. name = strings.TrimSpace(name)
  88. return name == "Google" || name == "Google Compute Engine"
  89. case "windows":
  90. name = strings.Replace(name, " ", "", -1)
  91. name = strings.Replace(name, "\n", "", -1)
  92. name = strings.Replace(name, "\r", "", -1)
  93. return name == "Google"
  94. default:
  95. log.Fatal(platformError(runningOS))
  96. }
  97. return false
  98. }
  99. func readManufacturer() ([]byte, error) {
  100. reader, err := manufacturerReader()
  101. if err != nil {
  102. return nil, err
  103. }
  104. if reader == nil {
  105. return nil, errors.New("got nil reader")
  106. }
  107. manufacturer, err := ioutil.ReadAll(reader)
  108. if err != nil {
  109. return nil, fmt.Errorf("failed reading %v: %v", linuxProductNameFile, err)
  110. }
  111. return manufacturer, nil
  112. }
  113. // AuthInfoFromContext extracts the alts.AuthInfo object from the given context,
  114. // if it exists. This API should be used by gRPC server RPC handlers to get
  115. // information about the communicating peer. For client-side, use grpc.Peer()
  116. // CallOption.
  117. func AuthInfoFromContext(ctx context.Context) (AuthInfo, error) {
  118. p, ok := peer.FromContext(ctx)
  119. if !ok {
  120. return nil, errors.New("no Peer found in Context")
  121. }
  122. return AuthInfoFromPeer(p)
  123. }
  124. // AuthInfoFromPeer extracts the alts.AuthInfo object from the given peer, if it
  125. // exists. This API should be used by gRPC clients after obtaining a peer object
  126. // using the grpc.Peer() CallOption.
  127. func AuthInfoFromPeer(p *peer.Peer) (AuthInfo, error) {
  128. altsAuthInfo, ok := p.AuthInfo.(AuthInfo)
  129. if !ok {
  130. return nil, errors.New("no alts.AuthInfo found in Peer")
  131. }
  132. return altsAuthInfo, nil
  133. }
  134. // ClientAuthorizationCheck checks whether the client is authorized to access
  135. // the requested resources based on the given expected client service accounts.
  136. // This API should be used by gRPC server RPC handlers. This API should not be
  137. // used by clients.
  138. func ClientAuthorizationCheck(ctx context.Context, expectedServiceAccounts []string) error {
  139. authInfo, err := AuthInfoFromContext(ctx)
  140. if err != nil {
  141. return status.Newf(codes.PermissionDenied, "The context is not an ALTS-compatible context: %v", err).Err()
  142. }
  143. for _, sa := range expectedServiceAccounts {
  144. if authInfo.PeerServiceAccount() == sa {
  145. return nil
  146. }
  147. }
  148. return status.Newf(codes.PermissionDenied, "Client %v is not authorized", authInfo.PeerServiceAccount()).Err()
  149. }