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
Show file tree
Hide file tree
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
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
}