mirror of
https://github.com/gravitational/teleport
synced 2024-10-21 01:34:01 +00:00
Moved tests from lib/srv and lib/utils into integrations.
This commit is contained in:
parent
e5d6faf482
commit
5f670ef7d9
|
@ -6,11 +6,12 @@ import (
|
|||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/gravitational/teleport"
|
||||
"github.com/gravitational/teleport/lib/auth"
|
||||
"github.com/gravitational/teleport/lib/auth/native"
|
||||
|
@ -23,7 +24,10 @@ import (
|
|||
"github.com/gravitational/teleport/lib/service"
|
||||
"github.com/gravitational/teleport/lib/services"
|
||||
"github.com/gravitational/teleport/lib/utils"
|
||||
|
||||
"github.com/gravitational/trace"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
)
|
||||
|
||||
// SetTestTimeouts affects global timeouts inside Teleport, making connections
|
||||
|
@ -558,6 +562,88 @@ func (i *TeleInstance) Stop(removeData bool) error {
|
|||
return i.Process.Wait()
|
||||
}
|
||||
|
||||
type proxyServer struct {
|
||||
sync.Mutex
|
||||
count int
|
||||
}
|
||||
|
||||
// ServeHTTP only accepts the CONNECT verb and will tunnel your connection to
|
||||
// the specified host. Also tracks the number of connections that it proxies for
|
||||
// debugging purposes.
|
||||
func (p *proxyServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
// validate http connect parameters
|
||||
if r.Method != http.MethodConnect {
|
||||
trace.WriteError(w, trace.BadParameter("%v not supported", r.Method))
|
||||
return
|
||||
}
|
||||
if r.Host == "" {
|
||||
trace.WriteError(w, trace.BadParameter("host not set"))
|
||||
return
|
||||
}
|
||||
|
||||
// hijack request so we can get underlying connection
|
||||
hj, ok := w.(http.Hijacker)
|
||||
if !ok {
|
||||
trace.WriteError(w, trace.AccessDenied("unable to hijack connection"))
|
||||
return
|
||||
}
|
||||
sconn, _, err := hj.Hijack()
|
||||
if err != nil {
|
||||
trace.WriteError(w, err)
|
||||
return
|
||||
}
|
||||
defer sconn.Close()
|
||||
|
||||
// dial to host we want to proxy connection to
|
||||
dconn, err := net.Dial("tcp", r.Host)
|
||||
if err != nil {
|
||||
trace.WriteError(w, err)
|
||||
return
|
||||
}
|
||||
defer dconn.Close()
|
||||
|
||||
// write 200 OK to the source, but don't close the connection
|
||||
resp := &http.Response{
|
||||
Status: "OK",
|
||||
StatusCode: 200,
|
||||
Proto: "HTTP/1.1",
|
||||
ProtoMajor: 1,
|
||||
ProtoMinor: 0,
|
||||
}
|
||||
err = resp.Write(sconn)
|
||||
if err != nil {
|
||||
trace.WriteError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
// success, we're proxying data now
|
||||
p.Lock()
|
||||
p.count = p.count + 1
|
||||
p.Unlock()
|
||||
|
||||
// copy from src to dst and dst to src
|
||||
errc := make(chan error, 2)
|
||||
replicate := func(dst io.Writer, src io.Reader) {
|
||||
_, err := io.Copy(dst, src)
|
||||
errc <- err
|
||||
}
|
||||
go replicate(sconn, dconn)
|
||||
go replicate(dconn, sconn)
|
||||
|
||||
// wait until done, error, or 10 second
|
||||
select {
|
||||
case <-time.After(10 * time.Second):
|
||||
case <-errc:
|
||||
}
|
||||
}
|
||||
|
||||
// Count returns the number of connections that have been proxied.
|
||||
func (p *proxyServer) Count() int {
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
return p.count
|
||||
}
|
||||
|
||||
func fatalIf(err error) {
|
||||
if err != nil {
|
||||
log.Fatal("", err)
|
||||
|
|
|
@ -21,8 +21,12 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
@ -275,6 +279,86 @@ func (s *IntSuite) TestAudit(c *check.C) {
|
|||
}
|
||||
}
|
||||
|
||||
// TestInteroperability checks if Teleport and OpenSSH behave in the same way
|
||||
// when executing commands.
|
||||
func (s *IntSuite) TestInteroperability(c *check.C) {
|
||||
tempdir, err := ioutil.TempDir("", "teleport-")
|
||||
c.Assert(err, check.IsNil)
|
||||
defer os.RemoveAll(tempdir)
|
||||
tempfile := filepath.Join(tempdir, "file.txt")
|
||||
|
||||
// create new teleport server that will be used by all tests
|
||||
t := s.newTeleport(c, nil, true)
|
||||
defer t.Stop(true)
|
||||
|
||||
var tests = []struct {
|
||||
inCommand string
|
||||
inStdin string
|
||||
outContains string
|
||||
outFile bool
|
||||
}{
|
||||
// 0 - echo "1\n2\n" | ssh localhost "cat -"
|
||||
// this command can be used to copy files by piping stdout to stdin over ssh.
|
||||
{
|
||||
"cat -",
|
||||
"1\n2\n",
|
||||
"1\n2\n",
|
||||
false,
|
||||
},
|
||||
// 1 - ssh -tt locahost '/bin/sh -c "mkdir -p /tmp && echo a > /tmp/file.txt"'
|
||||
// programs like ansible execute commands like this
|
||||
{
|
||||
fmt.Sprintf(`/bin/sh -c "mkdir -p /tmp && echo a > %v"`, tempfile),
|
||||
"",
|
||||
"a",
|
||||
true,
|
||||
},
|
||||
// 2 - ssh localhost tty
|
||||
// should print "not a tty"
|
||||
{
|
||||
"tty",
|
||||
"",
|
||||
"not a tty",
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
// create new teleport client
|
||||
cl, err := t.NewClient(s.me.Username, Site, Host, t.GetPortSSHInt())
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
// hook up stdin and stdout to a buffer for reading and writing
|
||||
inbuf := bytes.NewReader([]byte(tt.inStdin))
|
||||
outbuf := &bytes.Buffer{}
|
||||
cl.Stdin = inbuf
|
||||
cl.Stdout = outbuf
|
||||
cl.Stderr = outbuf
|
||||
|
||||
// run command and wait a maximum of 10 seconds for it to complete
|
||||
sessionEndC := make(chan interface{}, 0)
|
||||
go func() {
|
||||
// don't check for err, because sometimes this process should fail
|
||||
// with an error and that's what the test is checking for.
|
||||
cl.SSH(context.TODO(), []string{tt.inCommand}, false)
|
||||
sessionEndC <- true
|
||||
}()
|
||||
waitFor(sessionEndC, time.Second*10)
|
||||
|
||||
// if we are looking for the output in a file, look in the file
|
||||
// otherwise check stdout and stderr for the expected output
|
||||
if tt.outFile {
|
||||
bytes, err := ioutil.ReadFile(tempfile)
|
||||
c.Assert(err, check.IsNil)
|
||||
comment := check.Commentf("Test %v: %q does not contain: %q", i, string(bytes), tt.outContains)
|
||||
c.Assert(strings.Contains(string(bytes), tt.outContains), check.Equals, true, comment)
|
||||
} else {
|
||||
comment := check.Commentf("Test %v: %q does not contain: %q", i, outbuf.String(), tt.outContains)
|
||||
c.Assert(strings.Contains(outbuf.String(), tt.outContains), check.Equals, true, comment)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestInteractive covers SSH into shell and joining the same session from another client
|
||||
func (s *IntSuite) TestInteractive(c *check.C) {
|
||||
t := s.newTeleport(c, nil, true)
|
||||
|
@ -382,6 +466,16 @@ func (s *IntSuite) TestInvalidLogins(c *check.C) {
|
|||
// Then it executes an SSH command on A by connecting directly
|
||||
// to A and by connecting to B via B<->A tunnel
|
||||
func (s *IntSuite) TestTwoClusters(c *check.C) {
|
||||
// start the http proxy, we need to make sure this was not used
|
||||
ps := &proxyServer{}
|
||||
ts := httptest.NewServer(ps)
|
||||
defer ts.Close()
|
||||
|
||||
// clear out any proxy environment variables
|
||||
for _, v := range []string{"http_proxy", "https_proxy", "HTTP_PROXY", "HTTPS_PROXY"} {
|
||||
os.Setenv(v, "")
|
||||
}
|
||||
|
||||
username := s.me.Username
|
||||
|
||||
a := NewInstance("site-A", HostID, Host, s.getPorts(5), s.priv, s.pub)
|
||||
|
@ -410,6 +504,9 @@ func (s *IntSuite) TestTwoClusters(c *check.C) {
|
|||
outputB bytes.Buffer
|
||||
)
|
||||
|
||||
// make sure the direct dialer was used and not the proxy dialer
|
||||
c.Assert(ps.Count(), check.Equals, 0)
|
||||
|
||||
// if we got here, it means two sites are cross-connected. lets execute SSH commands
|
||||
sshPort := a.GetPortSSHInt()
|
||||
cmd := []string{"echo", "hello world"}
|
||||
|
@ -457,6 +554,51 @@ func (s *IntSuite) TestTwoClusters(c *check.C) {
|
|||
c.Assert(a.Stop(true), check.IsNil)
|
||||
}
|
||||
|
||||
// TestTwoClustersProxy checks if the reverse tunnel uses a HTTP PROXY to
|
||||
// establish a connection.
|
||||
func (s *IntSuite) TestTwoClustersProxy(c *check.C) {
|
||||
// start the http proxy
|
||||
ps := &proxyServer{}
|
||||
ts := httptest.NewServer(ps)
|
||||
defer ts.Close()
|
||||
|
||||
// set the http_proxy environment variable
|
||||
u, err := url.Parse(ts.URL)
|
||||
c.Assert(err, check.IsNil)
|
||||
os.Setenv("http_proxy", u.Host)
|
||||
defer os.Setenv("http_proxy", "")
|
||||
|
||||
username := s.me.Username
|
||||
|
||||
a := NewInstance("site-A", HostID, Host, s.getPorts(5), s.priv, s.pub)
|
||||
b := NewInstance("site-B", HostID, Host, s.getPorts(5), s.priv, s.pub)
|
||||
|
||||
a.AddUser(username, []string{username})
|
||||
b.AddUser(username, []string{username})
|
||||
|
||||
c.Assert(b.Create(a.Secrets.AsSlice(), false, nil), check.IsNil)
|
||||
c.Assert(a.Create(b.Secrets.AsSlice(), true, nil), check.IsNil)
|
||||
|
||||
c.Assert(b.Start(), check.IsNil)
|
||||
c.Assert(a.Start(), check.IsNil)
|
||||
|
||||
// wait for both sites to see each other via their reverse tunnels (for up to 10 seconds)
|
||||
abortTime := time.Now().Add(time.Second * 10)
|
||||
for len(b.Tunnel.GetSites()) < 2 && len(b.Tunnel.GetSites()) < 2 {
|
||||
time.Sleep(time.Millisecond * 200)
|
||||
if time.Now().After(abortTime) {
|
||||
c.Fatalf("two sites do not see each other: tunnels are not working")
|
||||
}
|
||||
}
|
||||
|
||||
// make sure the reverse tunnel went through the proxy
|
||||
c.Assert(ps.Count() > 0, check.Equals, true, check.Commentf("proxy did not intercept any connection"))
|
||||
|
||||
// stop both sites for real
|
||||
c.Assert(b.Stop(true), check.IsNil)
|
||||
c.Assert(a.Stop(true), check.IsNil)
|
||||
}
|
||||
|
||||
// TestHA tests scenario when auth server for the cluster goes down
|
||||
// and we switch to local persistent caches
|
||||
func (s *IntSuite) TestHA(c *check.C) {
|
||||
|
|
|
@ -23,7 +23,6 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"os/user"
|
||||
|
@ -291,254 +290,6 @@ func (s *SrvSuite) TestAgentForward(c *C) {
|
|||
c.Fatalf("expected socket to be closed, still could dial after 150 ms")
|
||||
}
|
||||
|
||||
// TestExecCommandLocalPtyAndRemotePty tests executing a command where you have
|
||||
// a local and remote PTY.
|
||||
func (s *SrvSuite) TestExecCommandLocalPtyAndRemotePty(c *C) {
|
||||
// expected output: success. top has a remote and local pty.
|
||||
term, err := newTerminal()
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
session, err := s.clt.NewSession()
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
session.Stdin = term.tty
|
||||
session.Stdout = term.tty
|
||||
session.Stderr = term.tty
|
||||
|
||||
modes := ssh.TerminalModes{
|
||||
ssh.ECHO: 0,
|
||||
ssh.TTY_OP_ISPEED: 14400,
|
||||
ssh.TTY_OP_OSPEED: 14400,
|
||||
}
|
||||
err = session.RequestPty("term", 40, 80, modes)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
// run top, wait 500 ms for top to start, send ^C
|
||||
go func() {
|
||||
err := session.Run("top")
|
||||
c.Assert(err, NotNil)
|
||||
}()
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
_, err = term.tty.Write([]byte("\x03"))
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
output := make([]byte, 2048)
|
||||
_, err = term.pty.Read(output)
|
||||
c.Assert(err, IsNil)
|
||||
outputContains := strings.Contains(string(output), "%CPU")
|
||||
c.Assert(outputContains, Equals, true, Commentf("Output does not contain %CPU expected from top"))
|
||||
|
||||
// 2 - ssh -tt locahost '/bin/sh -c "mkdir -p /tmp && echo a > /tmp/file.txt"'
|
||||
// expected output: success. this mirrors how ansible runs commands.
|
||||
term, err = newTerminal()
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
session, err = s.clt.NewSession()
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
session.Stdin = term.tty
|
||||
session.Stdout = term.tty
|
||||
session.Stderr = term.tty
|
||||
|
||||
modes = ssh.TerminalModes{
|
||||
ssh.ECHO: 0,
|
||||
ssh.TTY_OP_ISPEED: 14400,
|
||||
ssh.TTY_OP_OSPEED: 14400,
|
||||
}
|
||||
err = session.RequestPty("term", 40, 80, modes)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
tempdir, err := ioutil.TempDir("", "teleport-")
|
||||
c.Assert(err, IsNil)
|
||||
os.RemoveAll(tempdir)
|
||||
tempfile := tempdir + "/file.txt"
|
||||
|
||||
command := fmt.Sprintf(`/bin/sh -c "mkdir -p %v && echo a > %v"`, tempdir, tempfile)
|
||||
err = session.Run(command)
|
||||
c.Assert(err, IsNil)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
bytes, err := ioutil.ReadFile(tempfile)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(string(bytes), Equals, "a\n")
|
||||
}
|
||||
|
||||
// TestExecCommandLocalPtyAndNoRemotePty tests executing a command where you a
|
||||
// local PTY but no remote PTY.
|
||||
func (s *SrvSuite) TestExecCommandLocalPtyAndNoRemotePty(c *C) {
|
||||
// 1 - command: ssh localhost top
|
||||
// expected output: failure. no remote pty exists so top will not start.
|
||||
term, err := newTerminal()
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
session, err := s.clt.NewSession()
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
session.Stdin = term.tty
|
||||
session.Stdout = term.tty
|
||||
session.Stderr = term.tty
|
||||
|
||||
err = session.Run("top")
|
||||
c.Assert(err, NotNil)
|
||||
|
||||
output := make([]byte, 21)
|
||||
_, err = term.pty.Read(output)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(string(output), Equals, "top: failed tty get\r\n")
|
||||
|
||||
// 2 - command: ssh localhost seq 2
|
||||
// expected output: success. because we have a local pty attached
|
||||
// to stdout, the pty transforms 1\n2\n to 1\r\n2\r\n.
|
||||
term, err = newTerminal()
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
session, err = s.clt.NewSession()
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
session.Stdin = term.tty
|
||||
session.Stdout = term.tty
|
||||
session.Stderr = term.tty
|
||||
|
||||
err = session.Run("seq 2")
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
output = make([]byte, 6)
|
||||
_, err = term.pty.Read(output)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(string(output), Equals, "1\r\n2\r\n")
|
||||
}
|
||||
|
||||
// TestExecCommandNoLocalAndPtyRemotePty tests executing a command where you
|
||||
// have no local PTY but have a remote PTY.
|
||||
func (s *SrvSuite) TestExecCommandNoLocalPtyAndRemotePty(c *C) {
|
||||
c.Skip("Temporarily remove test until we track down why it's flaky. Output of [0x0a 0x31 0x0a 0x32 0x0a] does not match expected output of [0x31 0x0d 0x0a 0x032 0x0d 0x0a 0x0a]")
|
||||
|
||||
// 1 - command: seq 2 | ssh -tt localhost 'stty -opost; echo'
|
||||
// expected output: pipe stdin to ssh and turn off post-processing
|
||||
// and echo stdin. we should see "1\r\n2\r\n\n"
|
||||
session, err := s.clt.NewSession()
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
stdinPipe, err := session.StdinPipe()
|
||||
c.Assert(err, IsNil)
|
||||
stdoutPipe, err := session.StdoutPipe()
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
modes := ssh.TerminalModes{
|
||||
ssh.ECHO: 0,
|
||||
ssh.TTY_OP_ISPEED: 14400,
|
||||
ssh.TTY_OP_OSPEED: 14400,
|
||||
}
|
||||
err = session.RequestPty("term", 40, 80, modes)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
_, err = stdinPipe.Write([]byte("1\n2\n"))
|
||||
c.Assert(err, IsNil)
|
||||
stdinPipe.Close()
|
||||
|
||||
err = session.Run("stty -opost; echo")
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
stdout, err := ioutil.ReadAll(stdoutPipe)
|
||||
c.Assert(err, IsNil)
|
||||
outputContains := strings.Contains(string(stdout), "1\r\n2\r\n\n")
|
||||
c.Assert(outputContains, Equals, true, Commentf("Output [%# x] does not match expected output: [%# x]", string(stdout), "1\r\n2\r\n\n"))
|
||||
}
|
||||
|
||||
// TestExecCommandNoLocalPtyAndNoRemotePty tests executing a command where
|
||||
// you have no local PTY and no remote PTY. This is like running ssh
|
||||
// with the -T flag.
|
||||
func (s *SrvSuite) TestExecCommandNoLocalPtyAndNoRemotePty(c *C) {
|
||||
// 1 - command: echo "1\n2\n" | ssh -T localhost "cat -"
|
||||
// expected output: since no pty exists, cat takes stdin
|
||||
// from the pipe and prints it out as-is: "1\n2\n"
|
||||
session, err := s.clt.NewSession()
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
stdinPipe, err := session.StdinPipe()
|
||||
c.Assert(err, IsNil)
|
||||
stdoutPipe, err := session.StdoutPipe()
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
_, err = stdinPipe.Write([]byte("1\n2\n"))
|
||||
c.Assert(err, IsNil)
|
||||
stdinPipe.Close()
|
||||
|
||||
err = session.Run("cat -")
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
stdout, err := ioutil.ReadAll(stdoutPipe)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(string(stdout), Equals, "1\n2\n")
|
||||
|
||||
// 2 - command: echo "1\n2\n" | ssh -T localhost "cat - > /tmp/file"
|
||||
// expected output: this is like using ssh to copy a file from one
|
||||
// machine to another. since no pty exists, cat takes stdin from the pipe
|
||||
// and writes it to disk as-is: "1\n2\n"
|
||||
session, err = s.clt.NewSession()
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
stdinPipe, err = session.StdinPipe()
|
||||
c.Assert(err, IsNil)
|
||||
stdoutPipe, err = session.StdoutPipe()
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
tmpfile, err := ioutil.TempFile("", "teleport-")
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
_, err = stdinPipe.Write([]byte("1\n2\n"))
|
||||
c.Assert(err, IsNil)
|
||||
stdinPipe.Close()
|
||||
|
||||
cmd := fmt.Sprintf("cat - > %v", tmpfile.Name())
|
||||
err = session.Run(cmd)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
bytes, err := ioutil.ReadFile(tmpfile.Name())
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(string(bytes), Equals, "1\n2\n")
|
||||
|
||||
c.Assert(os.Remove(tmpfile.Name()), IsNil)
|
||||
}
|
||||
|
||||
// TestShell launches interactive shell session and executes a command
|
||||
func (s *SrvSuite) TestShell(c *C) {
|
||||
c.Skip("disabled")
|
||||
se, err := s.clt.NewSession()
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
writer, err := se.StdinPipe()
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
stdoutPipe, err := se.StdoutPipe()
|
||||
c.Assert(err, IsNil)
|
||||
reader := bufio.NewReader(stdoutPipe)
|
||||
|
||||
c.Assert(se.Shell(), IsNil)
|
||||
|
||||
buf := make([]byte, 256)
|
||||
_, err = reader.Read(buf)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(len(buf) > 0, Equals, true)
|
||||
|
||||
// send a few "keyboard inputs" into the session:
|
||||
_, err = io.WriteString(writer, "echo $((50+100))\n\r")
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
// read the output and make sure that "150" (output of $((50+100)) is there
|
||||
// NOTE: this test may fail if you have errors in your .bashrc or .profile
|
||||
// leading to tons of output when opening new bash session
|
||||
foundOutput := false
|
||||
for i := 0; i < 50 && !foundOutput; i++ {
|
||||
time.Sleep(time.Millisecond)
|
||||
_, err = reader.Read(buf)
|
||||
c.Assert(err, IsNil)
|
||||
foundOutput = strings.Contains(string(buf), "150")
|
||||
}
|
||||
c.Assert(foundOutput, Equals, true)
|
||||
c.Assert(se.Close(), IsNil)
|
||||
}
|
||||
|
||||
func (s *SrvSuite) TestAllowedUsers(c *C) {
|
||||
up, err := newUpack(s.user, []string{s.user}, s.a)
|
||||
c.Assert(err, IsNil)
|
||||
|
|
BIN
lib/utils/proxy/.proxy_test.go.swp
Normal file
BIN
lib/utils/proxy/.proxy_test.go.swp
Normal file
Binary file not shown.
|
@ -16,28 +16,12 @@ limitations under the License.
|
|||
package proxy
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/crypto/ssh"
|
||||
|
||||
"github.com/gravitational/teleport/lib/utils"
|
||||
"github.com/gravitational/trace"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"gopkg.in/check.v1"
|
||||
)
|
||||
|
||||
|
@ -55,58 +39,6 @@ func (s *ProxySuite) TearDownSuite(c *check.C) {}
|
|||
func (s *ProxySuite) SetUpTest(c *check.C) {}
|
||||
func (s *ProxySuite) TearDownTest(c *check.C) {}
|
||||
|
||||
func (s *ProxySuite) TestDirectDial(c *check.C) {
|
||||
os.Unsetenv("https_proxy")
|
||||
os.Unsetenv("http_proxy")
|
||||
|
||||
d := debugServer{}
|
||||
err := d.Start()
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
dialer := DialerFromEnvironment()
|
||||
client, err := dialer.Dial("tcp", d.Address(), &ssh.ClientConfig{})
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
session, err := client.NewSession()
|
||||
c.Assert(err, check.IsNil)
|
||||
defer session.Close()
|
||||
|
||||
session.Run("date")
|
||||
session.Close()
|
||||
client.Close()
|
||||
|
||||
c.Assert(d.Commands(), check.DeepEquals, []string{"date"})
|
||||
}
|
||||
|
||||
func (s *ProxySuite) TestProxyDial(c *check.C) {
|
||||
dh := &debugHandler{}
|
||||
ts := httptest.NewServer(dh)
|
||||
defer ts.Close()
|
||||
|
||||
u, err := url.Parse(ts.URL)
|
||||
c.Assert(err, check.IsNil)
|
||||
os.Setenv("http_proxy", u.Host)
|
||||
|
||||
ds := debugServer{}
|
||||
err = ds.Start()
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
dialer := DialerFromEnvironment()
|
||||
client, err := dialer.Dial("tcp", ds.Address(), &ssh.ClientConfig{})
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
session, err := client.NewSession()
|
||||
c.Assert(err, check.IsNil)
|
||||
defer session.Close()
|
||||
|
||||
session.Run("date")
|
||||
session.Close()
|
||||
client.Close()
|
||||
|
||||
c.Assert(ds.Commands(), check.DeepEquals, []string{"date"})
|
||||
c.Assert(dh.Count(), check.Equals, 1)
|
||||
}
|
||||
|
||||
func (s *ProxySuite) TestGetProxyAddress(c *check.C) {
|
||||
var tests = []struct {
|
||||
inEnvName string
|
||||
|
@ -151,222 +83,8 @@ func (s *ProxySuite) TestGetProxyAddress(c *check.C) {
|
|||
}
|
||||
}
|
||||
|
||||
type debugServer struct {
|
||||
sync.Mutex
|
||||
|
||||
addr string
|
||||
commands []string
|
||||
}
|
||||
|
||||
func (d *debugServer) Start() error {
|
||||
hostkey, err := d.generateHostKey()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
freePorts, err := utils.GetFreeTCPPorts(10)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
srvPort := freePorts[len(freePorts)-1]
|
||||
d.addr = "127.0.0.1:" + srvPort
|
||||
|
||||
config := &ssh.ServerConfig{
|
||||
NoClientAuth: true,
|
||||
}
|
||||
config.AddHostKey(hostkey)
|
||||
|
||||
listener, err := net.Listen("tcp", d.addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
go func() {
|
||||
for {
|
||||
conn, err := listener.Accept()
|
||||
if err != nil {
|
||||
log.Debugf("Unable to accept: %v", err)
|
||||
}
|
||||
|
||||
go d.handleConnection(conn, config)
|
||||
}
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *debugServer) handleConnection(conn net.Conn, config *ssh.ServerConfig) error {
|
||||
sconn, chans, reqs, err := ssh.NewServerConn(conn, config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go ssh.DiscardRequests(reqs)
|
||||
|
||||
newchan := <-chans
|
||||
channel, requests, err := newchan.Accept()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req := <-requests
|
||||
err = d.handleRequest(channel, req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
channel.Close()
|
||||
sconn.Close()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *debugServer) handleRequest(channel ssh.Channel, req *ssh.Request) error {
|
||||
if req.Type != "exec" {
|
||||
req.Reply(false, nil)
|
||||
return trace.BadParameter("only exec type supported")
|
||||
}
|
||||
|
||||
type execRequest struct {
|
||||
Command string
|
||||
}
|
||||
|
||||
var e execRequest
|
||||
if err := ssh.Unmarshal(req.Payload, &e); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
out, err := exec.Command(e.Command).Output()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
io.Copy(channel, bytes.NewReader(out))
|
||||
channel.Close()
|
||||
|
||||
d.Lock()
|
||||
d.commands = append(d.commands, e.Command)
|
||||
d.Unlock()
|
||||
|
||||
if req.WantReply {
|
||||
req.Reply(true, nil)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *debugServer) generateHostKey() (ssh.Signer, error) {
|
||||
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
privateKeyPEM := &pem.Block{
|
||||
Type: "RSA PRIVATE KEY",
|
||||
Bytes: x509.MarshalPKCS1PrivateKey(privateKey),
|
||||
}
|
||||
var privateKeyBuffer bytes.Buffer
|
||||
err = pem.Encode(&privateKeyBuffer, privateKeyPEM)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hostkey, err := ssh.ParsePrivateKey(privateKeyBuffer.Bytes())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return hostkey, nil
|
||||
}
|
||||
|
||||
func (d *debugServer) Commands() []string {
|
||||
d.Lock()
|
||||
defer d.Unlock()
|
||||
return d.commands
|
||||
}
|
||||
|
||||
func (d *debugServer) Address() string {
|
||||
return d.addr
|
||||
}
|
||||
|
||||
type debugHandler struct {
|
||||
sync.Mutex
|
||||
|
||||
count int
|
||||
}
|
||||
|
||||
func (d *debugHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
// validate http connect parameters
|
||||
if r.Method != http.MethodConnect {
|
||||
http.Error(w, fmt.Sprintf("%v not supported", r.Method), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if r.Host == "" {
|
||||
http.Error(w, fmt.Sprintf("host not set"), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// hijack request so we can get underlying connection
|
||||
hj, ok := w.(http.Hijacker)
|
||||
if !ok {
|
||||
http.Error(w, "unable to hijack connection", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
sconn, _, err := hj.Hijack()
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// dial to host we want to proxy connection to
|
||||
dconn, err := net.Dial("tcp", r.Host)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// write 200 OK to the source, but don't close the connection
|
||||
resp := &http.Response{
|
||||
Status: "OK",
|
||||
StatusCode: 200,
|
||||
Proto: "HTTP/1.1",
|
||||
ProtoMajor: 1,
|
||||
ProtoMinor: 0,
|
||||
}
|
||||
resp.Write(sconn)
|
||||
|
||||
// copy from src to dst and dst to src
|
||||
done := make(chan bool)
|
||||
go func() {
|
||||
io.Copy(sconn, dconn)
|
||||
done <- true
|
||||
}()
|
||||
go func() {
|
||||
io.Copy(dconn, sconn)
|
||||
done <- true
|
||||
}()
|
||||
|
||||
d.Lock()
|
||||
d.count = d.count + 1
|
||||
d.Unlock()
|
||||
|
||||
// wait until done
|
||||
<-done
|
||||
<-done
|
||||
|
||||
// close the connections
|
||||
sconn.Close()
|
||||
dconn.Close()
|
||||
}
|
||||
|
||||
func (d *debugHandler) Count() int {
|
||||
d.Lock()
|
||||
defer d.Unlock()
|
||||
return d.count
|
||||
}
|
||||
|
||||
func unsetEnv() {
|
||||
os.Unsetenv("http_proxy")
|
||||
os.Unsetenv("HTTP_PROXY")
|
||||
os.Unsetenv("https_proxy")
|
||||
os.Unsetenv("HTTPS_PROXY")
|
||||
for _, envname := range []string{"http_proxy", "https_proxy", "HTTP_PROXY", "HTTPS_PROXY"} {
|
||||
os.Unsetenv(envname)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue