mirror of https://github.com/hashicorp/packer
parent
8526244285
commit
36c1e8d838
@ -1,4 +1,4 @@
|
|||||||
package ansible
|
package adapter
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
@ -1,116 +0,0 @@
|
|||||||
package ansible
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
"net"
|
|
||||||
"os"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer/packer"
|
|
||||||
|
|
||||||
"golang.org/x/crypto/ssh"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestAdapter_Serve(t *testing.T) {
|
|
||||||
|
|
||||||
// done signals the adapter that the provisioner is done
|
|
||||||
done := make(chan struct{})
|
|
||||||
|
|
||||||
acceptC := make(chan struct{})
|
|
||||||
l := listener{done: make(chan struct{}), acceptC: acceptC}
|
|
||||||
|
|
||||||
config := &ssh.ServerConfig{}
|
|
||||||
|
|
||||||
ui := new(packer.NoopUi)
|
|
||||||
|
|
||||||
sut := newAdapter(done, &l, config, "", newUi(ui), communicator{})
|
|
||||||
go func() {
|
|
||||||
i := 0
|
|
||||||
for range acceptC {
|
|
||||||
i++
|
|
||||||
if i == 4 {
|
|
||||||
close(done)
|
|
||||||
l.Close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
sut.Serve()
|
|
||||||
}
|
|
||||||
|
|
||||||
type listener struct {
|
|
||||||
done chan struct{}
|
|
||||||
acceptC chan<- struct{}
|
|
||||||
i int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *listener) Accept() (net.Conn, error) {
|
|
||||||
log.Println("Accept() called")
|
|
||||||
l.acceptC <- struct{}{}
|
|
||||||
select {
|
|
||||||
case <-l.done:
|
|
||||||
log.Println("done, serving an error")
|
|
||||||
return nil, errors.New("listener is closed")
|
|
||||||
|
|
||||||
case <-time.After(10 * time.Millisecond):
|
|
||||||
l.i++
|
|
||||||
|
|
||||||
if l.i%2 == 0 {
|
|
||||||
c1, c2 := net.Pipe()
|
|
||||||
|
|
||||||
go func(c net.Conn) {
|
|
||||||
<-time.After(100 * time.Millisecond)
|
|
||||||
log.Println("closing c")
|
|
||||||
c.Close()
|
|
||||||
}(c1)
|
|
||||||
|
|
||||||
return c2, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, errors.New("accept error")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *listener) Close() error {
|
|
||||||
close(l.done)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *listener) Addr() net.Addr {
|
|
||||||
return addr{}
|
|
||||||
}
|
|
||||||
|
|
||||||
type addr struct{}
|
|
||||||
|
|
||||||
func (a addr) Network() string {
|
|
||||||
return a.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a addr) String() string {
|
|
||||||
return "test"
|
|
||||||
}
|
|
||||||
|
|
||||||
type communicator struct{}
|
|
||||||
|
|
||||||
func (c communicator) Start(*packer.RemoteCmd) error {
|
|
||||||
return errors.New("communicator not supported")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c communicator) Upload(string, io.Reader, *os.FileInfo) error {
|
|
||||||
return errors.New("communicator not supported")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c communicator) UploadDir(dst string, src string, exclude []string) error {
|
|
||||||
return errors.New("communicator not supported")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c communicator) Download(string, io.Writer) error {
|
|
||||||
return errors.New("communicator not supported")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c communicator) DownloadDir(src string, dst string, exclude []string) error {
|
|
||||||
return errors.New("communicator not supported")
|
|
||||||
}
|
|
||||||
@ -1,285 +0,0 @@
|
|||||||
package inspec
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/binary"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer/packer"
|
|
||||||
"golang.org/x/crypto/ssh"
|
|
||||||
)
|
|
||||||
|
|
||||||
// An adapter satisfies SSH requests (from an Inspec client) by delegating SSH
|
|
||||||
// exec and subsystem commands to a packer.Communicator.
|
|
||||||
type adapter struct {
|
|
||||||
done <-chan struct{}
|
|
||||||
l net.Listener
|
|
||||||
config *ssh.ServerConfig
|
|
||||||
ui packer.Ui
|
|
||||||
comm packer.Communicator
|
|
||||||
}
|
|
||||||
|
|
||||||
func newAdapter(done <-chan struct{}, l net.Listener, config *ssh.ServerConfig, ui packer.Ui, comm packer.Communicator) *adapter {
|
|
||||||
return &adapter{
|
|
||||||
done: done,
|
|
||||||
l: l,
|
|
||||||
config: config,
|
|
||||||
ui: ui,
|
|
||||||
comm: comm,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *adapter) Serve() {
|
|
||||||
log.Printf("SSH proxy: serving on %s", c.l.Addr())
|
|
||||||
|
|
||||||
for {
|
|
||||||
// Accept will return if either the underlying connection is closed or if a connection is made.
|
|
||||||
// after returning, check to see if c.done can be received. If so, then Accept() returned because
|
|
||||||
// the connection has been closed.
|
|
||||||
conn, err := c.l.Accept()
|
|
||||||
select {
|
|
||||||
case <-c.done:
|
|
||||||
return
|
|
||||||
default:
|
|
||||||
if err != nil {
|
|
||||||
c.ui.Error(fmt.Sprintf("listen.Accept failed: %v", err))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
go func(conn net.Conn) {
|
|
||||||
if err := c.Handle(conn, c.ui); err != nil {
|
|
||||||
c.ui.Error(err.Error())
|
|
||||||
}
|
|
||||||
}(conn)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *adapter) Handle(conn net.Conn, ui packer.Ui) error {
|
|
||||||
log.Print("SSH proxy: accepted connection")
|
|
||||||
_, chans, reqs, err := ssh.NewServerConn(conn, c.config)
|
|
||||||
if err != nil {
|
|
||||||
return errors.New("failed to handshake")
|
|
||||||
}
|
|
||||||
|
|
||||||
// discard all global requests
|
|
||||||
go ssh.DiscardRequests(reqs)
|
|
||||||
|
|
||||||
// Service the incoming NewChannels
|
|
||||||
for newChannel := range chans {
|
|
||||||
if newChannel.ChannelType() != "session" {
|
|
||||||
newChannel.Reject(ssh.UnknownChannelType, "unknown channel type")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
go func(ch ssh.NewChannel) {
|
|
||||||
if err := c.handleSession(ch); err != nil {
|
|
||||||
c.ui.Error(err.Error())
|
|
||||||
}
|
|
||||||
}(newChannel)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *adapter) handleSession(newChannel ssh.NewChannel) error {
|
|
||||||
channel, requests, err := newChannel.Accept()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer channel.Close()
|
|
||||||
|
|
||||||
done := make(chan struct{})
|
|
||||||
|
|
||||||
// Sessions have requests such as "pty-req", "shell", "env", and "exec".
|
|
||||||
// see RFC 4254, section 6
|
|
||||||
go func(in <-chan *ssh.Request) {
|
|
||||||
env := make([]envRequestPayload, 4)
|
|
||||||
for req := range in {
|
|
||||||
switch req.Type {
|
|
||||||
case "pty-req":
|
|
||||||
log.Println("inspec provisioner pty-req request")
|
|
||||||
// accept pty-req requests, but don't actually do anything. Necessary for OpenSSH and sudo.
|
|
||||||
req.Reply(true, nil)
|
|
||||||
|
|
||||||
case "env":
|
|
||||||
req, err := newEnvRequest(req)
|
|
||||||
if err != nil {
|
|
||||||
c.ui.Error(err.Error())
|
|
||||||
req.Reply(false, nil)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
env = append(env, req.Payload)
|
|
||||||
log.Printf("new env request: %s", req.Payload)
|
|
||||||
req.Reply(true, nil)
|
|
||||||
case "exec":
|
|
||||||
req, err := newExecRequest(req)
|
|
||||||
if err != nil {
|
|
||||||
c.ui.Error(err.Error())
|
|
||||||
req.Reply(false, nil)
|
|
||||||
close(done)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("new exec request: %s", req.Payload)
|
|
||||||
|
|
||||||
if len(req.Payload) == 0 {
|
|
||||||
req.Reply(false, nil)
|
|
||||||
close(done)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
go func(channel ssh.Channel) {
|
|
||||||
exit := c.exec(string(req.Payload), channel, channel, channel.Stderr())
|
|
||||||
|
|
||||||
exitStatus := make([]byte, 4)
|
|
||||||
binary.BigEndian.PutUint32(exitStatus, uint32(exit))
|
|
||||||
channel.SendRequest("exit-status", false, exitStatus)
|
|
||||||
close(done)
|
|
||||||
}(channel)
|
|
||||||
req.Reply(true, nil)
|
|
||||||
case "subsystem":
|
|
||||||
req, err := newSubsystemRequest(req)
|
|
||||||
if err != nil {
|
|
||||||
c.ui.Error(err.Error())
|
|
||||||
req.Reply(false, nil)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("new subsystem request: %s", req.Payload)
|
|
||||||
|
|
||||||
c.ui.Error(fmt.Sprintf("unsupported subsystem requested: %s", req.Payload))
|
|
||||||
req.Reply(false, nil)
|
|
||||||
default:
|
|
||||||
log.Printf("rejecting %s request", req.Type)
|
|
||||||
req.Reply(false, nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}(requests)
|
|
||||||
|
|
||||||
<-done
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *adapter) Shutdown() {
|
|
||||||
c.l.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *adapter) exec(command string, in io.Reader, out io.Writer, err io.Writer) int {
|
|
||||||
var exitStatus int
|
|
||||||
exitStatus = c.remoteExec(command, in, out, err)
|
|
||||||
return exitStatus
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *adapter) remoteExec(command string, in io.Reader, out io.Writer, err io.Writer) int {
|
|
||||||
cmd := &packer.RemoteCmd{
|
|
||||||
Stdin: in,
|
|
||||||
Stdout: out,
|
|
||||||
Stderr: err,
|
|
||||||
Command: command,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := c.comm.Start(cmd); err != nil {
|
|
||||||
c.ui.Error(err.Error())
|
|
||||||
return cmd.ExitStatus
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.Wait()
|
|
||||||
|
|
||||||
return cmd.ExitStatus
|
|
||||||
}
|
|
||||||
|
|
||||||
type envRequest struct {
|
|
||||||
*ssh.Request
|
|
||||||
Payload envRequestPayload
|
|
||||||
}
|
|
||||||
|
|
||||||
type envRequestPayload struct {
|
|
||||||
Name string
|
|
||||||
Value string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p envRequestPayload) String() string {
|
|
||||||
return fmt.Sprintf("%s=%s", p.Name, p.Value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func newEnvRequest(raw *ssh.Request) (*envRequest, error) {
|
|
||||||
r := new(envRequest)
|
|
||||||
r.Request = raw
|
|
||||||
|
|
||||||
if err := ssh.Unmarshal(raw.Payload, &r.Payload); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return r, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func sshString(buf io.Reader) (string, error) {
|
|
||||||
var size uint32
|
|
||||||
err := binary.Read(buf, binary.BigEndian, &size)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
b := make([]byte, size)
|
|
||||||
err = binary.Read(buf, binary.BigEndian, b)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return string(b), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type execRequest struct {
|
|
||||||
*ssh.Request
|
|
||||||
Payload execRequestPayload
|
|
||||||
}
|
|
||||||
|
|
||||||
type execRequestPayload string
|
|
||||||
|
|
||||||
func (p execRequestPayload) String() string {
|
|
||||||
return string(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
func newExecRequest(raw *ssh.Request) (*execRequest, error) {
|
|
||||||
r := new(execRequest)
|
|
||||||
r.Request = raw
|
|
||||||
buf := bytes.NewReader(r.Request.Payload)
|
|
||||||
|
|
||||||
var err error
|
|
||||||
var payload string
|
|
||||||
if payload, err = sshString(buf); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
r.Payload = execRequestPayload(payload)
|
|
||||||
return r, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type subsystemRequest struct {
|
|
||||||
*ssh.Request
|
|
||||||
Payload subsystemRequestPayload
|
|
||||||
}
|
|
||||||
|
|
||||||
type subsystemRequestPayload string
|
|
||||||
|
|
||||||
func (p subsystemRequestPayload) String() string {
|
|
||||||
return string(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
func newSubsystemRequest(raw *ssh.Request) (*subsystemRequest, error) {
|
|
||||||
r := new(subsystemRequest)
|
|
||||||
r.Request = raw
|
|
||||||
buf := bytes.NewReader(r.Request.Payload)
|
|
||||||
|
|
||||||
var err error
|
|
||||||
var payload string
|
|
||||||
if payload, err = sshString(buf); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
r.Payload = subsystemRequestPayload(payload)
|
|
||||||
return r, nil
|
|
||||||
}
|
|
||||||
Loading…
Reference in new issue