Skip to content

Commit 047fbf7

Browse files
authored
Merge pull request #319 from y1hao/yihao/expire
Expose "oslogin_ssh_key_expire_after" as a parameter
2 parents cdb9d3c + 161f926 commit 047fbf7

9 files changed

Lines changed: 124 additions & 6 deletions

File tree

.web-docs/components/builder/googlecompute/README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,11 @@ builder.
372372

373373
Note: Invalid or unsupported values will result in an error during provisioning.
374374

375+
- `oslogin_ssh_key_expire_after` (duration string | ex: "1h5m2s") - The duration after which an SSH key imported into OS Login will expire.
376+
This parameter is used when an SSH key is imported into the OS Login profile.
377+
The expiration time is calculated from the current time plus this duration.
378+
Example value: `1h`, `30m`, `24h`.
379+
375380
- `wait_to_add_ssh_keys` (duration string | ex: "1h5m2s") - The time to wait between the creation of the instance used to create the image,
376381
and the addition of SSH configuration, including SSH keys, to that instance.
377382
The delay is intended to protect packer from anything in the instance boot

builder/googlecompute/config.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,11 @@ type Config struct {
369369
//
370370
// Note: Invalid or unsupported values will result in an error during provisioning.
371371
OSLoginSSHUsername string `mapstructure:"oslogin_ssh_username" required:"false"`
372+
// The duration after which an SSH key imported into OS Login will expire.
373+
// This parameter is used when an SSH key is imported into the OS Login profile.
374+
// The expiration time is calculated from the current time plus this duration.
375+
// Example value: `1h`, `30m`, `24h`.
376+
OSLoginSSHKeyExpireAfter time.Duration `mapstructure:"oslogin_ssh_key_expire_after" required:"false"`
372377
// The time to wait between the creation of the instance used to create the image,
373378
// and the addition of SSH configuration, including SSH keys, to that instance.
374379
// The delay is intended to protect packer from anything in the instance boot

builder/googlecompute/config.hcl2spec.go

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

builder/googlecompute/step_import_os_login_ssh_key.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,16 @@ func (s *StepImportOSLoginSSHKey) Run(ctx context.Context, state multistep.State
102102
return multistep.ActionHalt
103103
}
104104

105-
loginProfile, err := driver.ImportOSLoginSSHKey(s.accountEmail, string(config.Comm.SSHPublicKey))
105+
// Calculate expiration time if oslogin_ssh_key_expire_after is set
106+
// This is used when an SSH key is imported into the OS Login profile
107+
var expirationTimeUsec *int64
108+
if config.OSLoginSSHKeyExpireAfter > 0 {
109+
expirationTime := time.Now().Add(config.OSLoginSSHKeyExpireAfter)
110+
expirationTimeUsecVal := expirationTime.UnixMicro()
111+
expirationTimeUsec = &expirationTimeUsecVal
112+
}
113+
114+
loginProfile, err := driver.ImportOSLoginSSHKey(s.accountEmail, string(config.Comm.SSHPublicKey), expirationTimeUsec)
106115
if err != nil {
107116
err := fmt.Errorf("Error importing SSH public key for OSLogin: %s", err)
108117
state.Put("error", err)

builder/googlecompute/step_import_os_login_ssh_key_test.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ import (
99
"encoding/hex"
1010
"os"
1111
"testing"
12+
"time"
1213

14+
"github.com/hashicorp/packer-plugin-googlecompute/lib/common"
1315
"github.com/hashicorp/packer-plugin-sdk/multistep"
1416
configsdk "github.com/hashicorp/packer-plugin-sdk/template/config"
1517
"google.golang.org/api/oauth2/v2"
@@ -309,3 +311,79 @@ func TestGetOSLoginUsername(t *testing.T) {
309311
})
310312
}
311313
}
314+
315+
func TestStepImportOSLoginSSHKey_withExpirationTime(t *testing.T) {
316+
state := testState(t)
317+
fakeAccountEmail := "raffi-compute@developer.gserviceaccount.com"
318+
driver := state.Get("driver").(*common.DriverMock)
319+
step := &StepImportOSLoginSSHKey{
320+
TokeninfoFunc: func() (*oauth2.Tokeninfo, error) {
321+
return &oauth2.Tokeninfo{Email: fakeAccountEmail}, nil
322+
},
323+
}
324+
defer step.Cleanup(state)
325+
326+
config := state.Get("config").(*Config)
327+
config.UseOSLogin = configsdk.TriTrue
328+
config.Comm.SSHPublicKey = []byte{'k', 'e', 'y'}
329+
config.OSLoginSSHKeyExpireAfter = 1 * time.Hour
330+
331+
beforeTime := time.Now()
332+
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
333+
t.Fatalf("bad action: %#v", action)
334+
}
335+
afterTime := time.Now()
336+
337+
if driver.ImportOSLoginSSHKeyExpirationTime == nil {
338+
t.Fatal("expected expiration time to be set, but got nil")
339+
}
340+
341+
expectedExpirationMin := beforeTime.Add(config.OSLoginSSHKeyExpireAfter).UnixMicro()
342+
expectedExpirationMax := afterTime.Add(config.OSLoginSSHKeyExpireAfter).UnixMicro()
343+
actualExpiration := *driver.ImportOSLoginSSHKeyExpirationTime
344+
345+
if actualExpiration < expectedExpirationMin || actualExpiration > expectedExpirationMax {
346+
t.Errorf("expected expiration time to be between %d and %d, but got %d", expectedExpirationMin, expectedExpirationMax, actualExpiration)
347+
}
348+
349+
if driver.ImportOSLoginSSHKeyUser != fakeAccountEmail {
350+
t.Errorf("expected user to be %q, but got %q", fakeAccountEmail, driver.ImportOSLoginSSHKeyUser)
351+
}
352+
353+
if driver.ImportOSLoginSSHKeyKey != "key" {
354+
t.Errorf("expected key to be %q, but got %q", "key", driver.ImportOSLoginSSHKeyKey)
355+
}
356+
}
357+
358+
func TestStepImportOSLoginSSHKey_withoutExpirationTime(t *testing.T) {
359+
state := testState(t)
360+
fakeAccountEmail := "raffi-compute@developer.gserviceaccount.com"
361+
driver := state.Get("driver").(*common.DriverMock)
362+
step := &StepImportOSLoginSSHKey{
363+
TokeninfoFunc: func() (*oauth2.Tokeninfo, error) {
364+
return &oauth2.Tokeninfo{Email: fakeAccountEmail}, nil
365+
},
366+
}
367+
defer step.Cleanup(state)
368+
369+
config := state.Get("config").(*Config)
370+
config.UseOSLogin = configsdk.TriTrue
371+
config.Comm.SSHPublicKey = []byte{'k', 'e', 'y'}
372+
config.OSLoginSSHKeyExpireAfter = 0 // Not set
373+
374+
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
375+
t.Fatalf("bad action: %#v", action)
376+
}
377+
378+
if driver.ImportOSLoginSSHKeyExpirationTime != nil {
379+
t.Errorf("expected expiration time to be nil when not set, but got %v", *driver.ImportOSLoginSSHKeyExpirationTime)
380+
}
381+
382+
if driver.ImportOSLoginSSHKeyUser != fakeAccountEmail {
383+
t.Errorf("expected user to be %q, but got %q", fakeAccountEmail, driver.ImportOSLoginSSHKeyUser)
384+
}
385+
386+
if driver.ImportOSLoginSSHKeyKey != "key" {
387+
t.Errorf("expected key to be %q, but got %q", "key", driver.ImportOSLoginSSHKeyKey)
388+
}
389+
}

