google_guest_agent/sshca/sshca.go (68 lines of code) (raw):

// Copyright 2023 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. // Package sshca is the actual writing end of the sshtrustedca pipeline. package sshca import ( "context" "encoding/json" "strings" "github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/events" "github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/events/sshtrustedca" "github.com/GoogleCloudPlatform/guest-agent/metadata" "github.com/GoogleCloudPlatform/guest-logging-go/logger" ) // Certificates wrapps a list of certificate authorities. type Certificates struct { Certs []TrustedCert `json:"trustedCertificateAuthorities"` } // TrustedCert defines the object containing a public key. type TrustedCert struct { PublicKey string `json:"publicKey"` } var ( // mdsClient is the metadata's client, used to query oslogin certificates. mdsClient *metadata.Client ) // Init initializes the sshca's event handler callback. func Init() { mdsClient = metadata.New() events.Get().Subscribe(sshtrustedca.ReadEvent, nil, writeFile) } // Close finishes the sshca module, deallocating everything allocated with Init(). func Close() { events.Get().Unsubscribe(sshtrustedca.ReadEvent, writeFile) mdsClient = nil } // writeFile is an event handler callback and writes the actual sshca content to the pipe // used by openssh to grant access based on ssh ca. func writeFile(ctx context.Context, evType string, data interface{}, evData *events.EventData) bool { // There was some error on the pipe watcher, just ignore it. if evData.Error != nil { logger.Debugf("Not handling ssh trusted ca cert event, we got an error: %+v", evData.Error) return true } // Make sure we close the pipe after we've done writing to it. pipeData, ok := evData.Data.(*sshtrustedca.PipeData) if !ok { logger.Errorf("Received invalid event data (%+v), ignoring this event and un-subscribing %s", evData.Data, evType) return false } defer func() { if err := pipeData.File.Close(); err != nil { logger.Errorf("Failed to close pipe: %+v", err) } pipeData.Finished() }() certificate, err := mdsClient.GetKey(ctx, "oslogin/certificates", nil) if err != nil { logger.Errorf("Failed to get certificate from metadata server: %+v", err) return true } // Keep a copy of the returned certificate for error fallback caching. var certs Certificates var outData []string if err := json.Unmarshal([]byte(certificate), &certs); err != nil { logger.Errorf("Failed to unmarshal certificate json: %+v", err) return true } for _, curr := range certs.Certs { outData = append(outData, curr.PublicKey) } outStr := strings.Join(outData, "\n") n, err := pipeData.File.WriteString(outStr) if err != nil { logger.Errorf("Failed to write certificate to the write end of the pipe: %+v", err) return true } if n != len(outStr) { logger.Errorf("Wrote the wrong ammout of data, wrote %d bytes instead of %d bytes", n, len(certificate)) } return true }