* Unentagled SSH communication with VM's from the Cloud API SSH private

key.
* Improved documentation.
This commit is contained in:
Jasper Siepkes 2016-12-29 13:24:56 +01:00
parent 4b651ce3c2
commit 0c9b576c05
3 changed files with 103 additions and 31 deletions

View File

@ -31,16 +31,11 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
errs = multierror.Append(errs, err) errs = multierror.Append(errs, err)
} }
// In Triton only the root user is setup in a VM.
b.config.Comm.SSHUsername = "root"
errs = multierror.Append(errs, b.config.AccessConfig.Prepare(&b.config.ctx)...) errs = multierror.Append(errs, b.config.AccessConfig.Prepare(&b.config.ctx)...)
errs = multierror.Append(errs, b.config.SourceMachineConfig.Prepare(&b.config.ctx)...) errs = multierror.Append(errs, b.config.SourceMachineConfig.Prepare(&b.config.ctx)...)
errs = multierror.Append(errs, b.config.Comm.Prepare(&b.config.ctx)...) errs = multierror.Append(errs, b.config.Comm.Prepare(&b.config.ctx)...)
errs = multierror.Append(errs, b.config.TargetImageConfig.Prepare(&b.config.ctx)...) errs = multierror.Append(errs, b.config.TargetImageConfig.Prepare(&b.config.ctx)...)
b.config.Comm.SSHPrivateKey = b.config.KeyMaterial
return nil, errs.ErrorOrNil() return nil, errs.ErrorOrNil()
} }
@ -61,9 +56,13 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
steps := []multistep.Step{ steps := []multistep.Step{
&StepCreateSourceMachine{}, &StepCreateSourceMachine{},
&communicator.StepConnect{ &communicator.StepConnect{
Config: &config.Comm, Config: &config.Comm,
Host: commHost, Host: commHost,
SSHConfig: sshConfig, SSHConfig: sshConfig(
b.config.Comm.SSHAgentAuth,
b.config.Comm.SSHUsername,
b.config.Comm.SSHPrivateKey,
b.config.Comm.SSHPassword),
}, },
&common.StepProvision{}, &common.StepProvision{},
&StepStopMachine{}, &StepStopMachine{},

View File

@ -4,7 +4,13 @@ import (
"fmt" "fmt"
"github.com/mitchellh/multistep" "github.com/mitchellh/multistep"
packerssh "github.com/mitchellh/packer/communicator/ssh"
"golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/agent"
"io/ioutil"
"log"
"net"
"os"
) )
func commHost(state multistep.StateBag) (string, error) { func commHost(state multistep.StateBag) (string, error) {
@ -19,18 +25,64 @@ func commHost(state multistep.StateBag) (string, error) {
return machine, nil return machine, nil
} }
func sshConfig(state multistep.StateBag) (*ssh.ClientConfig, error) { // SSHConfig returns a function that can be used for the SSH communicator
config := state.Get("config").(Config) // config for connecting to the instance created over SSH using the private key
// or password.
func sshConfig(useAgent bool, username, privateKeyPath, password string) func(multistep.StateBag) (*ssh.ClientConfig, error) {
return func(state multistep.StateBag) (*ssh.ClientConfig, error) {
signer, err := ssh.ParsePrivateKey([]byte(config.Comm.SSHPrivateKey)) if useAgent {
if err != nil { log.Println("Configuring SSH agent.")
return nil, fmt.Errorf("Error setting up SSH config: %s", err)
authSock := os.Getenv("SSH_AUTH_SOCK")
if authSock == "" {
return nil, fmt.Errorf("SSH_AUTH_SOCK is not set")
}
sshAgent, err := net.Dial("unix", authSock)
if err != nil {
return nil, fmt.Errorf("Cannot connect to SSH Agent socket %q: %s", authSock, err)
}
return &ssh.ClientConfig{
User: username,
Auth: []ssh.AuthMethod{
ssh.PublicKeysCallback(agent.NewClient(sshAgent).Signers),
},
}, nil
}
hasKey := privateKeyPath != ""
if hasKey {
log.Printf("Configuring SSH private key '%s'.", privateKeyPath)
privateKeyBytes, err := ioutil.ReadFile(privateKeyPath)
if err != nil {
return nil, fmt.Errorf("Unable to read SSH private key: %s", err)
}
signer, err := ssh.ParsePrivateKey(privateKeyBytes)
if err != nil {
return nil, fmt.Errorf("Error setting up SSH config: %s", err)
}
return &ssh.ClientConfig{
User: username,
Auth: []ssh.AuthMethod{
ssh.PublicKeys(signer),
},
}, nil
} else {
log.Println("Configuring SSH keyboard interactive.")
return &ssh.ClientConfig{
User: username,
Auth: []ssh.AuthMethod{
ssh.Password(password),
ssh.KeyboardInteractive(
packerssh.PasswordKeyboardInteractive(password)),
}}, nil
}
} }
return &ssh.ClientConfig{
User: config.Comm.SSHUsername,
Auth: []ssh.AuthMethod{
ssh.PublicKeys(signer),
},
}, nil
} }

View File

@ -18,12 +18,16 @@ Type: `triton`
The `triton` Packer builder is able to create new images for use with Triton. The `triton` Packer builder is able to create new images for use with Triton.
These images can be used with both the [Joyent public These images can be used with both the [Joyent public
cloud](https://www.joyent.com/) (which is powered by Triton) as well with cloud](https://www.joyent.com/) (which is powered by Triton) as well with
private [Triton](https://github.com/joyent/triton) installations. This builder private [Triton](https://github.com/joyent/triton) installations.
uses the Triton Cloud API to create images. The builder creates and launches a
temporary VM based on a specified source image, runs any provisioning necessary, This builder uses the Triton Cloud API to create these images. Triton also
uses the Triton "VM to image" functionality to create a reusable image and supports the Docker API however this builder does *not*. If you want to create
finally destroys the temporary VM. This reusable image can then be used to Docker images on Triton you should use the Packer Docker builder.
launch new VM's.
The builder creates and launches a temporary VM based on a specified source
image, runs any provisioning necessary, uses the Triton "VM to image"
functionality to create a reusable image and finally destroys the temporary VM.
This reusable image can then be used to launch new VM's.
The builder does *not* manage images. Once it creates an image, it is up to you The builder does *not* manage images. Once it creates an image, it is up to you
to use it or delete it. to use it or delete it.
@ -47,9 +51,16 @@ builder.
of `triton_key_id` is stored. For example `~/.ssh/id_rsa`. of `triton_key_id` is stored. For example `~/.ssh/id_rsa`.
- `source_machine_image` (string) - The UUID of the image to base the new - `source_machine_image` (string) - The UUID of the image to base the new
image on. On the Joyent public cloud this could for example be image on. Triton supports multiple types of images, called 'brands' in
Triton / Joyent lingo, for contains and VM's. See the chapter [Containers
and virtual machines](https://docs.joyent.com/public-cloud/instances) in the
Joyent Triton documentation for detailed information. The following brands
are currently supported by this builder:`joyent` and`kvm`. The choice of
base image automatically decides the brand. On the Joyent public cloud a
valid `source_machine_image` could for example be
`70e3ae72-96b6-11e6-9056-9737fd4d0764` for version 16.3.1 of the 64bit `70e3ae72-96b6-11e6-9056-9737fd4d0764` for version 16.3.1 of the 64bit
SmartOS base image. SmartOS base image (a 'joyent' brand image).
- `source_machine_package` (string) - The Triton package to use while building - `source_machine_package` (string) - The Triton package to use while building
the image. Does not affect (and does not have to be the same) as the package the image. Does not affect (and does not have to be the same) as the package
which will be used for a VM instance running this image. On the Joyent which will be used for a VM instance running this image. On the Joyent
@ -111,18 +122,28 @@ builder.
## Basic Example ## Basic Example
Below is a minimal example to create an image on the Joyent public cloud: Below is a minimal example to create an joyent-brand image on the Joyent public
cloud:
``` {.javascript} ``` {.javascript}
"builders": [{ "builders": [{
"type": "triton", "type": "triton",
"triton_account": "triton_username", "triton_account": "triton_username",
"triton_key_id": "6b:95:03:3d:d3:6e:52:69:01:96:1a:46:4a:8d:c1:7e", "triton_key_id": "6b:95:03:3d:d3:6e:52:69:01:96:1a:46:4a:8d:c1:7e",
"triton_key_material": "${file("~/.ssh/id_rsa")}", "triton_key_material": "~/.ssh/id_rsa",
"source_machine_name": "image-builder", "source_machine_name": "image-builder",
"source_machine_package": "g3-standard-0.5-smartos", "source_machine_package": "g3-standard-0.5-smartos",
"source_machine_image": "70e3ae72-96b6-11e6-9056-9737fd4d0764", "source_machine_image": "70e3ae72-96b6-11e6-9056-9737fd4d0764",
"ssh_username": "root",
"ssh_private_key_file": "~/.ssh/id_rsa",
"image_name": "my_new_image", "image_name": "my_new_image",
"image_version": "1.0.0", "image_version": "1.0.0",
}], }],
``` ```
In the above example the SSH key used for `triton_key_material` (connecting to
the Cloud API) and the `ssh_private_key_file` (connecting to the VM once it has
started) are the same. This is because Triton automatically configures the root
users to be able to login via SSH with the same key used to create the VM via
the Cloud API. In more advanced scenarios for example when using a
`source_machine_image` one might use different credentials.