packer-cn/builder/googlecompute/step_import_os_login_ssh_ke...

133 lines
3.7 KiB
Go

package googlecompute
import (
"context"
"crypto/sha256"
"encoding/hex"
"fmt"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
"google.golang.org/api/oauth2/v2"
)
// StepImportOSLoginSSHKey imports a temporary SSH key pair into a GCE login profile.
type StepImportOSLoginSSHKey struct {
Debug bool
TokeninfoFunc func(context.Context) (*oauth2.Tokeninfo, error)
accountEmail string
}
// Run executes the Packer build step that generates SSH key pairs.
// The key pairs are added to the ssh config
func (s *StepImportOSLoginSSHKey) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(*Config)
driver := state.Get("driver").(Driver)
ui := state.Get("ui").(packer.Ui)
if !config.UseOSLogin {
return multistep.ActionContinue
}
// If no public key information is available chances are that a private key was provided
// or that the user is using a SSH agent for authentication.
if config.Comm.SSHPublicKey == nil {
ui.Say("No public SSH key found; skipping SSH public key import for OSLogin...")
return multistep.ActionContinue
}
if s.TokeninfoFunc == nil {
s.TokeninfoFunc = tokeninfo
}
ui.Say("Importing SSH public key for OSLogin...")
// Generate SHA256 fingerprint of SSH public key
// Put it into state to clean up later
sha256sum := sha256.Sum256(config.Comm.SSHPublicKey)
state.Put("ssh_key_public_sha256", hex.EncodeToString(sha256sum[:]))
if config.account != nil {
s.accountEmail = config.account.Email
}
if s.accountEmail == "" {
info, err := s.TokeninfoFunc(ctx)
if err != nil {
err := fmt.Errorf("Error obtaining token information needed for OSLogin: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
s.accountEmail = info.Email
}
loginProfile, err := driver.ImportOSLoginSSHKey(s.accountEmail, string(config.Comm.SSHPublicKey))
if err != nil {
err := fmt.Errorf("Error importing SSH public key for OSLogin: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
// Replacing `SSHUsername` as the username have to be from OSLogin
if len(loginProfile.PosixAccounts) == 0 {
err := fmt.Errorf("Error importing SSH public key for OSLogin: no PosixAccounts available")
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
// Let's obtain the `Primary` account username
ui.Say("Obtaining SSH Username for OSLogin...")
var username string
for _, account := range loginProfile.PosixAccounts {
if account.Primary {
username = account.Username
break
}
}
if s.Debug {
ui.Message(fmt.Sprintf("ssh_username: %s", username))
}
config.Comm.SSHUsername = username
return multistep.ActionContinue
}
// Cleanup the SSH Key that we added to the POSIX account
func (s *StepImportOSLoginSSHKey) Cleanup(state multistep.StateBag) {
config := state.Get("config").(*Config)
driver := state.Get("driver").(Driver)
ui := state.Get("ui").(packer.Ui)
if !config.UseOSLogin {
return
}
fingerprint, ok := state.Get("ssh_key_public_sha256").(string)
if !ok || fingerprint == "" {
return
}
ui.Say("Deleting SSH public key for OSLogin...")
err := driver.DeleteOSLoginSSHKey(s.accountEmail, fingerprint)
if err != nil {
ui.Error(fmt.Sprintf("Error deleting SSH public key for OSLogin. Please delete it manually.\n\nError: %s", err))
return
}
ui.Message("SSH public key for OSLogin has been deleted!")
}
func tokeninfo(ctx context.Context) (*oauth2.Tokeninfo, error) {
svc, err := oauth2.NewService(ctx)
if err != nil {
err := fmt.Errorf("Error initializing oauth service needed for OSLogin: %s", err)
return nil, err
}
return svc.Tokeninfo().Context(ctx).Do()
}