Gcp oslogin issue 10170 (#10360)
This commit is contained in:
parent
f8e85f94fd
commit
355b93730b
|
@ -70,6 +70,9 @@ type Driver interface {
|
|||
// DeleteOSLoginSSHKey deletes the SSH public key for OSLogin with the given key.
|
||||
DeleteOSLoginSSHKey(user, fingerprint string) error
|
||||
|
||||
// If packer is running on a GCE, derives the user from it for use with OSLogin.
|
||||
GetOSLoginUserFromGCE() string
|
||||
|
||||
// Add to the instance metadata for the existing instance
|
||||
AddToInstanceMetadata(zone string, name string, metadata map[string]string) error
|
||||
}
|
||||
|
|
|
@ -10,9 +10,11 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
metadata "cloud.google.com/go/compute/metadata"
|
||||
compute "google.golang.org/api/compute/v1"
|
||||
"google.golang.org/api/option"
|
||||
oslogin "google.golang.org/api/oslogin/v1"
|
||||
|
@ -32,6 +34,7 @@ import (
|
|||
type driverGCE struct {
|
||||
projectId string
|
||||
service *compute.Service
|
||||
thisGCEUser string
|
||||
osLoginService *oslogin.Service
|
||||
ui packersdk.Ui
|
||||
}
|
||||
|
@ -119,6 +122,9 @@ func NewClientOptionGoogle(account *ServiceAccount, vaultOauth string, impersona
|
|||
// 4. On Google Compute Engine and Google App Engine Managed VMs, it fetches
|
||||
// credentials from the metadata server.
|
||||
// (In this final case any provided scopes are ignored.)
|
||||
//
|
||||
// Note: (4) is not usable with OSLogin on Google Compute Engine (GCE).
|
||||
// The GCE service account is derived separately and used instead.
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
|
@ -129,6 +135,9 @@ func NewClientOptionGoogle(account *ServiceAccount, vaultOauth string, impersona
|
|||
}
|
||||
|
||||
func NewDriverGCE(config GCEDriverConfig) (Driver, error) {
|
||||
|
||||
var thisGCEUser string
|
||||
|
||||
opts, err := NewClientOptionGoogle(config.Account, config.VaultOauthEngineName, config.ImpersonateServiceAccountName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -140,6 +149,15 @@ func NewDriverGCE(config GCEDriverConfig) (Driver, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if metadata.OnGCE() {
|
||||
log.Printf("[INFO] On GCE, capture service account for OSLogin...")
|
||||
thisGCEUser, err = metadata.NewClient(&http.Client{}).Email("")
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("[INFO] Instantiating OS Login client...")
|
||||
osLoginService, err := oslogin.NewService(context.TODO(), opts)
|
||||
if err != nil {
|
||||
|
@ -152,6 +170,7 @@ func NewDriverGCE(config GCEDriverConfig) (Driver, error) {
|
|||
return &driverGCE{
|
||||
projectId: config.ProjectId,
|
||||
service: service,
|
||||
thisGCEUser: thisGCEUser,
|
||||
osLoginService: osLoginService,
|
||||
ui: config.Ui,
|
||||
}, nil
|
||||
|
@ -625,8 +644,13 @@ func (d *driverGCE) getPasswordResponses(zone, instance string) ([]windowsPasswo
|
|||
return passwordResponses, nil
|
||||
}
|
||||
|
||||
func (d *driverGCE) GetOSLoginUserFromGCE() string {
|
||||
return d.thisGCEUser
|
||||
}
|
||||
|
||||
func (d *driverGCE) ImportOSLoginSSHKey(user, sshPublicKey string) (*oslogin.LoginProfile, error) {
|
||||
parent := fmt.Sprintf("users/%s", user)
|
||||
|
||||
resp, err := d.osLoginService.Users.ImportSshPublicKey(parent, &oslogin.SshPublicKey{
|
||||
Key: sshPublicKey,
|
||||
}).Do()
|
||||
|
|
|
@ -94,6 +94,8 @@ type DriverMock struct {
|
|||
AddToInstanceMetadataKVPairs map[string]string
|
||||
AddToInstanceMetadataErrCh <-chan error
|
||||
AddToInstanceMetadataErr error
|
||||
|
||||
OSLoginUserFromGCE string
|
||||
}
|
||||
|
||||
func (d *DriverMock) CreateImage(name, description, family, zone, disk string, image_labels map[string]string, image_licenses []string, image_encryption_key *compute.CustomerEncryptionKey, imageStorageLocations []string) (<-chan *Image, <-chan error) {
|
||||
|
@ -308,3 +310,7 @@ func (d *DriverMock) AddToInstanceMetadata(zone string, name string, metadata ma
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DriverMock) GetOSLoginUserFromGCE() string {
|
||||
return d.OSLoginUserFromGCE
|
||||
}
|
||||
|
|
|
@ -36,7 +36,10 @@ func (s *StepImportOSLoginSSHKey) Run(ctx context.Context, state multistep.State
|
|||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
if s.TokeninfoFunc == nil {
|
||||
// Are we running packer on a GCE ?
|
||||
s.accountEmail = driver.GetOSLoginUserFromGCE()
|
||||
|
||||
if s.TokeninfoFunc == nil && s.accountEmail == "" {
|
||||
s.TokeninfoFunc = tokeninfo
|
||||
}
|
||||
|
||||
|
@ -46,7 +49,7 @@ func (s *StepImportOSLoginSSHKey) Run(ctx context.Context, state multistep.State
|
|||
sha256sum := sha256.Sum256(config.Comm.SSHPublicKey)
|
||||
state.Put("ssh_key_public_sha256", hex.EncodeToString(sha256sum[:]))
|
||||
|
||||
if config.account != nil {
|
||||
if config.account != nil && s.accountEmail == "" {
|
||||
s.accountEmail = config.account.jwt.Email
|
||||
}
|
||||
|
||||
|
@ -62,6 +65,13 @@ func (s *StepImportOSLoginSSHKey) Run(ctx context.Context, state multistep.State
|
|||
s.accountEmail = info.Email
|
||||
}
|
||||
|
||||
if s.accountEmail == "" {
|
||||
err := fmt.Errorf("All options for deriving the OSLogin user have been exhausted")
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
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)
|
||||
|
|
|
@ -150,3 +150,37 @@ func TestStepImportOSLoginSSHKey_withPrivateSSHKey(t *testing.T) {
|
|||
t.Errorf("expected to not see a public key when using a dedicated private key, but got %q", pubKey)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepImportOSLoginSSHKey_onGCE(t *testing.T) {
|
||||
|
||||
state := testState(t)
|
||||
d := state.Get("driver").(*DriverMock)
|
||||
step := new(StepImportOSLoginSSHKey)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
fakeAccountEmail := "testing@packer.io"
|
||||
|
||||
config := state.Get("config").(*Config)
|
||||
config.UseOSLogin = true
|
||||
config.Comm.SSHPublicKey = []byte{'k', 'e', 'y'}
|
||||
|
||||
d.OSLoginUserFromGCE = fakeAccountEmail
|
||||
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
if step.accountEmail != fakeAccountEmail {
|
||||
t.Fatalf("expected accountEmail to be %q but got %q", fakeAccountEmail, step.accountEmail)
|
||||
}
|
||||
|
||||
pubKey, ok := state.GetOk("ssh_key_public_sha256")
|
||||
if !ok {
|
||||
t.Fatal("expected to see a public key")
|
||||
}
|
||||
|
||||
sha256sum := sha256.Sum256(config.Comm.SSHPublicKey)
|
||||
if pubKey != hex.EncodeToString(sha256sum[:]) {
|
||||
t.Errorf("expected to see a matching public key, but got %q", pubKey)
|
||||
}
|
||||
}
|
||||
|
|
2
go.mod
2
go.mod
|
@ -1,7 +1,7 @@
|
|||
module github.com/hashicorp/packer
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.66.0 // indirect
|
||||
cloud.google.com/go v0.66.0
|
||||
github.com/1and1/oneandone-cloudserver-sdk-go v1.0.1
|
||||
github.com/Azure/azure-sdk-for-go v40.5.0+incompatible
|
||||
github.com/Azure/go-autorest/autorest v0.10.0
|
||||
|
|
Loading…
Reference in New Issue