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
}