diff --git a/build.assets/Makefile b/build.assets/Makefile index 9702f638c5305..e6a556d54a767 100644 --- a/build.assets/Makefile +++ b/build.assets/Makefile @@ -77,4 +77,4 @@ enter: bbox # .PHONY:release release: - docker run $(DOCKERFLAGS) -i $(NOROOT) $(BBOX) /usr/bin/make release -e VERSION="$(VERSION)" -e ADDFLAGS="$(ADDFLAGS)" + docker run $(DOCKERFLAGS) -i $(NOROOT) $(BBOX) /usr/bin/make release -e ADDFLAGS="$(ADDFLAGS)" diff --git a/docker/Dockerfile b/docker/Dockerfile index f76b212f53520..745596f9d150d 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -7,9 +7,12 @@ FROM teleport-buildbox:latest ENV DEBUG=1 GOPATH=/root/go PATH=$PATH:/root/go/src/github.com/gravitational/teleport/build # htop is useful for testing terminal resizing -RUN apt-get install -y htop screen; \ +RUN apt-get install -y htop vim screen; \ mkdir -p /root/go/src/github.com/gravitational/teleport +# allows ansible testing +RUN apt-get install -y ansible + VOLUME ["/teleport", "/var/lib/teleport"] COPY .bashrc /root/.bashrc COPY .screenrc /root/.screenrc diff --git a/docker/README.md b/docker/README.md index 09f3cf71c9023..5d354e329be67 100644 --- a/docker/README.md +++ b/docker/README.md @@ -86,6 +86,65 @@ To setup Trusted Clusters: tctl -c /root/go/src/github.com/gravitational/teleport/docker/two-auth.yaml create -f docker/two-role-admin.yaml tctl -c /root/go/src/github.com/gravitational/teleport/docker/two-auth.yaml create -f docker/two-tc.yaml ``` + +### Ansible + +To setup Ansible: + +1. Follow steps in Trusted Cluster section to setup Trusted Clusters. +1. Use `tctl` to issue create user command and follow link on screen to create user. + + ```bash + tctl users add {username} root + ``` +1. Configure Ansible. + + ```bash + # add two-node to ansible hosts file + echo "172.10.1.2:3022" >> /etc/ansible/hosts + + # setup ssh_args that ansible will use to access trusted cluster nodes + sed -i '/ssh_args = -o ControlMaster=auto -o ControlPersist=60s/assh_args = -o "ProxyCommand ssh -p 3023 one -s proxy:%h:%p@two"' /etc/ansible/ansible.cfg + + # use scp over sftp + sed -i '/scp_if_ssh/s/^#//g' /etc/ansible/ansible.cfg + ``` + +1. Start and load OpenSSH agent with keys. + + ```bash + # create directory for ssh config + mkdir ~/.ssh && chmod 700 ~/.ssh + + # start ssh-agent + eval `ssh-agent` + + # log in with the user created before + tsh --proxy=localhost --user=rjones login + + # load keys into ssh-agent + tsh --proxy=localhost --user=rjones agent --load + ``` + +1. Verify Ansible works: + + ```bash + $ ansible all -m ping + 172.10.1.2 | success >> { + "changed": false, + "ping": "pong" + } + ``` + +1. Run an simple playbook: + + ```bash + # cd to directory that contains playbook + cd /root/go/src/github.com/gravitational/teleport/docker/ansible + + # run playbook + ansible-playbook playbook.yaml + ``` ### Interactive Usage diff --git a/docker/ansible/playbook.yaml b/docker/ansible/playbook.yaml new file mode 100644 index 0000000000000..63075db7662ed --- /dev/null +++ b/docker/ansible/playbook.yaml @@ -0,0 +1,26 @@ +--- +- hosts: all + tasks: + - name: "simple copy" + copy: + src: /root/go/src/github.com/gravitational/teleport/docker/ansible/simple.txt + dest: /tmp/simple.txt.out + owner: root + group: root + mode: 0644 + + - name: recursive copy + copy: + src: /root/go/src/github.com/gravitational/teleport/docker/ansible/rdir + dest: /tmp" + owner: root + group: root + mode: 0644 + + - name: simple template + template: + src: /root/go/src/github.com/gravitational/teleport/docker/ansible/template.j2 + dest: /tmp/template.out + owner: root + group: root + mode: 0644 diff --git a/docker/ansible/rdir/rdir/rdir b/docker/ansible/rdir/rdir/rdir new file mode 100644 index 0000000000000..6c89a1a91f484 --- /dev/null +++ b/docker/ansible/rdir/rdir/rdir @@ -0,0 +1 @@ +rdir diff --git a/docker/ansible/simple.txt b/docker/ansible/simple.txt new file mode 100644 index 0000000000000..4b5fa63702dd9 --- /dev/null +++ b/docker/ansible/simple.txt @@ -0,0 +1 @@ +hello, world diff --git a/docker/ansible/template.j2 b/docker/ansible/template.j2 new file mode 100644 index 0000000000000..c9b06fed8ce3d --- /dev/null +++ b/docker/ansible/template.j2 @@ -0,0 +1,2 @@ +template_host: {{ template_host }} +template_uid: {{ template_uid }} diff --git a/lib/client/api.go b/lib/client/api.go index 5ff1ec4aeca6f..2eb0bcdd2bda8 100644 --- a/lib/client/api.go +++ b/lib/client/api.go @@ -101,7 +101,8 @@ type Config struct { // HostLogin is a user login on a remote host HostLogin string - // HostPort is a remote host port to connect to + // HostPort is a remote host port to connect to. This is used for **explicit** + // port setting via -p flag, otherwise '0' is passed which means "use server default" HostPort int // ProxyHostPort is a host or IP of the proxy (with optional ":ssh_port,https_port"). @@ -276,16 +277,6 @@ func (c *Config) ProxySSHPort() (retval int) { return retval } -// NodeHostPort returns host:port string based on user supplied data -// either if user has set host:port in the connection string, -// or supplied the -p flag. If user has set both, -p flag data is ignored -func (c *Config) NodeHostPort() string { - if strings.Contains(c.Host, ":") { - return c.Host - } - return net.JoinHostPort(c.Host, strconv.Itoa(c.HostPort)) -} - // ProxySpecified returns true if proxy has been specified func (c *Config) ProxySpecified() bool { return len(c.ProxyHostPort) > 0 diff --git a/lib/client/api_test.go b/lib/client/api_test.go index 1702dd8c96c77..50e3fa7140ff0 100644 --- a/lib/client/api_test.go +++ b/lib/client/api_test.go @@ -72,7 +72,6 @@ func (s *APITestSuite) TestNew(c *check.C) { la := tc.LocalAgent() c.Assert(la, check.NotNil) - c.Assert(tc.NodeHostPort(), check.Equals, "localhost:22") } func (s *APITestSuite) TestParseLabels(c *check.C) { diff --git a/lib/config/configuration.go b/lib/config/configuration.go index 27d7b6006c710..e0f0cf133cdb8 100644 --- a/lib/config/configuration.go +++ b/lib/config/configuration.go @@ -34,6 +34,7 @@ import ( "golang.org/x/crypto/ssh" "github.com/gravitational/teleport" + "github.com/gravitational/teleport/lib/backend" "github.com/gravitational/teleport/lib/client" "github.com/gravitational/teleport/lib/defaults" "github.com/gravitational/teleport/lib/limiter" @@ -191,6 +192,10 @@ func ApplyFileConfig(fc *FileConfig, cfg *service.Config) error { return trace.Errorf("unsupported logger severity: '%v'", fc.Logger.Severity) } + if strings.ToLower(fc.Logger.Output) == "syslog" { + utils.SwitchLoggingtoSyslog() + } + // apply connection throttling: limiters := []limiter.LimiterConfig{ cfg.SSH.Limiter, @@ -545,7 +550,7 @@ func Configure(clf *CommandLineFlags, cfg *service.Config) error { } cfg.Console = ioutil.Discard - utils.InitLoggerDebug() + utils.InitLogger(utils.LoggingForDaemon, log.DebugLevel) } // apply --roles flag: @@ -608,6 +613,12 @@ func Configure(clf *CommandLineFlags, cfg *service.Config) error { cfg.AuthServers = append(cfg.AuthServers, cfg.Auth.SSHAddr) } + // add data_dir to the backend config: + if cfg.Auth.StorageConfig.Params == nil { + cfg.Auth.StorageConfig.Params = backend.Params{} + } + cfg.Auth.StorageConfig.Params["data_dir"] = cfg.DataDir + return nil } diff --git a/lib/services/role.go b/lib/services/role.go index fc6c2280f66f2..c41dd5bb768d8 100644 --- a/lib/services/role.go +++ b/lib/services/role.go @@ -266,8 +266,8 @@ func (r *RoleV2) CheckAndSetDefaults() error { } func (r *RoleV2) String() string { - return fmt.Sprintf("Role(Name=%v,MaxSessionTTL=%v,Logins=%v,NodeLabels=%v,Namespaces=%v,Resources=%v)", - r.GetName(), r.GetMaxSessionTTL(), r.GetLogins(), r.GetNodeLabels(), r.GetNamespaces(), r.GetResources()) + return fmt.Sprintf("Role(Name=%v,MaxSessionTTL=%v,Logins=%v,NodeLabels=%v,Namespaces=%v,Resources=%v,CanForwardAgent=%v)", + r.GetName(), r.GetMaxSessionTTL(), r.GetLogins(), r.GetNodeLabels(), r.GetNamespaces(), r.GetResources(), r.CanForwardAgent()) } // RoleSpecV2 is role specification for RoleV2 diff --git a/lib/srv/exec_test.go b/lib/srv/exec_test.go index 121751c9aaeaf..cd64de2a22951 100644 --- a/lib/srv/exec_test.go +++ b/lib/srv/exec_test.go @@ -19,7 +19,10 @@ package srv import ( "fmt" "net" + "os" "os/user" + "path" + "path/filepath" "gopkg.in/check.v1" @@ -81,7 +84,7 @@ func (s *ExecSuite) TestOSCommandPrep(c *check.C) { cmd, err = prepareCommand(s.ctx) c.Assert(err, check.IsNil) c.Assert(cmd, check.NotNil) - c.Assert(cmd.Path, check.Equals, "/bin/ls") + c.Assert(cmd.Path, check.Equals, findExecutable("ls")) c.Assert(cmd.Args, check.DeepEquals, []string{"ls", "-lh", "/etc"}) c.Assert(cmd.Dir, check.Equals, s.usr.HomeDir) c.Assert(cmd.Env, check.DeepEquals, expectedEnv) @@ -112,3 +115,15 @@ func (s *ExecSuite) OpenChannel(string, []byte) (ssh.Channel, <-chan *ssh.Reques return nil, nil, nil } func (s *ExecSuite) Wait() error { return nil } + +// findExecutable helper finds a given executable name (like 'ls') in $PATH +// and returns the full path +func findExecutable(execName string) string { + for _, dir := range filepath.SplitList(os.Getenv("PATH")) { + fp := path.Join(dir, execName) + if utils.IsFile(fp) { + return fp + } + } + return "not found in $PATH: " + execName +} diff --git a/lib/srv/proxy.go b/lib/srv/proxy.go index 5d4cd28da0a25..81475c26a7ce1 100644 --- a/lib/srv/proxy.go +++ b/lib/srv/proxy.go @@ -247,7 +247,9 @@ func (t *proxySubsys) proxyToHost( // if port is 0, it means the client wants us to figure out // which port to use useExactPort := len(t.port) > 0 && t.port != "0" - ips, _ := net.LookupHost(t.host) + log.Debugf("proxy connecting to host=%v port=%v, exact port=%v\n", t.host, t.port, useExactPort) + + ips, err := net.LookupHost(t.host) // enumerate and try to find a server with self-registered with a matching name/IP: var server services.Server @@ -257,6 +259,7 @@ func (t *proxySubsys) proxyToHost( log.Error(err) continue } + if t.host == ip || t.host == servers[i].GetHostname() || utils.SliceContainsStr(ips, ip) { server = servers[i] // found the server. see if we need to match the port @@ -275,6 +278,7 @@ func (t *proxySubsys) proxyToHost( t.port = strconv.Itoa(defaults.SSHServerListenPort) } serverAddr = net.JoinHostPort(t.host, t.port) + log.Warnf("server lookup failed: using default=%v", serverAddr) } // we must dial by server IP address because hostname diff --git a/lib/srv/sshserver.go b/lib/srv/sshserver.go index 0cb74049f7324..07864640f39ed 100644 --- a/lib/srv/sshserver.go +++ b/lib/srv/sshserver.go @@ -21,6 +21,7 @@ package srv import ( "fmt" "io" + "io/ioutil" "net" "os" "os/exec" @@ -42,7 +43,6 @@ import ( "github.com/gravitational/teleport/lib/sshutils" "github.com/gravitational/teleport/lib/teleagent" "github.com/gravitational/teleport/lib/utils" - "github.com/pborman/uuid" log "github.com/Sirupsen/logrus" "github.com/gravitational/trace" @@ -872,13 +872,17 @@ func (s *Server) handleAgentForward(ch ssh.Channel, req *ssh.Request, ctx *ctx) authChan, _, err := ctx.conn.OpenChannel("auth-agent@openssh.com", nil) if err != nil { - return err + return trace.Wrap(err) } clientAgent := agent.NewClient(authChan) ctx.setAgent(clientAgent, authChan) pid := os.Getpid() - socketPath := filepath.Join(os.TempDir(), fmt.Sprintf("teleport-agent-%v.socket", uuid.New())) + socketDir, err := ioutil.TempDir(os.TempDir(), "teleport-") + if err != nil { + return trace.Wrap(err) + } + socketPath := filepath.Join(socketDir, fmt.Sprintf("teleport-%v.socket", pid)) agentServer := &teleagent.AgentServer{Agent: clientAgent} err = agentServer.ListenUnixSocket(socketPath, uid, gid, 0600) diff --git a/lib/srv/sshserver_test.go b/lib/srv/sshserver_test.go index 56a429890d6ab..e1254d5e14a84 100644 --- a/lib/srv/sshserver_test.go +++ b/lib/srv/sshserver_test.go @@ -24,7 +24,9 @@ import ( "fmt" "io" "net" + "os" "os/user" + "path/filepath" "regexp" "strings" "testing" @@ -238,13 +240,16 @@ func (s *SrvSuite) TestAgentForward(c *C) { _, err = io.WriteString(writer, fmt.Sprintf("printenv %v\n\r", teleport.SSHAuthSock)) c.Assert(err, IsNil) - re := regexp.MustCompile(`/tmp/[^\s]+`) + pattern := filepath.Join(os.TempDir(), `teleport-[0-9]+`, `teleport-[0-9]+.socket`) + re := regexp.MustCompile(pattern) buf := make([]byte, 4096) + result := make([]byte, 0) var matches []string for i := 0; i < 3; i++ { - _, err = reader.Read(buf) + n, err := reader.Read(buf) c.Assert(err, IsNil) - matches = re.FindStringSubmatch(string(buf)) + result = append(result, buf[0:n]...) + matches = re.FindStringSubmatch(string(result)) if len(matches) != 0 { break } @@ -688,8 +693,11 @@ func (s *SrvSuite) TestPTY(c *C) { c.Assert(err, IsNil) defer se.Close() - // request PTY + // request PTY with valid size c.Assert(se.RequestPty("xterm", 30, 30, ssh.TerminalModes{}), IsNil) + + // request PTY with invalid size, should still work (selects defaults) + c.Assert(se.RequestPty("xterm", 0, 0, ssh.TerminalModes{}), IsNil) } // TestEnv requests setting environment variables. (We are currently ignoring these requests) diff --git a/lib/srv/term.go b/lib/srv/term.go index d4800edc6de9a..c3ca9035cdfcf 100644 --- a/lib/srv/term.go +++ b/lib/srv/term.go @@ -68,7 +68,12 @@ func requestPTY(req *ssh.Request) (*terminal, *rsession.TerminalParams, error) { log.Warnf("failed to parse PTY request: %v", err) return nil, nil, trace.Wrap(err) } - log.Debugf("Parsed pty request pty(enn=%v, w=%v, h=%v)", r.Env, r.W, r.H) + err := r.CheckAndSetDefaults() + if err != nil { + return nil, nil, trace.Wrap(err) + } + log.Debugf("Parsed pty request pty(env=%v, w=%v, h=%v)", r.Env, r.W, r.H) + t, err := newTerminal() if err != nil { log.Warnf("failed to create term: %v", err) diff --git a/lib/utils/cli.go b/lib/utils/cli.go index 979bb6fc997c0..5dfdf3309f8f8 100644 --- a/lib/utils/cli.go +++ b/lib/utils/cli.go @@ -33,16 +33,49 @@ import ( "github.com/gravitational/trace" ) -// InitLoggerCLI tools by default log into syslog, not stderr -func InitLoggerCLI() { - log.SetLevel(log.WarnLevel) - // clear existing hooks: +type LoggingPurpose int + +const ( + LoggingForDaemon LoggingPurpose = iota + LoggingForCLI + LoggingForTests +) + +// InitLogger configures the global logger for a given purpose / verbosity level +func InitLogger(purpose LoggingPurpose, level log.Level) { log.StandardLogger().Hooks = make(log.LevelHooks) - log.SetFormatter(&trace.TextFormatter{}) + formatter := &trace.TextFormatter{} + formatter.DisableTimestamp = true + log.SetFormatter(formatter) + log.SetLevel(level) + switch purpose { + case LoggingForCLI: + SwitchLoggingtoSyslog() + case LoggingForDaemon: + log.SetOutput(os.Stderr) + case LoggingForTests: + log.SetLevel(level) + val, _ := strconv.ParseBool(os.Getenv(teleport.VerboseLogsEnvVar)) + if val { + return + } + log.SetLevel(log.WarnLevel) + log.SetOutput(ioutil.Discard) + } +} + +func InitLoggerForTests() { + InitLogger(LoggingForTests, log.WarnLevel) +} + +// SwitchLoggingtoSyslog tells the logger to send the output to syslog +func SwitchLoggingtoSyslog() { + log.StandardLogger().Hooks = make(log.LevelHooks) hook, err := logrusSyslog.NewSyslogHook("", "", syslog.LOG_WARNING, "") if err != nil { // syslog not available + log.SetOutput(os.Stderr) log.Warn("syslog not available. reverting to stderr") } else { // ... and disable stderr: @@ -51,37 +84,6 @@ func InitLoggerCLI() { } } -// InitLoggerDebug configures the logger to dump everything to stderr -func InitLoggerDebug() { - InitDebugLogger(log.DebugLevel) -} - -// InitLoggerVerbose is a less chatty version of debug logger above -func InitLoggerVerbose() { - InitDebugLogger(log.InfoLevel) -} - -func InitDebugLogger(level log.Level) { - // clear existing hooks: - log.StandardLogger().Hooks = make(log.LevelHooks) - log.SetFormatter(&trace.TextFormatter{}) - log.SetOutput(os.Stderr) - log.SetLevel(level) -} - -// InitLoggerForTests inits logger to discard ouput in tests unless -// DEBUG is set to "1" -func InitLoggerForTests() { - val, _ := strconv.ParseBool(os.Getenv(teleport.VerboseLogsEnvVar)) - if val { - InitLoggerDebug() - return - } - log.SetLevel(log.WarnLevel) - log.StandardLogger().Hooks = make(log.LevelHooks) - log.SetOutput(ioutil.Discard) -} - // FatalError is for CLI front-ends: it detects gravitational/trace debugging // information, sends it to the logger, strips it off and prints a clean message to stderr func FatalError(err error) { diff --git a/tool/tctl/common/tctl.go b/tool/tctl/common/tctl.go index 0adb496a47eb6..61ecf2cecaad6 100644 --- a/tool/tctl/common/tctl.go +++ b/tool/tctl/common/tctl.go @@ -126,7 +126,7 @@ type DeleteCommand struct { } func Run() { - utils.InitLoggerCLI() + utils.InitLogger(utils.LoggingForCLI, logrus.WarnLevel) app := utils.InitCLIParser("tctl", GlobalHelpString) // generate default tctl configuration: @@ -840,7 +840,7 @@ func applyConfig(ccf *CLIConfig, cfg *service.Config) error { } // --debug flag if ccf.Debug { - utils.InitLoggerDebug() + utils.InitLogger(utils.LoggingForCLI, logrus.DebugLevel) logrus.Debugf("DEBUG loggign enabled") } return nil diff --git a/tool/teleport/common/teleport.go b/tool/teleport/common/teleport.go index 2c3e8f9d7878e..d578aefab4034 100644 --- a/tool/teleport/common/teleport.go +++ b/tool/teleport/common/teleport.go @@ -47,7 +47,7 @@ func Run(cmdlineArgs []string, testRun bool) (executedCommand string, conf *serv } // configure logger for a typical CLI scenario until configuration file is // parsed - utils.InitLoggerCLI() + utils.InitLogger(utils.LoggingForDaemon, log.WarnLevel) app := utils.InitCLIParser("teleport", "Clustered SSH service. Learn more at http://teleport.gravitational.com") // define global flags: diff --git a/tool/tsh/common/common_test.go b/tool/tsh/common/common_test.go index d955f7c31fb32..563afe81c2923 100644 --- a/tool/tsh/common/common_test.go +++ b/tool/tsh/common/common_test.go @@ -59,7 +59,6 @@ func (s *MainTestSuite) TestMakeClient(c *check.C) { tc, err = makeClient(&conf, true) c.Assert(err, check.IsNil) c.Assert(tc, check.NotNil) - c.Assert(tc.Config.NodeHostPort(), check.Equals, "localhost:0") // SSH port must not be set to default! c.Assert(tc.Config.ProxySSHHostPort(), check.Equals, "proxy:3023") c.Assert(tc.Config.ProxyWebHostPort(), check.Equals, "proxy:3080") localUser, err := client.Username() diff --git a/tool/tsh/common/tsh.go b/tool/tsh/common/tsh.go index afdbaaadee33a..7c835ec8c9a2c 100644 --- a/tool/tsh/common/tsh.go +++ b/tool/tsh/common/tsh.go @@ -32,6 +32,7 @@ import ( "github.com/gravitational/teleport/lib/utils" "github.com/gravitational/trace" + "github.com/Sirupsen/logrus" "github.com/buger/goterm" ) @@ -87,7 +88,7 @@ func Run(args []string, underTest bool) { cf CLIConf ) cf.IsUnderTest = underTest - utils.InitLoggerCLI() + utils.InitLogger(utils.LoggingForCLI, logrus.WarnLevel) // configure CLI argument parser: app := utils.InitCLIParser("tsh", "TSH: Teleport SSH client").Interspersed(false) @@ -148,7 +149,7 @@ func Run(args []string, underTest bool) { // apply -d flag: if *debugMode { - utils.InitLoggerDebug() + utils.InitLogger(utils.LoggingForCLI, logrus.DebugLevel) } switch command { @@ -391,7 +392,7 @@ export SSH_AGENT_PID=%v // makeClient takes the command-line configuration and constructs & returns // a fully configured TeleportClient object func makeClient(cf *CLIConf, useProfileLogin bool) (tc *client.TeleportClient, err error) { - // apply defults + // apply defaults if cf.MinsToLive == 0 { cf.MinsToLive = int32(defaults.CertDuration / time.Minute) }