/* * * Copyright 2018 gRPC authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package alts import ( "context" "errors" "fmt" "io" "io/ioutil" "log" "os" "os/exec" "regexp" "runtime" "strings" "google.golang.org/grpc/peer" ) const ( linuxProductNameFile = "/sys/class/dmi/id/product_name" windowsCheckCommand = "powershell.exe" windowsCheckCommandArgs = "Get-WmiObject -Class Win32_BIOS" powershellOutputFilter = "Manufacturer" windowsManufacturerRegex = ":(.*)" ) type platformError string func (k platformError) Error() string { return fmt.Sprintf("%s is not supported", string(k)) } var ( // The following two variables will be reassigned in tests. runningOS = runtime.GOOS manufacturerReader = func() (io.Reader, error) { switch runningOS { case "linux": return os.Open(linuxProductNameFile) case "windows": cmd := exec.Command(windowsCheckCommand, windowsCheckCommandArgs) out, err := cmd.Output() if err != nil { return nil, err } for _, line := range strings.Split(strings.TrimSuffix(string(out), "\n"), "\n") { if strings.HasPrefix(line, powershellOutputFilter) { re := regexp.MustCompile(windowsManufacturerRegex) name := re.FindString(line) name = strings.TrimLeft(name, ":") return strings.NewReader(name), nil } } return nil, errors.New("cannot determine the machine's manufacturer") default: return nil, platformError(runningOS) } } vmOnGCP bool ) // isRunningOnGCP checks whether the local system, without doing a network request is // running on GCP. func isRunningOnGCP() bool { manufacturer, err := readManufacturer() if err != nil { log.Fatalf("failure to read manufacturer information: %v", err) } name := string(manufacturer) switch runningOS { case "linux": name = strings.TrimSpace(name) return name == "Google" || name == "Google Compute Engine" case "windows": name = strings.Replace(name, " ", "", -1) name = strings.Replace(name, "\n", "", -1) name = strings.Replace(name, "\r", "", -1) return name == "Google" default: log.Fatal(platformError(runningOS)) } return false } func readManufacturer() ([]byte, error) { reader, err := manufacturerReader() if err != nil { return nil, err } if reader == nil { return nil, errors.New("got nil reader") } manufacturer, err := ioutil.ReadAll(reader) if err != nil { return nil, fmt.Errorf("failed reading %v: %v", linuxProductNameFile, err) } return manufacturer, nil } // AuthInfoFromContext extracts the alts.AuthInfo object from the given context, // if it exists. This API should be used by gRPC server RPC handlers to get // information about the communicating peer. For client-side, use grpc.Peer() // CallOption. func AuthInfoFromContext(ctx context.Context) (AuthInfo, error) { p, ok := peer.FromContext(ctx) if !ok { return nil, errors.New("no Peer found in Context") } return AuthInfoFromPeer(p) } // AuthInfoFromPeer extracts the alts.AuthInfo object from the given peer, if it // exists. This API should be used by gRPC clients after obtaining a peer object // using the grpc.Peer() CallOption. func AuthInfoFromPeer(p *peer.Peer) (AuthInfo, error) { altsAuthInfo, ok := p.AuthInfo.(AuthInfo) if !ok { return nil, errors.New("no alts.AuthInfo found in Peer") } return altsAuthInfo, nil }