docs-partials/builder/googlecompute/Config-not-required.mdx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,11 @@
318318

319319
Note: Invalid or unsupported values will result in an error during provisioning.
320320

321+
- `oslogin_ssh_key_expire_after` (duration string | ex: "1h5m2s") - The duration after which an SSH key imported into OS Login will expire.
322+
This parameter is used when an SSH key is imported into the OS Login profile.
323+
The expiration time is calculated from the current time plus this duration.
324+
Example value: `1h`, `30m`, `24h`.
325+
321326
- `wait_to_add_ssh_keys` (duration string | ex: "1h5m2s") - The time to wait between the creation of the instance used to create the image,
322327
and the addition of SSH configuration, including SSH keys, to that instance.
323328
The delay is intended to protect packer from anything in the instance boot

lib/common/driver.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,9 @@ type Driver interface {
8686
CreateOrResetWindowsPassword(zone, name string, config *WindowsPasswordConfig) (<-chan error, error)
8787

8888
// ImportOSLoginSSHKey imports SSH public key for OSLogin.
89-
ImportOSLoginSSHKey(user, sshPublicKey string) (*oslogin.LoginProfile, error)
89+
// expirationTimeUsec is an optional expiration time in microseconds since Unix epoch.
90+
// If nil, no expiration time will be set.
91+
ImportOSLoginSSHKey(user, sshPublicKey string, expirationTimeUsec *int64) (*oslogin.LoginProfile, error)
9092

9193
// DeleteOSLoginSSHKey deletes the SSH public key for OSLogin with the given key.
9294
DeleteOSLoginSSHKey(user, fingerprint string) error

lib/common/driver_gce.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -898,12 +898,17 @@ func (d *driverGCE) getPasswordResponses(zone, instance string) ([]windowsPasswo
898898
return passwordResponses, nil
899899
}
900900

901-
func (d *driverGCE) ImportOSLoginSSHKey(user, sshPublicKey string) (*oslogin.LoginProfile, error) {
901+
func (d *driverGCE) ImportOSLoginSSHKey(user, sshPublicKey string, expirationTimeUsec *int64) (*oslogin.LoginProfile, error) {
902902
parent := fmt.Sprintf("users/%s", user)
903903

904-
resp, err := d.osLoginService.Users.ImportSshPublicKey(parent, &oslogin.SshPublicKey{
904+
sshKey := &oslogin.SshPublicKey{
905905
Key: sshPublicKey,
906-
}).Do()
906+
}
907+
if expirationTimeUsec != nil {
908+
sshKey.ExpirationTimeUsec = *expirationTimeUsec
909+
}
910+
911+
resp, err := d.osLoginService.Users.ImportSshPublicKey(parent, sshKey).Do()
907912
if err != nil {
908913
return nil, err
909914
}

lib/common/driver_mock.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,10 @@ type DriverMock struct {
124124
UploadToBucketData io.Reader
125125
UploadToBucketResult string
126126
UploadToBucketError error
127+
128+
ImportOSLoginSSHKeyUser string
129+
ImportOSLoginSSHKeyKey string
130+
ImportOSLoginSSHKeyExpirationTime *int64
127131
}
128132

129133
func (d *DriverMock) CreateImage(project string, imageSpec *compute.Image) (<-chan *Image, <-chan error) {
@@ -379,7 +383,10 @@ func (d *DriverMock) CreateOrResetWindowsPassword(instance, zone string, c *Wind
379383
return resultCh, d.CreateOrResetWindowsPasswordErr
380384
}
381385

382-
func (d *DriverMock) ImportOSLoginSSHKey(user, key string) (*oslogin.LoginProfile, error) {
386+
func (d *DriverMock) ImportOSLoginSSHKey(user, key string, expirationTimeUsec *int64) (*oslogin.LoginProfile, error) {
387+
d.ImportOSLoginSSHKeyUser = user
388+
d.ImportOSLoginSSHKeyKey = key
389+
d.ImportOSLoginSSHKeyExpirationTime = expirationTimeUsec
383390
account := oslogin.PosixAccount{Primary: true, Username: "testing_packer_io"}
384391
profile := oslogin.LoginProfile{
385392
PosixAccounts: []*oslogin.PosixAccount{&account},

0 commit comments

Comments
 (0)