* Unentagled SSH communication with VM's from the Cloud API SSH private
key. * Improved documentation.
This commit is contained in:
parent
4b651ce3c2
commit
0c9b576c05
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,7 +58,11 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
||||||
&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{},
|
||||||
|
|
|
@ -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 {
|
||||||
|
log.Println("Configuring SSH agent.")
|
||||||
|
|
||||||
|
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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Error setting up SSH config: %s", err)
|
return nil, fmt.Errorf("Error setting up SSH config: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &ssh.ClientConfig{
|
return &ssh.ClientConfig{
|
||||||
User: config.Comm.SSHUsername,
|
User: username,
|
||||||
Auth: []ssh.AuthMethod{
|
Auth: []ssh.AuthMethod{
|
||||||
ssh.PublicKeys(signer),
|
ssh.PublicKeys(signer),
|
||||||
},
|
},
|
||||||
}, nil
|
}, 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
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -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.
|
||||||
|
|
Loading…
Reference in New Issue