google_authorized_keys/main.go (139 lines of code) (raw):

// Copyright 2022 Google LLC // 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 // https://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. // GoogleAuthorizedKeys obtains SSH keys from metadata. package main import ( "context" "encoding/json" "fmt" "io" "os" "path" "runtime" "strconv" "strings" "time" "github.com/GoogleCloudPlatform/guest-agent/metadata" "github.com/GoogleCloudPlatform/guest-agent/utils" "github.com/GoogleCloudPlatform/guest-logging-go/logger" ) var ( client metadata.MDSClientInterface programName = path.Base(os.Args[0]) ) func init() { client = metadata.New() } func logFormat(e logger.LogEntry) string { now := time.Now().Format("2006/01/02 15:04:05") return fmt.Sprintf("%s %s: %s", now, programName, e.Message) } func logFormatWindows(e logger.LogEntry) string { now := time.Now().Format("2006/01/02 15:04:05") // 2006/01/02 15:04:05 GCEMetadataScripts This is a log message. return fmt.Sprintf("%s %s: %s", now, programName, e.Message) } func parseSSHKeys(username string, keys []string) []string { var keyList []string for _, key := range keys { keySplit := strings.SplitN(key, ":", 2) if len(keySplit) != 2 { continue } user, keyVal, err := utils.GetUserKey(key) if err == nil { err = utils.ValidateUserKey(user, keyVal) } if err != nil { continue } if user == username { keyList = append(keyList, keyVal) } } return keyList } func getUserKeys(username string, instanceAttributes *attributes, projectAttributes *attributes) []string { var userKeyList []string instanceKeyList := parseSSHKeys(username, instanceAttributes.SSHKeys) userKeyList = append(userKeyList, instanceKeyList...) if !instanceAttributes.BlockProjectSSHKeys { projectKeyList := parseSSHKeys(username, projectAttributes.SSHKeys) userKeyList = append(userKeyList, projectKeyList...) } return userKeyList } func checkWinSSHEnabled(instanceAttributes *attributes, projectAttributes *attributes) bool { if instanceAttributes.EnableWindowsSSH != nil { return bool(*instanceAttributes.EnableWindowsSSH) } else if projectAttributes.EnableWindowsSSH != nil { return bool(*projectAttributes.EnableWindowsSSH) } return false } type attributes struct { EnableWindowsSSH *bool BlockProjectSSHKeys bool SSHKeys []string } func getMetadataAttributes(ctx context.Context, metadataKey string) (*attributes, error) { var a attributes type jsonAttributes struct { EnableWindowsSSH string `json:"enable-windows-ssh"` BlockProjectSSHKeys string `json:"block-project-ssh-keys"` SSHKeys string `json:"ssh-keys"` } var ja jsonAttributes metadata, err := client.GetKeyRecursive(ctx, metadataKey) if err != nil { return nil, err } if err := json.Unmarshal([]byte(metadata), &ja); err != nil { return nil, err } value, err := strconv.ParseBool(ja.BlockProjectSSHKeys) if err == nil { a.BlockProjectSSHKeys = value } value, err = strconv.ParseBool(ja.EnableWindowsSSH) if err == nil { a.EnableWindowsSSH = &value } if ja.SSHKeys != "" { a.SSHKeys = strings.Split(ja.SSHKeys, "\n") } return &a, nil } func main() { ctx := context.Background() username := os.Args[1] createdBy, _ := client.GetKey(ctx, "/instance/attributes/created-by", nil) opts := logger.LogOpts{ LoggerName: programName, FormatFunction: logFormat, MIG: createdBy, } if runtime.GOOS == "windows" { opts.Writers = []io.Writer{&utils.SerialPort{Port: "COM1"}, os.Stderr} opts.FormatFunction = logFormatWindows } else { opts.Writers = []io.Writer{os.Stderr} } if err := logger.Init(ctx, opts); err != nil { fmt.Printf("Error initializing logger: %+v", err) os.Exit(1) } // Try flushing logs before exiting, if not flushed logs could go missing. defer logger.Close() instanceAttributes, err := getMetadataAttributes(ctx, "instance/attributes/") if err != nil { logger.Errorf("Cannot read instance metadata attributes: %v", err) os.Exit(1) } projectAttributes, err := getMetadataAttributes(ctx, "project/attributes/") if err != nil { logger.Errorf("Cannot read project metadata attributes: %v", err) os.Exit(1) } if runtime.GOOS == "windows" && !checkWinSSHEnabled(instanceAttributes, projectAttributes) { logger.Errorf("Windows SSH not enabled with 'enable-windows-ssh' metadata key.") os.Exit(1) } userKeyList := getUserKeys(username, instanceAttributes, projectAttributes) fmt.Print(strings.Join(userKeyList, "\n")) }