in cmd/core_plugin/firstboot/firstboot_linux.go [83:189]
func writeSSHKeys(ctx context.Context, instanceSetup *cfg.InstanceSetup) error {
if instanceSetup == nil {
galog.V(2).Debug("No instance setup config, skipping SSH key generation")
return nil
}
hostKeyDir := instanceSetup.HostKeyDir
dir, err := os.Open(hostKeyDir)
if err != nil {
return fmt.Errorf("failed to open host key dir: %w", err)
}
defer dir.Close()
files, err := dir.Readdirnames(0)
if err != nil {
return fmt.Errorf("failed to read host key dir: %w", err)
}
keytypes := make(map[string]bool)
// Find keys present on disk, and deduce their type from filename.
for _, file := range files {
if !hostKeyFile(file) {
galog.V(2).Debugf("Skipping file %q, not a key file", file)
continue
}
keytype := file
keytype = strings.TrimPrefix(keytype, hostKeyFilePrefix)
keytype = strings.TrimSuffix(keytype, hostKeyFileSuffix)
keytypes[keytype] = true
}
// List keys we should generate, according to the config.
configKeys := instanceSetup.HostKeyTypes
for _, keytype := range strings.Split(configKeys, ",") {
keytypes[keytype] = true
}
client := metadata.New()
// Generate new keys and upload to guest attributes.
for keytype := range keytypes {
keyfile := path.Join(hostKeyDir, fmt.Sprintf("%s_%s_%s", hostKeyFilePrefix, keytype, hostKeyFileSuffix))
pubKeyFile := keyfile + ".pub"
tmpKeyFile := keyfile + ".temp"
tmpPubKeyFile := keyfile + ".temp.pub"
cmd := []string{"ssh-keygen", "-t", keytype, "-f", tmpKeyFile, "-N", "", "-q"}
opts := run.Options{Name: cmd[0], Args: cmd[1:], OutputType: run.OutputNone}
if _, err := run.WithContext(ctx, opts); err != nil {
galog.Warnf("Failed to generate SSH host key %q: %v", keyfile, err)
continue
}
if err := os.Chmod(tmpKeyFile, 0600); err != nil {
galog.Errorf("Failed to chmod SSH host key %q: %v", tmpKeyFile, err)
continue
}
if err := os.Chmod(tmpPubKeyFile, 0644); err != nil {
galog.Errorf("Failed to chmod SSH host key %q: %v", tmpPubKeyFile, err)
continue
}
if err := os.Rename(tmpKeyFile, keyfile); err != nil {
galog.Errorf("Failed to overwrite %q: %v", keyfile, err)
continue
}
if err := os.Rename(tmpPubKeyFile, pubKeyFile); err != nil {
galog.Errorf("Failed to overwrite %q: %v", keyfile+".pub", err)
continue
}
pubKey, err := os.ReadFile(pubKeyFile)
if err != nil {
galog.Errorf("Can't read %s public key: %v", keytype, err)
continue
}
vals := strings.Split(string(pubKey), " ")
if len(vals) < 2 {
galog.Warnf("Generated key(%q) is malformed, not uploading", keytype)
continue
}
if err := client.WriteGuestAttributes(ctx, "hostkeys/"+vals[0], vals[1]); err != nil {
galog.Errorf("Failed to upload %s key to guest attributes: %v", keytype, err)
}
}
_, err = exec.LookPath("restorecon")
if err != nil {
galog.Infof("restorecon not found, skipping SELinux context restoration")
return nil
}
cmd := []string{"restorecon", "-FR", hostKeyDir}
opts := run.Options{Name: cmd[0], Args: cmd[1:], OutputType: run.OutputNone}
if _, err := run.WithContext(ctx, opts); err != nil {
return fmt.Errorf("failed to restore SELinux context for: %s, %w", hostKeyDir, err)
}
return nil
}