Skip to content

Commit

Permalink
[feat] add temp SSH key ability to iso builder
Browse files Browse the repository at this point in the history
The documentation for proxmox-iso states that a temporary SSH key will
be created if no SSH credentials are passed.  This was not the case and
this commit remedies that.
  • Loading branch information
bl1nk1n committed Dec 3, 2024
1 parent ec7ac1b commit f9a49bb
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 11 deletions.
4 changes: 0 additions & 4 deletions builder/proxmox/clone/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,6 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
state.Put("clone-config", &b.config)

preSteps := []multistep.Step{
&StepSshKeyPair{
Debug: b.config.PackerDebug,
DebugKeyPath: fmt.Sprintf("%s.pem", b.config.PackerBuildName),
},
&StepMapSourceDisks{},
}
postSteps := []multistep.Step{}
Expand Down
16 changes: 12 additions & 4 deletions builder/proxmox/common/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,11 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook,

// Build the steps
coreSteps := []multistep.Step{
// Since the ISOs get added to the VM before/during this line we need to update the cloud-init data before this line. It probably needs to be before as CD creation is done in presteps
&stepStartVM{
vmCreator: b.vmCreator,
},
// Also need to update the cloud-init data before this line
commonsteps.HTTPServerFromHTTPConfig(&b.config.HTTPConfig),
&stepTypeBootCommand{
BootConfig: b.config.BootConfig,
Expand All @@ -67,14 +69,21 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook,
},
&commonsteps.StepProvision{},
&commonsteps.StepCleanupTempKeys{
Comm: &b.config.Comm,
Comm: comm,
},
&stepRemoveCloudInitDrive{},
&stepConvertToTemplate{},
&stepFinalizeTemplateConfig{},
&stepSuccess{},
}
preSteps := b.preSteps

preSteps := []multistep.Step{
&StepSshKeyPair{
Debug: b.config.PackerDebug,
DebugKeyPath: fmt.Sprintf("%s.pem", b.config.PackerBuildName),
},
&StepUpdateCloudInitSSH{},
}
for idx := range b.config.ISOs {
if b.config.ISOs[idx].ISODownloadPVE {
preSteps = append(preSteps,
Expand Down Expand Up @@ -104,8 +113,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook,
}
}

steps := append(preSteps, coreSteps...)
steps = append(steps, b.postSteps...)
steps := append(preSteps, b.preSteps..., coreSteps..., b.postSteps...)
// Run the steps
b.runner = commonsteps.NewRunner(steps, b.config.PackerConfig, ui)
b.runner.Run(ctx, state)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package proxmoxclone
package proxmox

import (
"context"
Expand Down Expand Up @@ -47,9 +47,8 @@ func (s *StepSshKeyPair) Run(ctx context.Context, state multistep.StateBag) mult
return multistep.ActionHalt
}

c.Comm.SSHPrivateKey = privateKeyBytes
c.Comm.SSHKeyPairName = kp.Comment
c.Comm.SSHTemporaryKeyPairName = kp.Comment
c.Comm.SSHPrivateKey = privateKeyBytes
c.Comm.SSHPublicKey = kp.PublicKeyAuthorizedKeysLine

return multistep.ActionContinue
Expand Down
95 changes: 95 additions & 0 deletions builder/proxmox/common/step_update_cloud_init_ssh.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package proxmox

import (
"context"
"os"
"slices"
"strings"
"fmt"

packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
common "github.com/hashicorp/packer-plugin-proxmox/builder/proxmox/common"
"github.com/hashicorp/packer-plugin-sdk/multistep"
)

type StepUpdateCloudInitSSH struct{}

func (s *StepUpdateCloudInitSSH) Run(ctx context.Context, state multiste.StateBag) multistep.StepAction {
// NOTE: Can pass Cloud-Init data via CD or HTTP Server

ui := state.Get("ui").(packersdk.Ui)
c := state.Get("config").(*common.Config)

if c.Comm.SSHTemporaryKeyPairName == "" ||
(len(c.ISOs) <= 1 &&
c.HTTPConfig.HTTPDir == "" &&
len(c.HTTPConfig.HTTPContent) == 0) {
return multistep.ActionContinue
}

temp_ssh_public_key := string(c.Comm.SSHPublicKey)

if len(c.ISOs) > 1 {
// Skip first ISO as it should be the BootISO
for idx := range c.ISOs[1:] {
if c.ISOs[idx].CDConfig.CDLabel != "cidata" {
continue
}

if value, ok := c.ISOs[idx].CDConfig.CDContent["user-data"]; ok {
c.ISOs[idx].CDConfig.CDContent["user-data"] = strings.ReplaceAll(value, "${temporary_ssh_public_key}", temp_ssh_public_key)
ui.Say("Updated 'user-data' CD Content to use temporary SSH public key")
// CDContent will take precedence over CDFiles
break
}

// TODO: This needs to be able to handle when directories and globs
// are passed to CDFiles
for jdx, value := range c.ISOs[idx].CDConfig.CDFiles {
if slices.Contains(value, "user-data") {
dat, err := os.ReadFile(c.ISOs[idx].CDConfig.CDFiles[jdx])
if err != nil {
// It is ok if file does not exist
break
}
// Choosing to write CDContent to avoid overwriting original
// file or creating a new file with the temporary public key
c.ISOs[idx].CDConfig.CDContent["user-data"] = strings.ReplaceAll(dat, "${temporary_ssh_public_key}", temp_ssh_public_key)
ui.Say("Created 'user-data' CD Content to override CD File and use temporary SSH public key")
// There can only be one user-data file
break
}
}
}
}

if c.HTTPConfig.HTTPDir != "" {
user_data_file := fmt.Sprintf("%s/user-data", c.HTTPConfig.HTTPDir)

dat, err := os.ReadFile(user_data_file)
if err != nil {
// It is ok if file does not exist
return multistep.ActionContinue
}

// Can't just write to "HTTPContent". Since it directly conflicts with
// "HTTPDir", we would have to walk and load every file from "HTTPDir"
// into "HTTPContent"
err := os.WriteFile(user_data_file, strings.ReplaceAll(dat, "${temporary_ssh_public_key}", temp_ssh_public_key), 0600)
if err == nil {
ui.Say(fmt.Sprintf("Rewrote '%s' in HTTPDir to use temporary SSH public key", user_data_file))
} else {
ui.Say(fmt.Sprintf("Failed to rewrite '%s' in HTTPDir to use temporary SSH public key", user_data_file))
}
} else if len(c.HTTPConfig.HTTPContent) > 0 {
if value, ok := c.HTTPConfig.HTTPContent["user-data"]; ok {
c.HTTPConfig.HTTPContent["user-data"] = strings.ReplaceAll(value, "%{temporary_ssh_public_key}", temp_ssh_public_key)
ui.Say("Updated 'user-data' HTTP Content to use temporary SSH public key")
}
}

return multistep.ActionContinue
}

0 comments on commit f9a49bb

Please sign in to comment.