Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: [feat] add temporary SSH key generation #302

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions builder/proxmox/clone/builder.go
Original file line number Diff line number Diff line change
@@ -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{}
16 changes: 12 additions & 4 deletions builder/proxmox/common/builder.go
Original file line number Diff line number Diff line change
@@ -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,
@@ -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,
@@ -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)
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"
@@ -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
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
}