google_guest_agent/agentcrypto/mtls_mds_linux.go (88 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 agentcrypto import ( "context" "fmt" "os" "os/exec" "path/filepath" "github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/run" "github.com/GoogleCloudPlatform/guest-agent/utils" "github.com/GoogleCloudPlatform/guest-logging-go/logger" ) const ( // defaultCredsDir is the directory location for MTLS MDS credentials. defaultCredsDir = "/run/google-mds-mtls" // rootCACertFileName is the root CA cert. rootCACertFileName = "root.crt" // clientCredsFileName are client credentials, its basically the file // that has the EC private key and the client certificate concatenated. clientCredsFileName = "client.key" ) var ( // certUpdaters is a map of known CA certificate updaters with the local directory paths for certificates. certUpdaters = map[string][]string{ // SUSE, Debian and Ubuntu distributions. // https://manpages.ubuntu.com/manpages/xenial/man8/update-ca-certificates.8.html // https://github.com/openSUSE/ca-certificates "update-ca-certificates": {"/usr/local/share/ca-certificates", "/usr/share/pki/trust/anchors"}, // CentOS, Fedora, RedHat distributions. // https://www.unix.com/man-page/centos/8/UPDATE-CA-TRUST "update-ca-trust": {"/etc/pki/ca-trust/source/anchors"}, } ) // writeRootCACert writes Root CA cert from UEFI variable to output file. func (j *CredsJob) writeRootCACert(ctx context.Context, content []byte, outputFile string) error { // The directory should be executable, but the file does not need to be. if err := os.MkdirAll(filepath.Dir(outputFile), 0655); err != nil { return err } if err := utils.SaferWriteFile(content, outputFile, 0644); err != nil { return err } if !j.useNativeStore.Load() { logger.Debugf("SkipNativeStore is enabled, will not write root cert to system store") return nil } // Best effort to update system store, don't fail. if err := updateSystemStore(ctx, outputFile); err != nil { logger.Errorf("Failed add Root MDS cert to system trust store with error: %v", err) } return nil } // writeClientCredentials stores client credentials (certificate and private key). func (j *CredsJob) writeClientCredentials(plaintext []byte, outputFile string) error { // The directory should be executable, but the file does not need to be. if err := os.MkdirAll(filepath.Dir(outputFile), 0655); err != nil { return err } return utils.SaferWriteFile(plaintext, outputFile, 0644) } // getCAStoreUpdater interates over known system trust store updaters and returns the first found. func getCAStoreUpdater() (string, error) { var errs []string for u := range certUpdaters { _, err := exec.LookPath(u) if err == nil { return u, nil } errs = append(errs, fmt.Sprintf("lookup for %q failed with error: %v", u, err)) } return "", fmt.Errorf("no known trust updaters were found: %v", errs) } // certificateDirFromUpdater returns directory of local CA certificates for the given updater tool. func certificateDirFromUpdater(updater string) (string, error) { dirs, ok := certUpdaters[updater] if !ok { return "", fmt.Errorf("unknown updater %q, no local trusted CA certificate directory found", updater) } for _, dir := range dirs { fi, err := os.Stat(dir) if err == nil && fi.IsDir() { return dir, nil } } return "", fmt.Errorf("no of the known directories %v found for updater %q", dirs, updater) } // updateSystemStore updates the local system store with the cert. func updateSystemStore(ctx context.Context, cert string) error { cmd, err := getCAStoreUpdater() if err != nil { return err } dir, err := certificateDirFromUpdater(cmd) if err != nil { return err } dest := filepath.Join(dir, filepath.Base(cert)) if err := utils.CopyFile(cert, dest, 0644); err != nil { return err } res := run.WithOutput(ctx, cmd) if res.ExitCode != 0 { return fmt.Errorf("command %q failed with error: %s", cmd, res.Error()) } logger.Infof("Certificate %q added to system store successfully %s", cert, res.StdOut) return nil }