summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--go.mod8
-rw-r--r--go.sum16
-rw-r--r--integrations/mssql.ini.tmpl1
-rw-r--r--integrations/mysql.ini.tmpl1
-rw-r--r--integrations/mysql8.ini.tmpl1
-rw-r--r--integrations/pgsql.ini.tmpl1
-rw-r--r--integrations/repo_test.go2
-rw-r--r--integrations/sqlite.ini1
-rw-r--r--modules/ssh/ssh.go291
-rw-r--r--vendor/github.com/anmitsu/go-shlex/.gitignore1
-rw-r--r--vendor/github.com/anmitsu/go-shlex/LICENSE20
-rw-r--r--vendor/github.com/anmitsu/go-shlex/README.md38
-rw-r--r--vendor/github.com/anmitsu/go-shlex/shlex.go193
-rw-r--r--vendor/github.com/gliderlabs/ssh/LICENSE27
-rw-r--r--vendor/github.com/gliderlabs/ssh/README.md96
-rw-r--r--vendor/github.com/gliderlabs/ssh/agent.go83
-rw-r--r--vendor/github.com/gliderlabs/ssh/circle.yml26
-rw-r--r--vendor/github.com/gliderlabs/ssh/conn.go55
-rw-r--r--vendor/github.com/gliderlabs/ssh/context.go152
-rw-r--r--vendor/github.com/gliderlabs/ssh/doc.go45
-rw-r--r--vendor/github.com/gliderlabs/ssh/options.go77
-rw-r--r--vendor/github.com/gliderlabs/ssh/server.go394
-rw-r--r--vendor/github.com/gliderlabs/ssh/session.go308
-rw-r--r--vendor/github.com/gliderlabs/ssh/ssh.go123
-rw-r--r--vendor/github.com/gliderlabs/ssh/tcpip.go193
-rw-r--r--vendor/github.com/gliderlabs/ssh/util.go83
-rw-r--r--vendor/github.com/gliderlabs/ssh/wrap.go33
-rw-r--r--vendor/golang.org/x/sys/unix/syscall_linux.go5
-rw-r--r--vendor/golang.org/x/sys/unix/types_netbsd.go1
-rw-r--r--vendor/golang.org/x/sys/unix/types_openbsd.go1
-rw-r--r--vendor/golang.org/x/sys/unix/ztypes_netbsd_386.go1
-rw-r--r--vendor/golang.org/x/sys/unix/ztypes_netbsd_amd64.go1
-rw-r--r--vendor/golang.org/x/sys/unix/ztypes_netbsd_arm.go1
-rw-r--r--vendor/golang.org/x/sys/unix/ztypes_netbsd_arm64.go1
-rw-r--r--vendor/golang.org/x/sys/unix/ztypes_openbsd_386.go1
-rw-r--r--vendor/golang.org/x/sys/unix/ztypes_openbsd_amd64.go1
-rw-r--r--vendor/golang.org/x/sys/unix/ztypes_openbsd_arm.go1
-rw-r--r--vendor/golang.org/x/sys/unix/ztypes_openbsd_arm64.go1
-rw-r--r--vendor/golang.org/x/sys/windows/syscall_windows.go4
-rw-r--r--vendor/golang.org/x/sys/windows/zsyscall_windows.go2
-rw-r--r--vendor/modules.txt8
41 files changed, 2121 insertions, 177 deletions
diff --git a/go.mod b/go.mod
index 22ad85b136..088f987b4d 100644
--- a/go.mod
+++ b/go.mod
@@ -40,7 +40,7 @@ require (
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 // indirect
github.com/facebookgo/stats v0.0.0-20151006221625-1b76add642e4 // indirect
github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 // indirect
- github.com/gliderlabs/ssh v0.1.4 // indirect
+ github.com/gliderlabs/ssh v0.2.2
github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd // indirect
github.com/glycerine/goconvey v0.0.0-20190315024820-982ee783a72e // indirect
github.com/go-macaron/binding v0.0.0-20160711225916-9440f336b443
@@ -110,11 +110,11 @@ require (
github.com/yohcop/openid-go v0.0.0-20160914080427-2c050d2dae53
go.etcd.io/bbolt v1.3.2 // indirect
golang.org/x/crypto v0.0.0-20190618222545-ea8f1a30c443
- golang.org/x/net v0.0.0-20190613194153-d28f0bde5980
+ golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b
golang.org/x/oauth2 v0.0.0-20181101160152-c453e0c75759
- golang.org/x/sys v0.0.0-20190618155005-516e3c20635f
+ golang.org/x/sys v0.0.0-20190620070143-6f217b454f45
golang.org/x/text v0.3.2
- golang.org/x/tools v0.0.0-20190618163018-fdf1049a943a // indirect
+ golang.org/x/tools v0.0.0-20190620154339-431033348dd0 // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/asn1-ber.v1 v1.0.0-20150924051756-4e86f4367175 // indirect
gopkg.in/bufio.v1 v1.0.0-20140618132640-567b2bfa514e // indirect
diff --git a/go.sum b/go.sum
index 80b30fc46e..e074914c8c 100644
--- a/go.sum
+++ b/go.sum
@@ -100,8 +100,8 @@ github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/gliderlabs/ssh v0.1.3/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
-github.com/gliderlabs/ssh v0.1.4 h1:5N8AYXpaQAPy0L7linKa5aI+WRfyYagAhjksVzxh+mI=
-github.com/gliderlabs/ssh v0.1.4/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
+github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
+github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd h1:r04MMPyLHj/QwZuMJ5+7tJcBr1AQjpiAK/rZWRrQT7o=
github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
github.com/glycerine/goconvey v0.0.0-20190315024820-982ee783a72e h1:SiEs4J3BKVIeaWrH3tKaz3QLZhJ68iJ/A4xrzIoE5+Y=
@@ -369,8 +369,8 @@ golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73r
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190502183928-7f726cade0ab/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190613194153-d28f0bde5980 h1:dfGZHvZk057jK2MCeWus/TowKpJ8y4AmooUzdBSR9GU=
-golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b h1:lkjdUzSyJ5P1+eal9fxXX9Xg2BTfswsonKUse48C0uE=
+golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/oauth2 v0.0.0-20180620175406-ef147856a6dd/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181101160152-c453e0c75759 h1:TMrx+Qdx7uJAeUbv15N72h5Hmyb5+VDjEiMufAEAM04=
@@ -395,15 +395,15 @@ golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e h1:nFYrTHrdrAOpShe27kaFHjsqY
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190618155005-516e3c20635f h1:dHNZYIYdq2QuU6w73vZ/DzesPbVlZVYZTtTZmrnsbQ8=
-golang.org/x/sys v0.0.0-20190618155005-516e3c20635f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190620070143-6f217b454f45 h1:Dl2hc890lrizvUppGbRWhnIh2f8jOTCQpY5IKWRS0oM=
+golang.org/x/sys v0.0.0-20190620070143-6f217b454f45/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20190618163018-fdf1049a943a h1:aQmaYPOmKItb96VioBrTlYay5tSNUdKAFEhPCWMeLSM=
-golang.org/x/tools v0.0.0-20190618163018-fdf1049a943a/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190620154339-431033348dd0 h1:qUGDNmGEM+ZBtwF9vuzEv+9nQQPL+l/oNBZ+DCDTAyo=
+golang.org/x/tools v0.0.0-20190620154339-431033348dd0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.2.0 h1:S0iUepdCWODXRvtE+gcRDd15L+k+k1AiHlMiMjefH24=
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
diff --git a/integrations/mssql.ini.tmpl b/integrations/mssql.ini.tmpl
index a8e6d332f0..06d9de6107 100644
--- a/integrations/mssql.ini.tmpl
+++ b/integrations/mssql.ini.tmpl
@@ -34,6 +34,7 @@ LFS_CONTENT_PATH = data/lfs-mssql
OFFLINE_MODE = false
LFS_JWT_SECRET = Tv_MjmZuHqpIY6GFl12ebgkRAMt4RlWt0v4EHKSXO0w
APP_DATA_PATH = integrations/gitea-integration-mssql/data
+BUILTIN_SSH_SERVER_USER = git
[mailer]
ENABLED = true
diff --git a/integrations/mysql.ini.tmpl b/integrations/mysql.ini.tmpl
index 0c9e6c3ecd..44ca51ff8b 100644
--- a/integrations/mysql.ini.tmpl
+++ b/integrations/mysql.ini.tmpl
@@ -34,6 +34,7 @@ LFS_CONTENT_PATH = data/lfs-mysql
OFFLINE_MODE = false
LFS_JWT_SECRET = Tv_MjmZuHqpIY6GFl12ebgkRAMt4RlWt0v4EHKSXO0w
APP_DATA_PATH = integrations/gitea-integration-mysql/data
+BUILTIN_SSH_SERVER_USER = git
[mailer]
ENABLED = true
diff --git a/integrations/mysql8.ini.tmpl b/integrations/mysql8.ini.tmpl
index 7006433276..6db3c880ff 100644
--- a/integrations/mysql8.ini.tmpl
+++ b/integrations/mysql8.ini.tmpl
@@ -34,6 +34,7 @@ LFS_CONTENT_PATH = data/lfs-mysql8
OFFLINE_MODE = false
LFS_JWT_SECRET = Tv_MjmZuHqpIY6GFl12ebgkRAMt4RlWt0v4EHKSXO0w
APP_DATA_PATH = integrations/gitea-integration-mysql8/data
+BUILTIN_SSH_SERVER_USER = git
[mailer]
ENABLED = false
diff --git a/integrations/pgsql.ini.tmpl b/integrations/pgsql.ini.tmpl
index 894a243ba8..e69198c868 100644
--- a/integrations/pgsql.ini.tmpl
+++ b/integrations/pgsql.ini.tmpl
@@ -34,6 +34,7 @@ LFS_CONTENT_PATH = data/lfs-pgsql
OFFLINE_MODE = false
LFS_JWT_SECRET = Tv_MjmZuHqpIY6GFl12ebgkRAMt4RlWt0v4EHKSXO0w
APP_DATA_PATH = integrations/gitea-integration-pgsql/data
+BUILTIN_SSH_SERVER_USER = git
[mailer]
ENABLED = true
diff --git a/integrations/repo_test.go b/integrations/repo_test.go
index 37f163a9fb..b733c9679f 100644
--- a/integrations/repo_test.go
+++ b/integrations/repo_test.go
@@ -73,7 +73,7 @@ func TestViewRepo1CloneLinkAuthorized(t *testing.T) {
assert.Equal(t, setting.AppURL+"user2/repo1.git", link)
link, exists = htmlDoc.doc.Find("#repo-clone-ssh").Attr("data-link")
assert.True(t, exists, "The template has changed")
- sshURL := fmt.Sprintf("ssh://%s@%s:%d/user2/repo1.git", setting.RunUser, setting.SSH.Domain, setting.SSH.Port)
+ sshURL := fmt.Sprintf("ssh://%s@%s:%d/user2/repo1.git", setting.SSH.BuiltinServerUser, setting.SSH.Domain, setting.SSH.Port)
assert.Equal(t, sshURL, link)
}
diff --git a/integrations/sqlite.ini b/integrations/sqlite.ini
index 086081b666..b188406ee9 100644
--- a/integrations/sqlite.ini
+++ b/integrations/sqlite.ini
@@ -31,6 +31,7 @@ OFFLINE_MODE = false
LFS_JWT_SECRET = Tv_MjmZuHqpIY6GFl12ebgkRAMt4RlWt0v4EHKSXO0w
APP_DATA_PATH = integrations/gitea-integration-sqlite/data
ENABLE_GZIP = true
+BUILTIN_SSH_SERVER_USER = git
[mailer]
ENABLED = true
diff --git a/modules/ssh/ssh.go b/modules/ssh/ssh.go
index c5251ef23a..1818f33306 100644
--- a/modules/ssh/ssh.go
+++ b/modules/ssh/ssh.go
@@ -1,4 +1,3 @@
-// Copyright 2014 The Gogs Authors. All rights reserved.
// Copyright 2017 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
@@ -10,178 +9,157 @@ import (
"crypto/rsa"
"crypto/x509"
"encoding/pem"
+ "fmt"
"io"
- "io/ioutil"
- "net"
"os"
"os/exec"
"path/filepath"
"strings"
-
- "github.com/Unknwon/com"
- "golang.org/x/crypto/ssh"
+ "sync"
+ "syscall"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
+
+ "github.com/Unknwon/com"
+ "github.com/gliderlabs/ssh"
+ gossh "golang.org/x/crypto/ssh"
)
-func cleanCommand(cmd string) string {
- i := strings.Index(cmd, "git")
- if i == -1 {
- return cmd
+type contextKey string
+
+const giteaKeyID = contextKey("gitea-key-id")
+
+func getExitStatusFromError(err error) int {
+ if err == nil {
+ return 0
}
- return cmd[i:]
-}
-func handleServerConn(keyID string, chans <-chan ssh.NewChannel) {
- for newChan := range chans {
- if newChan.ChannelType() != "session" {
- err := newChan.Reject(ssh.UnknownChannelType, "unknown channel type")
- if err != nil {
- log.Error("Error rejecting channel: %v", err)
- }
- continue
- }
+ exitErr, ok := err.(*exec.ExitError)
+ if !ok {
+ return 1
+ }
- ch, reqs, err := newChan.Accept()
- if err != nil {
- log.Error("Error accepting channel: %v", err)
- continue
+ waitStatus, ok := exitErr.Sys().(syscall.WaitStatus)
+ if !ok {
+ // This is a fallback and should at least let us return something useful
+ // when running on Windows, even if it isn't completely accurate.
+ if exitErr.Success() {
+ return 0
}
- go func(in <-chan *ssh.Request) {
- defer func() {
- if err = ch.Close(); err != nil {
- log.Error("Close: %v", err)
- }
- }()
- for req := range in {
- payload := cleanCommand(string(req.Payload))
- switch req.Type {
- case "exec":
- cmdName := strings.TrimLeft(payload, "'()")
- log.Trace("SSH: Payload: %v", cmdName)
-
- args := []string{"serv", "key-" + keyID, "--config=" + setting.CustomConf}
- log.Trace("SSH: Arguments: %v", args)
- cmd := exec.Command(setting.AppPath, args...)
- cmd.Env = append(
- os.Environ(),
- "SSH_ORIGINAL_COMMAND="+cmdName,
- "SKIP_MINWINSVC=1",
- )
-
- stdout, err := cmd.StdoutPipe()
- if err != nil {
- log.Error("SSH: StdoutPipe: %v", err)
- return
- }
- stderr, err := cmd.StderrPipe()
- if err != nil {
- log.Error("SSH: StderrPipe: %v", err)
- return
- }
- input, err := cmd.StdinPipe()
- if err != nil {
- log.Error("SSH: StdinPipe: %v", err)
- return
- }
-
- // FIXME: check timeout
- if err = cmd.Start(); err != nil {
- log.Error("SSH: Start: %v", err)
- return
- }
-
- err = req.Reply(true, nil)
- if err != nil {
- log.Error("SSH: Reply: %v", err)
- }
- go func() {
- _, err = io.Copy(input, ch)
- if err != nil {
- log.Error("SSH: Copy: %v", err)
- }
- }()
- _, err = io.Copy(ch, stdout)
- if err != nil {
- log.Error("SSH: Copy: %v", err)
- }
- _, err = io.Copy(ch.Stderr(), stderr)
- if err != nil {
- log.Error("SSH: Copy: %v", err)
- }
-
- if err = cmd.Wait(); err != nil {
- log.Error("SSH: Wait: %v", err)
- return
- }
-
- _, err = ch.SendRequest("exit-status", false, []byte{0, 0, 0, 0})
- if err != nil {
- log.Error("SSH: SendRequest: %v", err)
- }
- return
- default:
- }
- }
- }(reqs)
+ return 1
}
+
+ return waitStatus.ExitStatus()
}
-func listen(config *ssh.ServerConfig, host string, port int) {
- listener, err := net.Listen("tcp", host+":"+com.ToStr(port))
+func sessionHandler(session ssh.Session) {
+ keyID := session.Context().Value(giteaKeyID).(int64)
+
+ command := session.RawCommand()
+
+ log.Trace("SSH: Payload: %v", command)
+
+ args := []string{"serv", "key-" + com.ToStr(keyID), "--config=" + setting.CustomConf}
+ log.Trace("SSH: Arguments: %v", args)
+ cmd := exec.Command(setting.AppPath, args...)
+ cmd.Env = append(
+ os.Environ(),
+ "SSH_ORIGINAL_COMMAND="+command,
+ "SKIP_MINWINSVC=1",
+ )
+
+ stdout, err := cmd.StdoutPipe()
if err != nil {
- log.Fatal("Failed to start SSH server: %v", err)
+ log.Error("SSH: StdoutPipe: %v", err)
+ return
}
- for {
- // Once a ServerConfig has been configured, connections can be accepted.
- conn, err := listener.Accept()
- if err != nil {
- log.Error("SSH: Error accepting incoming connection: %v", err)
- continue
+ stderr, err := cmd.StderrPipe()
+ if err != nil {
+ log.Error("SSH: StderrPipe: %v", err)
+ return
+ }
+ stdin, err := cmd.StdinPipe()
+ if err != nil {
+ log.Error("SSH: StdinPipe: %v", err)
+ return
+ }
+
+ wg := &sync.WaitGroup{}
+ wg.Add(2)
+
+ if err = cmd.Start(); err != nil {
+ log.Error("SSH: Start: %v", err)
+ return
+ }
+
+ go func() {
+ defer stdin.Close()
+ if _, err := io.Copy(stdin, session); err != nil {
+ log.Error("Failed to write session to stdin. %s", err)
+ }
+ }()
+
+ go func() {
+ defer wg.Done()
+ if _, err := io.Copy(session, stdout); err != nil {
+ log.Error("Failed to write stdout to session. %s", err)
+ }
+ }()
+
+ go func() {
+ defer wg.Done()
+ if _, err := io.Copy(session.Stderr(), stderr); err != nil {
+ log.Error("Failed to write stderr to session. %s", err)
}
+ }()
+
+ // Ensure all the output has been written before we wait on the command
+ // to exit.
+ wg.Wait()
+
+ // Wait for the command to exit and log any errors we get
+ err = cmd.Wait()
+ if err != nil {
+ log.Error("SSH: Wait: %v", err)
+ }
+
+ if err := session.Exit(getExitStatusFromError(err)); err != nil {
+ log.Error("Session failed to exit. %s", err)
+ }
+}
+
+func publicKeyHandler(ctx ssh.Context, key ssh.PublicKey) bool {
+ if ctx.User() != setting.SSH.BuiltinServerUser {
+ return false
+ }
- // Before use, a handshake must be performed on the incoming net.Conn.
- // It must be handled in a separate goroutine,
- // otherwise one user could easily block entire loop.
- // For example, user could be asked to trust server key fingerprint and hangs.
- go func() {
- log.Trace("SSH: Handshaking for %s", conn.RemoteAddr())
- sConn, chans, reqs, err := ssh.NewServerConn(conn, config)
- if err != nil {
- if err == io.EOF {
- log.Warn("SSH: Handshaking with %s was terminated: %v", conn.RemoteAddr(), err)
- } else {
- log.Error("SSH: Error on handshaking with %s: %v", conn.RemoteAddr(), err)
- }
- return
- }
-
- log.Trace("SSH: Connection from %s (%s)", sConn.RemoteAddr(), sConn.ClientVersion())
- // The incoming Request channel must be serviced.
- go ssh.DiscardRequests(reqs)
- go handleServerConn(sConn.Permissions.Extensions["key-id"], chans)
- }()
+ pkey, err := models.SearchPublicKeyByContent(strings.TrimSpace(string(gossh.MarshalAuthorizedKey(key))))
+ if err != nil {
+ log.Error("SearchPublicKeyByContent: %v", err)
+ return false
}
+
+ ctx.SetValue(giteaKeyID, pkey.ID)
+
+ return true
}
// Listen starts a SSH server listens on given port.
func Listen(host string, port int, ciphers []string, keyExchanges []string, macs []string) {
- config := &ssh.ServerConfig{
- Config: ssh.Config{
- Ciphers: ciphers,
- KeyExchanges: keyExchanges,
- MACs: macs,
- },
- PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
- pkey, err := models.SearchPublicKeyByContent(strings.TrimSpace(string(ssh.MarshalAuthorizedKey(key))))
- if err != nil {
- log.Error("SearchPublicKeyByContent: %v", err)
- return nil, err
- }
- return &ssh.Permissions{Extensions: map[string]string{"key-id": com.ToStr(pkey.ID)}}, nil
+ // TODO: Handle ciphers, keyExchanges, and macs
+
+ srv := ssh.Server{
+ Addr: fmt.Sprintf("%s:%d", host, port),
+ PublicKeyHandler: publicKeyHandler,
+ Handler: sessionHandler,
+
+ // We need to explicitly disable the PtyCallback so text displays
+ // properly.
+ PtyCallback: func(ctx ssh.Context, pty ssh.Pty) bool {
+ return false
},
}
@@ -197,20 +175,21 @@ func Listen(host string, port int, ciphers []string, keyExchanges []string, macs
if err != nil {
log.Fatal("Failed to generate private key: %v", err)
}
- log.Trace("SSH: New private key is generateed: %s", keyPath)
+ log.Trace("New private key is generated: %s", keyPath)
}
- privateBytes, err := ioutil.ReadFile(keyPath)
+ err := srv.SetOption(ssh.HostKeyFile(keyPath))
if err != nil {
- log.Fatal("SSH: Failed to load private key")
+ log.Error("Failed to set Host Key. %s", err)
}
- private, err := ssh.ParsePrivateKey(privateBytes)
- if err != nil {
- log.Fatal("SSH: Failed to parse private key")
- }
- config.AddHostKey(private)
- go listen(config, host, port)
+ go func() {
+ err := srv.ListenAndServe()
+ if err != nil {
+ log.Error("Failed to serve with builtin SSH server. %s", err)
+ }
+ }()
+
}
// GenKeyPair make a pair of public and private keys for SSH access.
@@ -238,12 +217,12 @@ func GenKeyPair(keyPath string) error {
}
// generate public key
- pub, err := ssh.NewPublicKey(&privateKey.PublicKey)
+ pub, err := gossh.NewPublicKey(&privateKey.PublicKey)
if err != nil {
return err
}
- public := ssh.MarshalAuthorizedKey(pub)
+ public := gossh.MarshalAuthorizedKey(pub)
p, err := os.OpenFile(keyPath+".pub", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
if err != nil {
return err
diff --git a/vendor/github.com/anmitsu/go-shlex/.gitignore b/vendor/github.com/anmitsu/go-shlex/.gitignore
new file mode 100644
index 0000000000..0b46bb5df1
--- /dev/null
+++ b/vendor/github.com/anmitsu/go-shlex/.gitignore
@@ -0,0 +1 @@
+shlex.test
diff --git a/vendor/github.com/anmitsu/go-shlex/LICENSE b/vendor/github.com/anmitsu/go-shlex/LICENSE
new file mode 100644
index 0000000000..4a17268ac0
--- /dev/null
+++ b/vendor/github.com/anmitsu/go-shlex/LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) anmitsu <anmitsu.s@gmail.com>
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/vendor/github.com/anmitsu/go-shlex/README.md b/vendor/github.com/anmitsu/go-shlex/README.md
new file mode 100644
index 0000000000..c4ffe72be1
--- /dev/null
+++ b/vendor/github.com/anmitsu/go-shlex/README.md
@@ -0,0 +1,38 @@
+# go-shlex
+
+go-shlex is a library to make a lexical analyzer like Unix shell for
+Go.
+
+## Install
+
+ go get -u "github.com/anmitsu/go-shlex"
+
+## Usage
+
+```go
+package main
+
+import (
+ "fmt"
+ "log"
+
+ "github.com/anmitsu/go-shlex"
+)
+
+func main() {
+ cmd := `cp -Rdp "file name" 'file name2' dir\ name`
+ words, err := shlex.Split(cmd, true)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ for _, w := range words {
+ fmt.Println(w)
+ }
+}
+```
+
+## Documentation
+
+http://godoc.org/github.com/anmitsu/go-shlex
+
diff --git a/vendor/github.com/anmitsu/go-shlex/shlex.go b/vendor/github.com/anmitsu/go-shlex/shlex.go
new file mode 100644
index 0000000000..e742c38afc
--- /dev/null
+++ b/vendor/github.com/anmitsu/go-shlex/shlex.go
@@ -0,0 +1,193 @@
+// Package shlex provides a simple lexical analysis like Unix shell.
+package shlex
+
+import (
+ "bufio"
+ "errors"
+ "io"
+ "strings"
+ "unicode"
+)
+
+var (
+ ErrNoClosing = errors.New("No closing quotation")
+ ErrNoEscaped = errors.New("No escaped character")
+)
+
+// Tokenizer is the interface that classifies a token according to
+// words, whitespaces, quotations, escapes and escaped quotations.
+type Tokenizer interface {
+ IsWord(rune) bool
+ IsWhitespace(rune) bool
+ IsQuote(rune) bool
+ IsEscape(rune) bool
+ IsEscapedQuote(rune) bool
+}
+
+// DefaultTokenizer implements a simple tokenizer like Unix shell.
+type DefaultTokenizer struct{}
+
+func (t *DefaultTokenizer) IsWord(r rune) bool {
+ return r == '_' || unicode.IsLetter(r) || unicode.IsNumber(r)
+}
+func (t *DefaultTokenizer) IsQuote(r rune) bool {
+ switch r {
+ case '\'', '"':
+ return true
+ default:
+ return false
+ }
+}
+func (t *DefaultTokenizer) IsWhitespace(r rune) bool {
+ return unicode.IsSpace(r)
+}
+func (t *DefaultTokenizer) IsEscape(r rune) bool {
+ return r == '\\'
+}
+func (t *DefaultTokenizer) IsEscapedQuote(r rune) bool {
+ return r == '"'
+}
+
+// Lexer represents a lexical analyzer.
+type Lexer struct {
+ reader *bufio.Reader
+ tokenizer Tokenizer
+ posix bool
+ whitespacesplit bool
+}
+
+// NewLexer creates a new Lexer reading from io.Reader. This Lexer
+// has a DefaultTokenizer according to posix and whitespacesplit
+// rules.
+func NewLexer(r io.Reader, posix, whitespacesplit bool) *Lexer {
+ return &Lexer{
+ reader: bufio.NewReader(r),
+ tokenizer: &DefaultTokenizer{},
+ posix: posix,
+ whitespacesplit: whitespacesplit,
+ }
+}
+
+// NewLexerString creates a new Lexer reading from a string. This
+// Lexer has a DefaultTokenizer according to posix and whitespacesplit
+// rules.
+func NewLexerString(s string, posix, whitespacesplit bool) *Lexer {
+ return NewLexer(strings.NewReader(s), posix, whitespacesplit)
+}
+
+// Split splits a string according to posix or non-posix rules.
+func Split(s string, posix bool) ([]string, error) {
+ return NewLexerString(s, posix, true).Split()
+}
+
+// SetTokenizer sets a Tokenizer.
+func (l *Lexer) SetTokenizer(t Tokenizer) {
+ l.tokenizer = t
+}
+
+func (l *Lexer) Split() ([]string, error) {
+ result := make([]string, 0)
+ for {
+ token, err := l.readToken()
+ if token != "" {
+ result = append(result, token)
+ }
+
+ if err == io.EOF {
+ break
+ } else if err != nil {
+ return result, err
+ }
+ }
+ return result, nil
+}
+
+func (l *Lexer) readToken() (string, error) {
+ t := l.tokenizer
+ token := ""
+ quoted := false
+ state := ' '
+ escapedstate := ' '
+scanning:
+ for {
+ next, _, err := l.reader.ReadRune()
+ if err != nil {
+ if t.IsQuote(state) {
+ return token, ErrNoClosing
+ } else if t.IsEscape(state) {
+ return token, ErrNoEscaped
+ }
+ return token, err
+ }
+
+ switch {
+ case t.IsWhitespace(state):
+ switch {
+ case t.IsWhitespace(next):
+ break scanning
+ case l.posix && t.IsEscape(next):
+ escapedstate = 'a'
+ state = next
+ case t.IsWord(next):
+ token += string(next)
+ state = 'a'
+ case t.IsQuote(next):
+ if !l.posix {
+ token += string(next)
+ }
+ state = next
+ default:
+ token = string(next)
+ if l.whitespacesplit {
+ state = 'a'
+ } else if token != "" || (l.posix && quoted) {
+ break scanning
+ }
+ }
+ case t.IsQuote(state):
+ quoted = true
+ switch {
+ case next == state:
+ if !l.posix {
+ token += string(next)
+ break scanning
+ } else {
+ state = 'a'
+ }
+ case l.posix && t.IsEscape(next) && t.IsEscapedQuote(state):
+ escapedstate = state
+ state = next
+ default:
+ token += string(next)
+ }
+ case t.IsEscape(state):
+ if t.IsQuote(escapedstate) && next != state && next != escapedstate {
+ token += string(state)
+ }
+ token += string(next)
+ state = escapedstate
+ case t.IsWord(state):
+ switch {
+ case t.IsWhitespace(next):
+ if token != "" || (l.posix && quoted) {
+ break scanning
+ }
+ case l.posix && t.IsQuote(next):
+ state = next
+ case l.posix && t.IsEscape(next):
+ escapedstate = 'a'
+ state = next
+ case t.IsWord(next) || t.IsQuote(next):
+ token += string(next)
+ default:
+ if l.whitespacesplit {
+ token += string(next)
+ } else if token != "" {
+ l.reader.UnreadRune()
+ break scanning
+ }
+ }
+ }
+ }
+ return token, nil
+}
diff --git a/vendor/github.com/gliderlabs/ssh/LICENSE b/vendor/github.com/gliderlabs/ssh/LICENSE
new file mode 100644
index 0000000000..4a03f02a28
--- /dev/null
+++ b/vendor/github.com/gliderlabs/ssh/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2016 Glider Labs. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Glider Labs nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/github.com/gliderlabs/ssh/README.md b/vendor/github.com/gliderlabs/ssh/README.md
new file mode 100644
index 0000000000..0e976e34f9
--- /dev/null
+++ b/vendor/github.com/gliderlabs/ssh/README.md
@@ -0,0 +1,96 @@
+# gliderlabs/ssh
+
+[![GoDoc](https://godoc.org/github.com/gliderlabs/ssh?status.svg)](https://godoc.org/github.com/gliderlabs/ssh)
+[![CircleCI](https://img.shields.io/circleci/project/github/gliderlabs/ssh.svg)](https://circleci.com/gh/gliderlabs/ssh)
+[![Go Report Card](https://goreportcard.com/badge/github.com/gliderlabs/ssh)](https://goreportcard.com/report/github.com/gliderlabs/ssh)
+[![OpenCollective](https://opencollective.com/ssh/sponsors/badge.svg)](#sponsors)
+[![Slack](http://slack.gliderlabs.com/badge.svg)](http://slack.gliderlabs.com)
+[![Email Updates](https://img.shields.io/badge/updates-subscribe-yellow.svg)](https://app.convertkit.com/landing_pages/243312)
+
+> The Glider Labs SSH server package is dope. &mdash;[@bradfitz](https://twitter.com/bradfitz), Go team member
+
+This Go package wraps the [crypto/ssh
+package](https://godoc.org/golang.org/x/crypto/ssh) with a higher-level API for
+building SSH servers. The goal of the API was to make it as simple as using
+[net/http](https://golang.org/pkg/net/http/), so the API is very similar:
+
+```go
+ package main
+
+ import (
+ "github.com/gliderlabs/ssh"
+ "io"
+ "log"
+ )
+
+ func main() {
+ ssh.Handle(func(s ssh.Session) {
+ io.WriteString(s, "Hello world\n")
+ })
+
+ log.Fatal(ssh.ListenAndServe(":2222", nil))
+ }
+
+```
+This package was built by [@progrium](https://twitter.com/progrium) after working on nearly a dozen projects at Glider Labs using SSH and collaborating with [@shazow](https://twitter.com/shazow) (known for [ssh-chat](https://github.com/shazow/ssh-chat)).
+
+## Examples
+
+A bunch of great examples are in the `_examples` directory.
+
+## Usage
+
+[See GoDoc reference.](https://godoc.org/github.com/gliderlabs/ssh)
+
+## Contributing
+
+Pull requests are welcome! However, since this project is very much about API
+design, please submit API changes as issues to discuss before submitting PRs.
+
+Also, you can [join our Slack](http://slack.gliderlabs.com) to discuss as well.
+
+## Roadmap
+
+* Non-session channel handlers
+* Cleanup callback API
+* 1.0 release
+* High-level client?
+
+## Sponsors
+
+Become a sponsor and get your logo on our README on Github with a link to your site. [[Become a sponsor](https://opencollective.com/ssh#sponsor)]
+
+<a href="https://opencollective.com/ssh/sponsor/0/website" target="_blank"><img src="https://opencollective.com/ssh/sponsor/0/avatar.svg"></a>
+<a href="https://opencollective.com/ssh/sponsor/1/website" target="_blank"><img src="https://opencollective.com/ssh/sponsor/1/avatar.svg"></a>
+<a href="https://opencollective.com/ssh/sponsor/2/website" target="_blank"><img src="https://opencollective.com/ssh/sponsor/2/avatar.svg"></a>
+<a href="https://opencollective.com/ssh/sponsor/3/website" target="_blank"><img src="https://opencollective.com/ssh/sponsor/3/avatar.svg"></a>
+<a href="https://opencollective.com/ssh/sponsor/4/website" target="_blank"><img src="https://opencollective.com/ssh/sponsor/4/avatar.svg"></a>
+<a href="https://opencollective.com/ssh/sponsor/5/website" target="_blank"><img src="https://opencollective.com/ssh/sponsor/5/avatar.svg"></a>
+<a href="https://opencollective.com/ssh/sponsor/6/website" target="_blank"><img src="https://opencollective.com/ssh/sponsor/6/avatar.svg"></a>
+<a href="https://opencollective.com/ssh/sponsor/7/website" target="_blank"><img src="https://opencollective.com/ssh/sponsor/7/avatar.svg"></a>
+<a href="https://opencollective.com/ssh/sponsor/8/website" target="_blank"><img src="https://opencollective.com/ssh/sponsor/8/avatar.svg"></a>
+<a href="https://opencollective.com/ssh/sponsor/9/website" target="_blank"><img src="https://opencollective.com/ssh/sponsor/9/avatar.svg"></a>
+<a href="https://opencollective.com/ssh/sponsor/10/website" target="_blank"><img src="https://opencollective.com/ssh/sponsor/10/avatar.svg"></a>
+<a href="https://opencollective.com/ssh/sponsor/11/website" target="_blank"><img src="https://opencollective.com/ssh/sponsor/11/avatar.svg"></a>
+<a href="https://opencollective.com/ssh/sponsor/12/website" target="_blank"><img src="https://opencollective.com/ssh/sponsor/12/avatar.svg"></a>
+<a href="https://opencollective.com/ssh/sponsor/13/website" target="_blank"><img src="https://opencollective.com/ssh/sponsor/13/avatar.svg"></a>
+<a href="https://opencollective.com/ssh/sponsor/14/website" target="_blank"><img src="https://opencollective.com/ssh/sponsor/14/avatar.svg"></a>
+<a href="https://opencollective.com/ssh/sponsor/15/website" target="_blank"><img src="https://opencollective.com/ssh/sponsor/15/avatar.svg"></a>
+<a href="https://opencollective.com/ssh/sponsor/16/website" target="_blank"><img src="https://opencollective.com/ssh/sponsor/16/avatar.svg"></a>
+<a href="https://opencollective.com/ssh/sponsor/17/website" target="_blank"><img src="https://opencollective.com/ssh/sponsor/17/avatar.svg"></a>
+<a href="https://opencollective.com/ssh/sponsor/18/website" target="_blank"><img src="https://opencollective.com/ssh/sponsor/18/avatar.svg"></a>
+<a href="https://opencollective.com/ssh/sponsor/19/website" target="_blank"><img src="https://opencollective.com/ssh/sponsor/19/avatar.svg"></a>
+<a href="https://opencollective.com/ssh/sponsor/20/website" target="_blank"><img src="https://opencollective.com/ssh/sponsor/20/avatar.svg"></a>
+<a href="https://opencollective.com/ssh/sponsor/21/website" target="_blank"><img src="https://opencollective.com/ssh/sponsor/21/avatar.svg"></a>
+<a href="https://opencollective.com/ssh/sponsor/22/website" target="_blank"><img src="https://opencollective.com/ssh/sponsor/22/avatar.svg"></a>
+<a href="https://opencollective.com/ssh/sponsor/23/website" target="_blank"><img src="https://opencollective.com/ssh/sponsor/23/avatar.svg"></a>
+<a href="https://opencollective.com/ssh/sponsor/24/website" target="_blank"><img src="https://opencollective.com/ssh/sponsor/24/avatar.svg"></a>
+<a href="https://opencollective.com/ssh/sponsor/25/website" target="_blank"><img src="https://opencollective.com/ssh/sponsor/25/avatar.svg"></a>
+<a href="https://opencollective.com/ssh/sponsor/26/website" target="_blank"><img src="https://opencollective.com/ssh/sponsor/26/avatar.svg"></a>
+<a href="https://opencollective.com/ssh/sponsor/27/website" target="_blank"><img src="https://opencollective.com/ssh/sponsor/27/avatar.svg"></a>
+<a href="https://opencollective.com/ssh/sponsor/28/website" target="_blank"><img src="https://opencollective.com/ssh/sponsor/28/avatar.svg"></a>
+<a href="https://opencollective.com/ssh/sponsor/29/website" target="_blank"><img src="https://opencollective.com/ssh/sponsor/29/avatar.svg"></a>
+
+## License
+
+BSD
diff --git a/vendor/github.com/gliderlabs/ssh/agent.go b/vendor/github.com/gliderlabs/ssh/agent.go
new file mode 100644
index 0000000000..d8dcb9a0a4
--- /dev/null
+++ b/vendor/github.com/gliderlabs/ssh/agent.go
@@ -0,0 +1,83 @@
+package ssh
+
+import (
+ "io"
+ "io/ioutil"
+ "net"
+ "path"
+ "sync"
+
+ gossh "golang.org/x/crypto/ssh"
+)
+
+const (
+ agentRequestType = "auth-agent-req@openssh.com"
+ agentChannelType = "auth-agent@openssh.com"
+
+ agentTempDir = "auth-agent"
+ agentListenFile = "listener.sock"
+)
+
+// contextKeyAgentRequest is an internal context key for storing if the
+// client requested agent forwarding
+var contextKeyAgentRequest = &contextKey{"auth-agent-req"}
+
+// SetAgentRequested sets up the session context so that AgentRequested
+// returns true.
+func SetAgentRequested(ctx Context) {
+ ctx.SetValue(contextKeyAgentRequest, true)
+}
+
+// AgentRequested returns true if the client requested agent forwarding.
+func AgentRequested(sess Session) bool {
+ return sess.Context().Value(contextKeyAgentRequest) == true
+}
+
+// NewAgentListener sets up a temporary Unix socket that can be communicated
+// to the session environment and used for forwarding connections.
+func NewAgentListener() (net.Listener, error) {
+ dir, err := ioutil.TempDir("", agentTempDir)
+ if err != nil {
+ return nil, err
+ }
+ l, err := net.Listen("unix", path.Join(dir, agentListenFile))
+ if err != nil {
+ return nil, err
+ }
+ return l, nil
+}
+
+// ForwardAgentConnections takes connections from a listener to proxy into the
+// session on the OpenSSH channel for agent connections. It blocks and services
+// connections until the listener stop accepting.
+func ForwardAgentConnections(l net.Listener, s Session) {
+ sshConn := s.Context().Value(ContextKeyConn).(gossh.Conn)
+ for {
+ conn, err := l.Accept()
+ if err != nil {
+ return
+ }
+ go func(conn net.Conn) {
+ defer conn.Close()
+ channel, reqs, err := sshConn.OpenChannel(agentChannelType, nil)
+ if err != nil {
+ return
+ }
+ defer channel.Close()
+ go gossh.DiscardRequests(reqs)
+ var wg sync.WaitGroup
+ wg.Add(2)
+ go func() {
+ io.Copy(conn, channel)
+ conn.(*net.UnixConn).CloseWrite()
+ wg.Done()
+ }()
+ go func() {
+ io.Copy(channel, conn)
+ channel.CloseWrite()
+ wg.Done()
+ }()
+ wg.Wait()
+ }(conn)
+ }
+}
diff --git a/vendor/github.com/gliderlabs/ssh/circle.yml b/vendor/github.com/gliderlabs/ssh/circle.yml
new file mode 100644
index 0000000000..08616b01c3
--- /dev/null
+++ b/vendor/github.com/gliderlabs/ssh/circle.yml
@@ -0,0 +1,26 @@
+version: 2
+jobs:
+ build-go-latest:
+ docker:
+ - image: golang:latest
+ working_directory: /go/src/github.com/gliderlabs/ssh
+ steps:
+ - checkout
+ - run: go get
+ - run: go test -v -race
+
+ build-go-1.9:
+ docker:
+ - image: golang:1.9
+ working_directory: /go/src/github.com/gliderlabs/ssh
+ steps:
+ - checkout
+ - run: go get
+ - run: go test -v -race
+
+workflows:
+ version: 2
+ build:
+ jobs:
+ - build-go-latest
+ - build-go-1.9
diff --git a/vendor/github.com/gliderlabs/ssh/conn.go b/vendor/github.com/gliderlabs/ssh/conn.go
new file mode 100644
index 0000000000..ebef8845ba
--- /dev/null
+++ b/vendor/github.com/gliderlabs/ssh/conn.go
@@ -0,0 +1,55 @@
+package ssh
+
+import (
+ "context"
+ "net"
+ "time"
+)
+
+type serverConn struct {
+ net.Conn
+
+ idleTimeout time.Duration
+ maxDeadline time.Time
+ closeCanceler context.CancelFunc
+}
+
+func (c *serverConn) Write(p []byte) (n int, err error) {
+ c.updateDeadline()
+ n, err = c.Conn.Write(p)
+ if _, isNetErr := err.(net.Error); isNetErr && c.closeCanceler != nil {
+ c.closeCanceler()
+ }
+ return
+}
+
+func (c *serverConn) Read(b []byte) (n int, err error) {
+ c.updateDeadline()
+ n, err = c.Conn.Read(b)
+ if _, isNetErr := err.(net.Error); isNetErr && c.closeCanceler != nil {
+ c.closeCanceler()
+ }
+ return
+}
+
+func (c *serverConn) Close() (err error) {
+ err = c.Conn.Close()
+ if c.closeCanceler != nil {
+ c.closeCanceler()
+ }
+ return
+}
+
+func (c *serverConn) updateDeadline() {
+ switch {
+ case c.idleTimeout > 0:
+ idleDeadline := time.Now().Add(c.idleTimeout)
+ if idleDeadline.Unix() < c.maxDeadline.Unix() || c.maxDeadline.IsZero() {
+ c.Conn.SetDeadline(idleDeadline)
+ return
+ }
+ fallthrough
+ default:
+ c.Conn.SetDeadline(c.maxDeadline)
+ }
+}
diff --git a/vendor/github.com/gliderlabs/ssh/context.go b/vendor/github.com/gliderlabs/ssh/context.go
new file mode 100644
index 0000000000..2f61a40d61
--- /dev/null
+++ b/vendor/github.com/gliderlabs/ssh/context.go
@@ -0,0 +1,152 @@
+package ssh
+
+import (
+ "context"
+ "encoding/hex"
+ "net"
+ "sync"
+
+ gossh "golang.org/x/crypto/ssh"
+)
+
+// contextKey is a value for use with context.WithValue. It's used as
+// a pointer so it fits in an interface{} without allocation.
+type contextKey struct {
+ name string
+}
+
+var (
+ // ContextKeyUser is a context key for use with Contexts in this package.
+ // The associated value will be of type string.
+ ContextKeyUser = &contextKey{"user"}
+
+ // ContextKeySessionID is a context key for use with Contexts in this package.
+ // The associated value will be of type string.
+ ContextKeySessionID = &contextKey{"session-id"}
+
+ // ContextKeyPermissions is a context key for use with Contexts in this package.
+ // The associated value will be of type *Permissions.
+ ContextKeyPermissions = &contextKey{"permissions"}
+
+ // ContextKeyClientVersion is a context key for use with Contexts in this package.
+ // The associated value will be of type string.
+ ContextKeyClientVersion = &contextKey{"client-version"}
+
+ // ContextKeyServerVersion is a context key for use with Contexts in this package.
+ // The associated value will be of type string.
+ ContextKeyServerVersion = &contextKey{"server-version"}
+
+ // ContextKeyLocalAddr is a context key for use with Contexts in this package.
+ // The associated value will be of type net.Addr.
+ ContextKeyLocalAddr = &contextKey{"local-addr"}
+
+ // ContextKeyRemoteAddr is a context key for use with Contexts in this package.
+ // The associated value will be of type net.Addr.
+ ContextKeyRemoteAddr = &contextKey{"remote-addr"}
+
+ // ContextKeyServer is a context key for use with Contexts in this package.
+ // The associated value will be of type *Server.
+ ContextKeyServer = &contextKey{"ssh-server"}
+
+ // ContextKeyConn is a context key for use with Contexts in this package.
+ // The associated value will be of type gossh.ServerConn.
+ ContextKeyConn = &contextKey{"ssh-conn"}
+
+ // ContextKeyPublicKey is a context key for use with Contexts in this package.
+ // The associated value will be of type PublicKey.
+ ContextKeyPublicKey = &contextKey{"public-key"}
+)
+
+// Context is a package specific context interface. It exposes connection
+// metadata and allows new values to be easily written to it. It's used in
+// authentication handlers and callbacks, and its underlying context.Context is
+// exposed on Session in the session Handler. A connection-scoped lock is also
+// embedded in the context to make it easier to limit operations per-connection.
+type Context interface {
+ context.Context
+ sync.Locker
+
+ // User returns the username used when establishing the SSH connection.
+ User() string
+
+ // SessionID returns the session hash.
+ SessionID() string
+
+ // ClientVersion returns the version reported by the client.
+ ClientVersion() string
+
+ // ServerVersion returns the version reported by the server.
+ ServerVersion() string
+
+ // RemoteAddr returns the remote address for this connection.
+ RemoteAddr() net.Addr
+
+ // LocalAddr returns the local address for this connection.
+ LocalAddr() net.Addr
+
+ // Permissions returns the Permissions object used for this connection.
+ Permissions() *Permissions
+
+ // SetValue allows you to easily write new values into the underlying context.
+ SetValue(key, value interface{})
+}
+
+type sshContext struct {
+ context.Context
+ *sync.Mutex
+}
+
+func newContext(srv *Server) (*sshContext, context.CancelFunc) {
+ innerCtx, cancel := context.WithCancel(context.Background())
+ ctx := &sshContext{innerCtx, &sync.Mutex{}}
+ ctx.SetValue(ContextKeyServer, srv)
+ perms := &Permissions{&gossh.Permissions{}}
+ ctx.SetValue(ContextKeyPermissions, perms)
+ return ctx, cancel
+}
+
+// this is separate from newContext because we will get ConnMetadata
+// at different points so it needs to be applied separately
+func applyConnMetadata(ctx Context, conn gossh.ConnMetadata) {
+ if ctx.Value(ContextKeySessionID) != nil {
+ return
+ }
+ ctx.SetValue(ContextKeySessionID, hex.EncodeToString(conn.SessionID()))
+ ctx.SetValue(ContextKeyClientVersion, string(conn.ClientVersion()))
+ ctx.SetValue(ContextKeyServerVersion, string(conn.ServerVersion()))
+ ctx.SetValue(ContextKeyUser, conn.User())
+ ctx.SetValue(ContextKeyLocalAddr, conn.LocalAddr())
+ ctx.SetValue(ContextKeyRemoteAddr, conn.RemoteAddr())
+}
+
+func (ctx *sshContext) SetValue(key, value interface{}) {
+ ctx.Context = context.WithValue(ctx.Context, key, value)
+}
+
+func (ctx *sshContext) User() string {
+ return ctx.Value(ContextKeyUser).(string)
+}
+
+func (ctx *sshContext) SessionID() string {
+ return ctx.Value(ContextKeySessionID).(string)
+}
+
+func (ctx *sshContext) ClientVersion() string {
+ return ctx.Value(ContextKeyClientVersion).(string)
+}
+
+func (ctx *sshContext) ServerVersion() string {
+ return ctx.Value(ContextKeyServerVersion).(string)
+}
+
+func (ctx *sshContext) RemoteAddr() net.Addr {
+ return ctx.Value(ContextKeyRemoteAddr).(net.Addr)
+}
+
+func (ctx *sshContext) LocalAddr() net.Addr {
+ return ctx.Value(ContextKeyLocalAddr).(net.Addr)
+}
+
+func (ctx *sshContext) Permissions() *Permissions {
+ return ctx.Value(ContextKeyPermissions).(*Permissions)
+}
diff --git a/vendor/github.com/gliderlabs/ssh/doc.go b/vendor/github.com/gliderlabs/ssh/doc.go
new file mode 100644
index 0000000000..5a10393c2c
--- /dev/null
+++ b/vendor/github.com/gliderlabs/ssh/doc.go
@@ -0,0 +1,45 @@
+/*
+Package ssh wraps the crypto/ssh package with a higher-level API for building
+SSH servers. The goal of the API was to make it as simple as using net/http, so
+the API is very similar.
+
+You should be able to build any SSH server using only this package, which wraps
+relevant types and some functions from crypto/ssh. However, you still need to
+use crypto/ssh for building SSH clients.
+
+ListenAndServe starts an SSH server with a given address, handler, and options. The
+handler is usually nil, which means to use DefaultHandler. Handle sets DefaultHandler:
+
+ ssh.Handle(func(s ssh.Session) {
+ io.WriteString(s, "Hello world\n")
+ })
+
+ log.Fatal(ssh.ListenAndServe(":2222", nil))
+
+If you don't specify a host key, it will generate one every time. This is convenient
+except you'll have to deal with clients being confused that the host key is different.
+It's a better idea to generate or point to an existing key on your system:
+
+ log.Fatal(ssh.ListenAndServe(":2222", nil, ssh.HostKeyFile("/Users/progrium/.ssh/id_rsa")))
+
+Although all options have functional option helpers, another way to control the
+server's behavior is by creating a custom Server:
+
+ s := &ssh.Server{
+ Addr: ":2222",
+ Handler: sessionHandler,
+ PublicKeyHandler: authHandler,
+ }
+ s.AddHostKey(hostKeySigner)
+
+ log.Fatal(s.ListenAndServe())
+
+This package automatically handles basic SSH requests like setting environment
+variables, requesting PTY, and changing window size. These requests are
+processed, responded to, and any relevant state is updated. This state is then
+exposed to you via the Session interface.
+
+The one big feature missing from the Session abstraction is signals. This was
+started, but not completed. Pull Requests welcome!
+*/
+package ssh
diff --git a/vendor/github.com/gliderlabs/ssh/options.go b/vendor/github.com/gliderlabs/ssh/options.go
new file mode 100644
index 0000000000..af748985be
--- /dev/null
+++ b/vendor/github.com/gliderlabs/ssh/options.go
@@ -0,0 +1,77 @@
+package ssh
+
+import (
+ "io/ioutil"
+
+ gossh "golang.org/x/crypto/ssh"
+)
+
+// PasswordAuth returns a functional option that sets PasswordHandler on the server.
+func PasswordAuth(fn PasswordHandler) Option {
+ return func(srv *Server) error {
+ srv.PasswordHandler = fn
+ return nil
+ }
+}
+
+// PublicKeyAuth returns a functional option that sets PublicKeyHandler on the server.
+func PublicKeyAuth(fn PublicKeyHandler) Option {
+ return func(srv *Server) error {
+ srv.PublicKeyHandler = fn
+ return nil
+ }
+}
+
+// HostKeyFile returns a functional option that adds HostSigners to the server
+// from a PEM file at filepath.
+func HostKeyFile(filepath string) Option {
+ return func(srv *Server) error {
+ pemBytes, err := ioutil.ReadFile(filepath)
+ if err != nil {
+ return err
+ }
+
+ signer, err := gossh.ParsePrivateKey(pemBytes)
+ if err != nil {
+ return err
+ }
+
+ srv.AddHostKey(signer)
+
+ return nil
+ }
+}
+
+// HostKeyPEM returns a functional option that adds HostSigners to the server
+// from a PEM file as bytes.
+func HostKeyPEM(bytes []byte) Option {
+ return func(srv *Server) error {
+ signer, err := gossh.ParsePrivateKey(bytes)
+ if err != nil {
+ return err
+ }
+
+ srv.AddHostKey(signer)
+
+ return nil
+ }
+}
+
+// NoPty returns a functional option that sets PtyCallback to return false,
+// denying PTY requests.
+func NoPty() Option {
+ return func(srv *Server) error {
+ srv.PtyCallback = func(ctx Context, pty Pty) bool {
+ return false
+ }
+ return nil
+ }
+}
+
+// WrapConn returns a functional option that sets ConnCallback on the server.
+func WrapConn(fn ConnCallback) Option {
+ return func(srv *Server) error {
+ srv.ConnCallback = fn
+ return nil
+ }
+}
diff --git a/vendor/github.com/gliderlabs/ssh/server.go b/vendor/github.com/gliderlabs/ssh/server.go
new file mode 100644
index 0000000000..9ff09169a7
--- /dev/null
+++ b/vendor/github.com/gliderlabs/ssh/server.go
@@ -0,0 +1,394 @@
+package ssh
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "net"
+ "sync"
+ "time"
+
+ gossh "golang.org/x/crypto/ssh"
+)
+
+// ErrServerClosed is returned by the Server's Serve, ListenAndServe,
+// and ListenAndServeTLS methods after a call to Shutdown or Close.
+var ErrServerClosed = errors.New("ssh: Server closed")
+
+type RequestHandler func(ctx Context, srv *Server, req *gossh.Request) (ok bool, payload []byte)
+
+var DefaultRequestHandlers = map[string]RequestHandler{}
+
+type ChannelHandler func(srv *Server, conn *gossh.ServerConn, newChan gossh.NewChannel, ctx Context)
+
+var DefaultChannelHandlers = map[string]ChannelHandler{
+ "session": DefaultSessionHandler,
+}
+
+// Server defines parameters for running an SSH server. The zero value for
+// Server is a valid configuration. When both PasswordHandler and
+// PublicKeyHandler are nil, no client authentication is performed.
+type Server struct {
+ Addr string // TCP address to listen on, ":22" if empty
+ Handler Handler // handler to invoke, ssh.DefaultHandler if nil
+ HostSigners []Signer // private keys for the host key, must have at least one
+ Version string // server version to be sent before the initial handshake
+
+ KeyboardInteractiveHandler KeyboardInteractiveHandler // keyboard-interactive authentication handler
+ PasswordHandler PasswordHandler // password authentication handler
+ PublicKeyHandler PublicKeyHandler // public key authentication handler
+ PtyCallback PtyCallback // callback for allowing PTY sessions, allows all if nil
+ ConnCallback ConnCallback // optional callback for wrapping net.Conn before handling
+ LocalPortForwardingCallback LocalPortForwardingCallback // callback for allowing local port forwarding, denies all if nil
+ ReversePortForwardingCallback ReversePortForwardingCallback // callback for allowing reverse port forwarding, denies all if nil
+ ServerConfigCallback ServerConfigCallback // callback for configuring detailed SSH options
+ SessionRequestCallback SessionRequestCallback // callback for allowing or denying SSH sessions
+
+ IdleTimeout time.Duration // connection timeout when no activity, none if empty
+ MaxTimeout time.Duration // absolute connection timeout, none if empty
+
+ // ChannelHandlers allow overriding the built-in session handlers or provide
+ // extensions to the protocol, such as tcpip forwarding. By default only the
+ // "session" handler is enabled.
+ ChannelHandlers map[string]ChannelHandler
+
+ // RequestHandlers allow overriding the server-level request handlers or
+ // provide extensions to the protocol, such as tcpip forwarding. By default
+ // no handlers are enabled.
+ RequestHandlers map[string]RequestHandler
+
+ listenerWg sync.WaitGroup
+ mu sync.Mutex
+ listeners map[net.Listener]struct{}
+ conns map[*gossh.ServerConn]struct{}
+ connWg sync.WaitGroup
+ doneChan chan struct{}
+}
+
+func (srv *Server) ensureHostSigner() error {
+ if len(srv.HostSigners) == 0 {
+ signer, err := generateSigner()
+ if err != nil {
+ return err
+ }
+ srv.HostSigners = append(srv.HostSigners, signer)
+ }
+ return nil
+}
+
+func (srv *Server) ensureHandlers() {
+ srv.mu.Lock()
+ defer srv.mu.Unlock()
+ if srv.RequestHandlers == nil {
+ srv.RequestHandlers = map[string]RequestHandler{}
+ for k, v := range DefaultRequestHandlers {
+ srv.RequestHandlers[k] = v
+ }
+ }
+ if srv.ChannelHandlers == nil {
+ srv.ChannelHandlers = map[string]ChannelHandler{}
+ for k, v := range DefaultChannelHandlers {
+ srv.ChannelHandlers[k] = v
+ }
+ }
+}
+
+func (srv *Server) config(ctx Context) *gossh.ServerConfig {
+ var config *gossh.ServerConfig
+ if srv.ServerConfigCallback == nil {
+ config = &gossh.ServerConfig{}
+ } else {
+ config = srv.ServerConfigCallback(ctx)
+ }
+ for _, signer := range srv.HostSigners {
+ config.AddHostKey(signer)
+ }
+ if srv.PasswordHandler == nil && srv.PublicKeyHandler == nil {
+ config.NoClientAuth = true
+ }
+ if srv.Version != "" {
+ config.ServerVersion = "SSH-2.0-" + srv.Version
+ }
+ if srv.PasswordHandler != nil {
+ config.PasswordCallback = func(conn gossh.ConnMetadata, password []byte) (*gossh.Permissions, error) {
+ applyConnMetadata(ctx, conn)
+ if ok := srv.PasswordHandler(ctx, string(password)); !ok {
+ return ctx.Permissions().Permissions, fmt.Errorf("permission denied")
+ }
+ return ctx.Permissions().Permissions, nil
+ }
+ }
+ if srv.PublicKeyHandler != nil {
+ config.PublicKeyCallback = func(conn gossh.ConnMetadata, key gossh.PublicKey) (*gossh.Permissions, error) {
+ applyConnMetadata(ctx, conn)
+ if ok := srv.PublicKeyHandler(ctx, key); !ok {
+ return ctx.Permissions().Permissions, fmt.Errorf("permission denied")
+ }
+ ctx.SetValue(ContextKeyPublicKey, key)
+ return ctx.Permissions().Permissions, nil
+ }
+ }
+ if srv.KeyboardInteractiveHandler != nil {
+ config.KeyboardInteractiveCallback = func(conn gossh.ConnMetadata, challenger gossh.KeyboardInteractiveChallenge) (*gossh.Permissions, error) {
+ if ok := srv.KeyboardInteractiveHandler(ctx, challenger); !ok {
+ return ctx.Permissions().Permissions, fmt.Errorf("permission denied")
+ }
+ return ctx.Permissions().Permissions, nil
+ }
+ }
+ return config
+}
+
+// Handle sets the Handler for the server.
+func (srv *Server) Handle(fn Handler) {
+ srv.Handler = fn
+}
+
+// Close immediately closes all active listeners and all active
+// connections.
+//
+// Close returns any error returned from closing the Server's
+// underlying Listener(s).
+func (srv *Server) Close() error {
+ srv.mu.Lock()
+ defer srv.mu.Unlock()
+ srv.closeDoneChanLocked()
+ err := srv.closeListenersLocked()
+ for c := range srv.conns {
+ c.Close()
+ delete(srv.conns, c)
+ }
+ return err
+}
+
+// Shutdown gracefully shuts down the server without interrupting any
+// active connections. Shutdown works by first closing all open
+// listeners, and then waiting indefinitely for connections to close.
+// If the provided context expires before the shutdown is complete,
+// then the context's error is returned.
+func (srv *Server) Shutdown(ctx context.Context) error {
+ srv.mu.Lock()
+ lnerr := srv.closeListenersLocked()
+ srv.closeDoneChanLocked()
+ srv.mu.Unlock()
+
+ finished := make(chan struct{}, 1)
+ go func() {
+ srv.listenerWg.Wait()
+ srv.connWg.Wait()
+ finished <- struct{}{}
+ }()
+
+ select {
+ case <-ctx.Done():
+ return ctx.Err()
+ case <-finished:
+ return lnerr
+ }
+}
+
+// Serve accepts incoming connections on the Listener l, creating a new
+// connection goroutine for each. The connection goroutines read requests and then
+// calls srv.Handler to handle sessions.
+//
+// Serve always returns a non-nil error.
+func (srv *Server) Serve(l net.Listener) error {
+ srv.ensureHandlers()
+ defer l.Close()
+ if err := srv.ensureHostSigner(); err != nil {
+ return err
+ }
+ if srv.Handler == nil {
+ srv.Handler = DefaultHandler
+ }
+ var tempDelay time.Duration
+
+ srv.trackListener(l, true)
+ defer srv.trackListener(l, false)
+ for {
+ conn, e := l.Accept()
+ if e != nil {
+ select {
+ case <-srv.getDoneChan():
+ return ErrServerClosed
+ default:
+ }
+ if ne, ok := e.(net.Error); ok && ne.Temporary() {
+ if tempDelay == 0 {
+ tempDelay = 5 * time.Millisecond
+ } else {
+ tempDelay *= 2
+ }
+ if max := 1 * time.Second; tempDelay > max {
+ tempDelay = max
+ }
+ time.Sleep(tempDelay)
+ continue
+ }
+ return e
+ }
+ go srv.handleConn(conn)
+ }
+}
+
+func (srv *Server) handleConn(newConn net.Conn) {
+ if srv.ConnCallback != nil {
+ cbConn := srv.ConnCallback(newConn)
+ if cbConn == nil {
+ newConn.Close()
+ return
+ }
+ newConn = cbConn
+ }
+ ctx, cancel := newContext(srv)
+ conn := &serverConn{
+ Conn: newConn,
+ idleTimeout: srv.IdleTimeout,
+ closeCanceler: cancel,
+ }
+ if srv.MaxTimeout > 0 {
+ conn.maxDeadline = time.Now().Add(srv.MaxTimeout)
+ }
+ defer conn.Close()
+ sshConn, chans, reqs, err := gossh.NewServerConn(conn, srv.config(ctx))
+ if err != nil {
+ // TODO: trigger event callback
+ return
+ }
+
+ srv.trackConn(sshConn, true)
+ defer srv.trackConn(sshConn, false)
+
+ ctx.SetValue(ContextKeyConn, sshConn)
+ applyConnMetadata(ctx, sshConn)
+ //go gossh.DiscardRequests(reqs)
+ go srv.handleRequests(ctx, reqs)
+ for ch := range chans {
+ handler := srv.ChannelHandlers[ch.ChannelType()]
+ if handler == nil {
+ handler = srv.ChannelHandlers["default"]
+ }
+ if handler == nil {
+ ch.Reject(gossh.UnknownChannelType, "unsupported channel type")
+ continue
+ }
+ go handler(srv, sshConn, ch, ctx)
+ }
+}
+
+func (srv *Server) handleRequests(ctx Context, in <-chan *gossh.Request) {
+ for req := range in {
+ handler := srv.RequestHandlers[req.Type]
+ if handler == nil {
+ handler = srv.RequestHandlers["default"]
+ }
+ if handler == nil {
+ req.Reply(false, nil)
+ continue
+ }
+ /*reqCtx, cancel := context.WithCancel(ctx)
+ defer cancel() */
+ ret, payload := handler(ctx, srv, req)
+ req.Reply(ret, payload)
+ }
+}
+
+// ListenAndServe listens on the TCP network address srv.Addr and then calls
+// Serve to handle incoming connections. If srv.Addr is blank, ":22" is used.
+// ListenAndServe always returns a non-nil error.
+func (srv *Server) ListenAndServe() error {
+ addr := srv.Addr
+ if addr == "" {
+ addr = ":22"
+ }
+ ln, err := net.Listen("tcp", addr)
+ if err != nil {
+ return err
+ }
+ return srv.Serve(ln)
+}
+
+// AddHostKey adds a private key as a host key. If an existing host key exists
+// with the same algorithm, it is overwritten. Each server config must have at
+// least one host key.
+func (srv *Server) AddHostKey(key Signer) {
+ // these are later added via AddHostKey on ServerConfig, which performs the
+ // check for one of every algorithm.
+ srv.HostSigners = append(srv.HostSigners, key)
+}
+
+// SetOption runs a functional option against the server.
+func (srv *Server) SetOption(option Option) error {
+ return option(srv)
+}
+
+func (srv *Server) getDoneChan() <-chan struct{} {
+ srv.mu.Lock()
+ defer srv.mu.Unlock()
+ return srv.getDoneChanLocked()
+}
+
+func (srv *Server) getDoneChanLocked() chan struct{} {
+ if srv.doneChan == nil {
+ srv.doneChan = make(chan struct{})
+ }
+ return srv.doneChan
+}
+
+func (srv *Server) closeDoneChanLocked() {
+ ch := srv.getDoneChanLocked()
+ select {
+ case <-ch:
+ // Already closed. Don't close again.
+ default:
+ // Safe to close here. We're the only closer, guarded
+ // by srv.mu.
+ close(ch)
+ }
+}
+
+func (srv *Server) closeListenersLocked() error {
+ var err error
+ for ln := range srv.listeners {
+ if cerr := ln.Close(); cerr != nil && err == nil {
+ err = cerr
+ }
+ delete(srv.listeners, ln)
+ }
+ return err
+}
+
+func (srv *Server) trackListener(ln net.Listener, add bool) {
+ srv.mu.Lock()
+ defer srv.mu.Unlock()
+ if srv.listeners == nil {
+ srv.listeners = make(map[net.Listener]struct{})
+ }
+ if add {
+ // If the *Server is being reused after a previous
+ // Close or Shutdown, reset its doneChan:
+ if len(srv.listeners) == 0 && len(srv.conns) == 0 {
+ srv.doneChan = nil
+ }
+ srv.listeners[ln] = struct{}{}
+ srv.listenerWg.Add(1)
+ } else {
+ delete(srv.listeners, ln)
+ srv.listenerWg.Done()
+ }
+}
+
+func (srv *Server) trackConn(c *gossh.ServerConn, add bool) {
+ srv.mu.Lock()
+ defer srv.mu.Unlock()
+ if srv.conns == nil {
+ srv.conns = make(map[*gossh.ServerConn]struct{})
+ }
+ if add {
+ srv.conns[c] = struct{}{}
+ srv.connWg.Add(1)
+ } else {
+ delete(srv.conns, c)
+ srv.connWg.Done()
+ }
+}
diff --git a/vendor/github.com/gliderlabs/ssh/session.go b/vendor/github.com/gliderlabs/ssh/session.go
new file mode 100644
index 0000000000..6c77d6c8a7
--- /dev/null
+++ b/vendor/github.com/gliderlabs/ssh/session.go
@@ -0,0 +1,308 @@
+package ssh
+
+import (
+ "bytes"
+ "context"
+ "errors"
+ "fmt"
+ "net"
+ "sync"
+
+ "github.com/anmitsu/go-shlex"
+ gossh "golang.org/x/crypto/ssh"
+)
+
+// Session provides access to information about an SSH session and methods
+// to read and write to the SSH channel with an embedded Channel interface from
+// cypto/ssh.
+//
+// When Command() returns an empty slice, the user requested a shell. Otherwise
+// the user is performing an exec with those command arguments.
+//
+// TODO: Signals
+type Session interface {
+ gossh.Channel
+
+ // User returns the username used when establishing the SSH connection.
+ User() string
+
+ // RemoteAddr returns the net.Addr of the client side of the connection.
+ RemoteAddr() net.Addr
+
+ // LocalAddr returns the net.Addr of the server side of the connection.
+ LocalAddr() net.Addr
+
+ // Environ returns a copy of strings representing the environment set by the
+ // user for this session, in the form "key=value".
+ Environ() []string
+
+ // Exit sends an exit status and then closes the session.
+ Exit(code int) error
+
+ // Command returns a shell parsed slice of arguments that were provided by the
+ // user. Shell parsing splits the command string according to POSIX shell rules,
+ // which considers quoting not just whitespace.
+ Command() []string
+
+ // RawCommand returns the exact command that was provided by the user.
+ RawCommand() string
+
+ // PublicKey returns the PublicKey used to authenticate. If a public key was not
+ // used it will return nil.
+ PublicKey() PublicKey
+
+ // Context returns the connection's context. The returned context is always
+ // non-nil and holds the same data as the Context passed into auth
+ // handlers and callbacks.
+ //
+ // The context is canceled when the client's connection closes or I/O
+ // operation fails.
+ Context() context.Context
+
+ // Permissions returns a copy of the Permissions object that was available for
+ // setup in the auth handlers via the Context.
+ Permissions() Permissions
+
+ // Pty returns PTY information, a channel of window size changes, and a boolean
+ // of whether or not a PTY was accepted for this session.
+ Pty() (Pty, <-chan Window, bool)
+
+ // Signals registers a channel to receive signals sent from the client. The
+ // channel must handle signal sends or it will block the SSH request loop.
+ // Registering nil will unregister the channel from signal sends. During the
+ // time no channel is registered signals are buffered up to a reasonable amount.
+ // If there are buffered signals when a channel is registered, they will be
+ // sent in order on the channel immediately after registering.
+ Signals(c chan<- Signal)
+}
+
+// maxSigBufSize is how many signals will be buffered
+// when there is no signal channel specified
+const maxSigBufSize = 128
+
+func DefaultSessionHandler(srv *Server, conn *gossh.ServerConn, newChan gossh.NewChannel, ctx Context) {
+ ch, reqs, err := newChan.Accept()
+ if err != nil {
+ // TODO: trigger event callback
+ return
+ }
+ sess := &session{
+ Channel: ch,
+ conn: conn,
+ handler: srv.Handler,
+ ptyCb: srv.PtyCallback,
+ sessReqCb: srv.SessionRequestCallback,
+ ctx: ctx,
+ }
+ sess.handleRequests(reqs)
+}
+
+type session struct {
+ sync.Mutex
+ gossh.Channel
+ conn *gossh.ServerConn
+ handler Handler
+ handled bool
+ exited bool
+ pty *Pty
+ winch chan Window
+ env []string
+ ptyCb PtyCallback
+ sessReqCb SessionRequestCallback
+ rawCmd string
+ ctx Context
+ sigCh chan<- Signal
+ sigBuf []Signal
+}
+
+func (sess *session) Write(p []byte) (n int, err error) {
+ if sess.pty != nil {
+ m := len(p)
+ // normalize \n to \r\n when pty is accepted.
+ // this is a hardcoded shortcut since we don't support terminal modes.
+ p = bytes.Replace(p, []byte{'\n'}, []byte{'\r', '\n'}, -1)
+ p = bytes.Replace(p, []byte{'\r', '\r', '\n'}, []byte{'\r', '\n'}, -1)
+ n, err = sess.Channel.Write(p)
+ if n > m {
+ n = m
+ }
+ return
+ }
+ return sess.Channel.Write(p)
+}
+
+func (sess *session) PublicKey() PublicKey {
+ sessionkey := sess.ctx.Value(ContextKeyPublicKey)
+ if sessionkey == nil {
+ return nil
+ }
+ return sessionkey.(PublicKey)
+}
+
+func (sess *session) Permissions() Permissions {
+ // use context permissions because its properly
+ // wrapped and easier to dereference
+ perms := sess.ctx.Value(ContextKeyPermissions).(*Permissions)
+ return *perms
+}
+
+func (sess *session) Context() context.Context {
+ return sess.ctx
+}
+
+func (sess *session) Exit(code int) error {
+ sess.Lock()
+ defer sess.Unlock()
+ if sess.exited {
+ return errors.New("Session.Exit called multiple times")
+ }
+ sess.exited = true
+
+ status := struct{ Status uint32 }{uint32(code)}
+ _, err := sess.SendRequest("exit-status", false, gossh.Marshal(&status))
+ if err != nil {
+ return err
+ }
+ return sess.Close()
+}
+
+func (sess *session) User() string {
+ return sess.conn.User()
+}
+
+func (sess *session) RemoteAddr() net.Addr {
+ return sess.conn.RemoteAddr()
+}
+
+func (sess *session) LocalAddr() net.Addr {
+ return sess.conn.LocalAddr()
+}
+
+func (sess *session) Environ() []string {
+ return append([]string(nil), sess.env...)
+}
+
+func (sess *session) RawCommand() string {
+ return sess.rawCmd
+}
+
+func (sess *session) Command() []string {
+ cmd, _ := shlex.Split(sess.rawCmd, true)
+ return append([]string(nil), cmd...)
+}
+
+func (sess *session) Pty() (Pty, <-chan Window, bool) {
+ if sess.pty != nil {
+ return *sess.pty, sess.winch, true
+ }
+ return Pty{}, sess.winch, false
+}
+
+func (sess *session) Signals(c chan<- Signal) {
+ sess.Lock()
+ defer sess.Unlock()
+ sess.sigCh = c
+ if len(sess.sigBuf) > 0 {
+ go func() {
+ for _, sig := range sess.sigBuf {
+ sess.sigCh <- sig
+ }
+ }()
+ }
+}
+
+func (sess *session) handleRequests(reqs <-chan *gossh.Request) {
+ for req := range reqs {
+ switch req.Type {
+ case "shell", "exec":
+ if sess.handled {
+ req.Reply(false, nil)
+ continue
+ }
+
+ var payload = struct{ Value string }{}
+ gossh.Unmarshal(req.Payload, &payload)
+ sess.rawCmd = payload.Value
+
+ // If there's a session policy callback, we need to confirm before
+ // accepting the session.
+ if sess.sessReqCb != nil && !sess.sessReqCb(sess, req.Type) {
+ sess.rawCmd = ""
+ req.Reply(false, nil)
+ continue
+ }
+
+ sess.handled = true
+ req.Reply(true, nil)
+
+ go func() {
+ sess.handler(sess)
+ sess.Exit(0)
+ }()
+ case "env":
+ if sess.handled {
+ req.Reply(false, nil)
+ continue
+ }
+ var kv struct{ Key, Value string }
+ gossh.Unmarshal(req.Payload, &kv)
+ sess.env = append(sess.env, fmt.Sprintf("%s=%s", kv.Key, kv.Value))
+ req.Reply(true, nil)
+ case "signal":
+ var payload struct{ Signal string }
+ gossh.Unmarshal(req.Payload, &payload)
+ sess.Lock()
+ if sess.sigCh != nil {
+ sess.sigCh <- Signal(payload.Signal)
+ } else {
+ if len(sess.sigBuf) < maxSigBufSize {
+ sess.sigBuf = append(sess.sigBuf, Signal(payload.Signal))
+ }
+ }
+ sess.Unlock()
+ case "pty-req":
+ if sess.handled || sess.pty != nil {
+ req.Reply(false, nil)
+ continue
+ }
+ ptyReq, ok := parsePtyRequest(req.Payload)
+ if !ok {
+ req.Reply(false, nil)
+ continue
+ }
+ if sess.ptyCb != nil {
+ ok := sess.ptyCb(sess.ctx, ptyReq)
+ if !ok {
+ req.Reply(false, nil)
+ continue
+ }
+ }
+ sess.pty = &ptyReq
+ sess.winch = make(chan Window, 1)
+ sess.winch <- ptyReq.Window
+ defer func() {
+ // when reqs is closed
+ close(sess.winch)
+ }()
+ req.Reply(ok, nil)
+ case "window-change":
+ if sess.pty == nil {
+ req.Reply(false, nil)
+ continue
+ }
+ win, ok := parseWinchRequest(req.Payload)
+ if ok {
+ sess.pty.Window = win
+ sess.winch <- win
+ }
+ req.Reply(ok, nil)
+ case agentRequestType:
+ // TODO: option/callback to allow agent forwarding
+ SetAgentRequested(sess.ctx)
+ req.Reply(true, nil)
+ default:
+ // TODO: debug log
+ req.Reply(false, nil)
+ }
+ }
+}
diff --git a/vendor/github.com/gliderlabs/ssh/ssh.go b/vendor/github.com/gliderlabs/ssh/ssh.go
new file mode 100644
index 0000000000..f5a935a984
--- /dev/null
+++ b/vendor/github.com/gliderlabs/ssh/ssh.go
@@ -0,0 +1,123 @@
+package ssh
+
+import (
+ "crypto/subtle"
+ "net"
+
+ gossh "golang.org/x/crypto/ssh"
+)
+
+type Signal string
+
+// POSIX signals as listed in RFC 4254 Section 6.10.
+const (
+ SIGABRT Signal = "ABRT"
+ SIGALRM Signal = "ALRM"
+ SIGFPE Signal = "FPE"
+ SIGHUP Signal = "HUP"
+ SIGILL Signal = "ILL"
+ SIGINT Signal = "INT"
+ SIGKILL Signal = "KILL"
+ SIGPIPE Signal = "PIPE"
+ SIGQUIT Signal = "QUIT"
+ SIGSEGV Signal = "SEGV"
+ SIGTERM Signal = "TERM"
+ SIGUSR1 Signal = "USR1"
+ SIGUSR2 Signal = "USR2"
+)
+
+// DefaultHandler is the default Handler used by Serve.
+var DefaultHandler Handler
+
+// Option is a functional option handler for Server.
+type Option func(*Server) error
+
+// Handler is a callback for handling established SSH sessions.
+type Handler func(Session)
+
+// PublicKeyHandler is a callback for performing public key authentication.
+type PublicKeyHandler func(ctx Context, key PublicKey) bool
+
+// PasswordHandler is a callback for performing password authentication.
+type PasswordHandler func(ctx Context, password string) bool
+
+// KeyboardInteractiveHandler is a callback for performing keyboard-interactive authentication.
+type KeyboardInteractiveHandler func(ctx Context, challenger gossh.KeyboardInteractiveChallenge) bool
+
+// PtyCallback is a hook for allowing PTY sessions.
+type PtyCallback func(ctx Context, pty Pty) bool
+
+// SessionRequestCallback is a callback for allowing or denying SSH sessions.
+type SessionRequestCallback func(sess Session, requestType string) bool
+
+// ConnCallback is a hook for new connections before handling.
+// It allows wrapping for timeouts and limiting by returning
+// the net.Conn that will be used as the underlying connection.
+type ConnCallback func(conn net.Conn) net.Conn
+
+// LocalPortForwardingCallback is a hook for allowing port forwarding
+type LocalPortForwardingCallback func(ctx Context, destinationHost string, destinationPort uint32) bool
+
+// ReversePortForwardingCallback is a hook for allowing reverse port forwarding
+type ReversePortForwardingCallback func(ctx Context, bindHost string, bindPort uint32) bool
+
+// ServerConfigCallback is a hook for creating custom default server configs
+type ServerConfigCallback func(ctx Context) *gossh.ServerConfig
+
+// Window represents the size of a PTY window.
+type Window struct {
+ Width int
+ Height int
+}
+
+// Pty represents a PTY request and configuration.
+type Pty struct {
+ Term string
+ Window Window
+ // HELP WANTED: terminal modes!
+}
+
+// Serve accepts incoming SSH connections on the listener l, creating a new
+// connection goroutine for each. The connection goroutines read requests and
+// then calls handler to handle sessions. Handler is typically nil, in which
+// case the DefaultHandler is used.
+func Serve(l net.Listener, handler Handler, options ...Option) error {
+ srv := &Server{Handler: handler}
+ for _, option := range options {
+ if err := srv.SetOption(option); err != nil {
+ return err
+ }
+ }
+ return srv.Serve(l)
+}
+
+// ListenAndServe listens on the TCP network address addr and then calls Serve
+// with handler to handle sessions on incoming connections. Handler is typically
+// nil, in which case the DefaultHandler is used.
+func ListenAndServe(addr string, handler Handler, options ...Option) error {
+ srv := &Server{Addr: addr, Handler: handler}
+ for _, option := range options {
+ if err := srv.SetOption(option); err != nil {
+ return err
+ }
+ }
+ return srv.ListenAndServe()
+}
+
+// Handle registers the handler as the DefaultHandler.
+func Handle(handler Handler) {
+ DefaultHandler = handler
+}
+
+// KeysEqual is constant time compare of the keys to avoid timing attacks.
+func KeysEqual(ak, bk PublicKey) bool {
+
+ //avoid panic if one of the keys is nil, return false instead
+ if ak == nil || bk == nil {
+ return false
+ }
+
+ a := ak.Marshal()
+ b := bk.Marshal()
+ return (len(a) == len(b) && subtle.ConstantTimeCompare(a, b) == 1)
+}
diff --git a/vendor/github.com/gliderlabs/ssh/tcpip.go b/vendor/github.com/gliderlabs/ssh/tcpip.go
new file mode 100644
index 0000000000..335fda6575
--- /dev/null
+++ b/vendor/github.com/gliderlabs/ssh/tcpip.go
@@ -0,0 +1,193 @@
+package ssh
+
+import (
+ "io"
+ "log"
+ "net"
+ "strconv"
+ "sync"
+
+ gossh "golang.org/x/crypto/ssh"
+)
+
+const (
+ forwardedTCPChannelType = "forwarded-tcpip"
+)
+
+// direct-tcpip data struct as specified in RFC4254, Section 7.2
+type localForwardChannelData struct {
+ DestAddr string
+ DestPort uint32
+
+ OriginAddr string
+ OriginPort uint32
+}
+
+// DirectTCPIPHandler can be enabled by adding it to the server's
+// ChannelHandlers under direct-tcpip.
+func DirectTCPIPHandler(srv *Server, conn *gossh.ServerConn, newChan gossh.NewChannel, ctx Context) {
+ d := localForwardChannelData{}
+ if err := gossh.Unmarshal(newChan.ExtraData(), &d); err != nil {
+ newChan.Reject(gossh.ConnectionFailed, "error parsing forward data: "+err.Error())
+ return
+ }
+
+ if srv.LocalPortForwardingCallback == nil || !srv.LocalPortForwardingCallback(ctx, d.DestAddr, d.DestPort) {
+ newChan.Reject(gossh.Prohibited, "port forwarding is disabled")
+ return
+ }
+
+ dest := net.JoinHostPort(d.DestAddr, strconv.FormatInt(int64(d.DestPort), 10))
+
+ var dialer net.Dialer
+ dconn, err := dialer.DialContext(ctx, "tcp", dest)
+ if err != nil {
+ newChan.Reject(gossh.ConnectionFailed, err.Error())
+ return
+ }
+
+ ch, reqs, err := newChan.Accept()
+ if err != nil {
+ dconn.Close()
+ return
+ }
+ go gossh.DiscardRequests(reqs)
+
+ go func() {
+ defer ch.Close()
+ defer dconn.Close()
+ io.Copy(ch, dconn)
+ }()
+ go func() {
+ defer ch.Close()
+ defer dconn.Close()
+ io.Copy(dconn, ch)
+ }()
+}
+
+type remoteForwardRequest struct {
+ BindAddr string
+ BindPort uint32
+}
+
+type remoteForwardSuccess struct {
+ BindPort uint32
+}
+
+type remoteForwardCancelRequest struct {
+ BindAddr string
+ BindPort uint32
+}
+
+type remoteForwardChannelData struct {
+ DestAddr string
+ DestPort uint32
+ OriginAddr string
+ OriginPort uint32
+}
+
+// ForwardedTCPHandler can be enabled by creating a ForwardedTCPHandler and
+// adding the HandleSSHRequest callback to the server's RequestHandlers under
+// tcpip-forward and cancel-tcpip-forward.
+type ForwardedTCPHandler struct {
+ forwards map[string]net.Listener
+ sync.Mutex
+}
+
+func (h *ForwardedTCPHandler) HandleSSHRequest(ctx Context, srv *Server, req *gossh.Request) (bool, []byte) {
+ h.Lock()
+ if h.forwards == nil {
+ h.forwards = make(map[string]net.Listener)
+ }
+ h.Unlock()
+ conn := ctx.Value(ContextKeyConn).(*gossh.ServerConn)
+ switch req.Type {
+ case "tcpip-forward":
+ var reqPayload remoteForwardRequest
+ if err := gossh.Unmarshal(req.Payload, &reqPayload); err != nil {
+ // TODO: log parse failure
+ return false, []byte{}
+ }
+ if srv.ReversePortForwardingCallback == nil || !srv.ReversePortForwardingCallback(ctx, reqPayload.BindAddr, reqPayload.BindPort) {
+ return false, []byte("port forwarding is disabled")
+ }
+ addr := net.JoinHostPort(reqPayload.BindAddr, strconv.Itoa(int(reqPayload.BindPort)))
+ ln, err := net.Listen("tcp", addr)
+ if err != nil {
+ // TODO: log listen failure
+ return false, []byte{}
+ }
+ _, destPortStr, _ := net.SplitHostPort(ln.Addr().String())
+ destPort, _ := strconv.Atoi(destPortStr)
+ h.Lock()
+ h.forwards[addr] = ln
+ h.Unlock()
+ go func() {
+ <-ctx.Done()
+ h.Lock()
+ ln, ok := h.forwards[addr]
+ h.Unlock()
+ if ok {
+ ln.Close()
+ }
+ }()
+ go func() {
+ for {
+ c, err := ln.Accept()
+ if err != nil {
+ // TODO: log accept failure
+ break
+ }
+ originAddr, orignPortStr, _ := net.SplitHostPort(c.RemoteAddr().String())
+ originPort, _ := strconv.Atoi(orignPortStr)
+ payload := gossh.Marshal(&remoteForwardChannelData{
+ DestAddr: reqPayload.BindAddr,
+ DestPort: uint32(destPort),
+ OriginAddr: originAddr,
+ OriginPort: uint32(originPort),
+ })
+ go func() {
+ ch, reqs, err := conn.OpenChannel(forwardedTCPChannelType, payload)
+ if err != nil {
+ // TODO: log failure to open channel
+ log.Println(err)
+ c.Close()
+ return
+ }
+ go gossh.DiscardRequests(reqs)
+ go func() {
+ defer ch.Close()
+ defer c.Close()
+ io.Copy(ch, c)
+ }()
+ go func() {
+ defer ch.Close()
+ defer c.Close()
+ io.Copy(c, ch)
+ }()
+ }()
+ }
+ h.Lock()
+ delete(h.forwards, addr)
+ h.Unlock()
+ }()
+ return true, gossh.Marshal(&remoteForwardSuccess{uint32(destPort)})
+
+ case "cancel-tcpip-forward":
+ var reqPayload remoteForwardCancelRequest
+ if err := gossh.Unmarshal(req.Payload, &reqPayload); err != nil {
+ // TODO: log parse failure
+ return false, []byte{}
+ }
+ addr := net.JoinHostPort(reqPayload.BindAddr, strconv.Itoa(int(reqPayload.BindPort)))
+ h.Lock()
+ ln, ok := h.forwards[addr]
+ h.Unlock()
+ if ok {
+ ln.Close()
+ }
+ return true, nil
+ default:
+ return false, nil
+ }
+}
diff --git a/vendor/github.com/gliderlabs/ssh/util.go b/vendor/github.com/gliderlabs/ssh/util.go
new file mode 100644
index 0000000000..015a44ecc0
--- /dev/null
+++ b/vendor/github.com/gliderlabs/ssh/util.go
@@ -0,0 +1,83 @@
+package ssh
+
+import (
+ "crypto/rand"
+ "crypto/rsa"
+ "encoding/binary"
+
+ "golang.org/x/crypto/ssh"
+)
+
+func generateSigner() (ssh.Signer, error) {
+ key, err := rsa.GenerateKey(rand.Reader, 2048)
+ if err != nil {
+ return nil, err
+ }
+ return ssh.NewSignerFromKey(key)
+}
+
+func parsePtyRequest(s []byte) (pty Pty, ok bool) {
+ term, s, ok := parseString(s)
+ if !ok {
+ return
+ }
+ width32, s, ok := parseUint32(s)
+ if !ok {
+ return
+ }
+ height32, _, ok := parseUint32(s)
+ if !ok {
+ return
+ }
+ pty = Pty{
+ Term: term,
+ Window: Window{
+ Width: int(width32),
+ Height: int(height32),
+ },
+ }
+ return
+}
+
+func parseWinchRequest(s []byte) (win Window, ok bool) {
+ width32, s, ok := parseUint32(s)
+ if width32 < 1 {
+ ok = false
+ }
+ if !ok {
+ return
+ }
+ height32, _, ok := parseUint32(s)
+ if height32 < 1 {
+ ok = false
+ }
+ if !ok {
+ return
+ }
+ win = Window{
+ Width: int(width32),
+ Height: int(height32),
+ }
+ return
+}
+
+func parseString(in []byte) (out string, rest []byte, ok bool) {
+ if len(in) < 4 {
+ return
+ }
+ length := binary.BigEndian.Uint32(in)
+ if uint32(len(in)) < 4+length {
+ return
+ }
+ out = string(in[4 : 4+length])
+ rest = in[4+length:]
+ ok = true
+ return
+}
+
+func parseUint32(in []byte) (uint32, []byte, bool) {
+ if len(in) < 4 {
+ return 0, nil, false
+ }
+ return binary.BigEndian.Uint32(in), in[4:], true
+}
diff --git a/vendor/github.com/gliderlabs/ssh/wrap.go b/vendor/github.com/gliderlabs/ssh/wrap.go
new file mode 100644
index 0000000000..d1f2b161e6
--- /dev/null
+++ b/vendor/github.com/gliderlabs/ssh/wrap.go
@@ -0,0 +1,33 @@
+package ssh
+
+import gossh "golang.org/x/crypto/ssh"
+
+// PublicKey is an abstraction of different types of public keys.
+type PublicKey interface {
+ gossh.PublicKey
+}
+
+// The Permissions type holds fine-grained permissions that are specific to a
+// user or a specific authentication method for a user. Permissions, except for
+// "source-address", must be enforced in the server application layer, after
+// successful authentication.
+type Permissions struct {
+ *gossh.Permissions
+}
+
+// A Signer can create signatures that verify against a public key.
+type Signer interface {
+ gossh.Signer
+}
+
+// ParseAuthorizedKey parses a public key from an authorized_keys file used in
+// OpenSSH according to the sshd(8) manual page.
+func ParseAuthorizedKey(in []byte) (out PublicKey, comment string, options []string, rest []byte, err error) {
+ return gossh.ParseAuthorizedKey(in)
+}
+
+// ParsePublicKey parses an SSH public key formatted for use in
+// the SSH wire protocol according to RFC 4253, section 6.6.
+func ParsePublicKey(in []byte) (out PublicKey, err error) {
+ return gossh.ParsePublicKey(in)
+}
diff --git a/vendor/golang.org/x/sys/unix/syscall_linux.go b/vendor/golang.org/x/sys/unix/syscall_linux.go
index c92545ea51..f5c1b8800e 100644
--- a/vendor/golang.org/x/sys/unix/syscall_linux.go
+++ b/vendor/golang.org/x/sys/unix/syscall_linux.go
@@ -13,7 +13,6 @@ package unix
import (
"encoding/binary"
- "net"
"runtime"
"syscall"
"unsafe"
@@ -765,7 +764,7 @@ const px_proto_oe = 0
type SockaddrPPPoE struct {
SID uint16
- Remote net.HardwareAddr
+ Remote []byte
Dev string
raw RawSockaddrPPPoX
}
@@ -916,7 +915,7 @@ func anyToSockaddr(fd int, rsa *RawSockaddrAny) (Sockaddr, error) {
}
sa := &SockaddrPPPoE{
SID: binary.BigEndian.Uint16(pp[6:8]),
- Remote: net.HardwareAddr(pp[8:14]),
+ Remote: pp[8:14],
}
for i := 14; i < 14+IFNAMSIZ; i++ {
if pp[i] == 0 {
diff --git a/vendor/golang.org/x/sys/unix/types_netbsd.go b/vendor/golang.org/x/sys/unix/types_netbsd.go
index 2dd4f9542c..4a96d72c37 100644
--- a/vendor/golang.org/x/sys/unix/types_netbsd.go
+++ b/vendor/golang.org/x/sys/unix/types_netbsd.go
@@ -254,6 +254,7 @@ type Ptmget C.struct_ptmget
const (
AT_FDCWD = C.AT_FDCWD
+ AT_SYMLINK_FOLLOW = C.AT_SYMLINK_FOLLOW
AT_SYMLINK_NOFOLLOW = C.AT_SYMLINK_NOFOLLOW
)
diff --git a/vendor/golang.org/x/sys/unix/types_openbsd.go b/vendor/golang.org/x/sys/unix/types_openbsd.go
index 8aafbe4469..775cb57dc8 100644
--- a/vendor/golang.org/x/sys/unix/types_openbsd.go
+++ b/vendor/golang.org/x/sys/unix/types_openbsd.go
@@ -241,6 +241,7 @@ type Winsize C.struct_winsize
const (
AT_FDCWD = C.AT_FDCWD
+ AT_SYMLINK_FOLLOW = C.AT_SYMLINK_FOLLOW
AT_SYMLINK_NOFOLLOW = C.AT_SYMLINK_NOFOLLOW
)
diff --git a/vendor/golang.org/x/sys/unix/ztypes_netbsd_386.go b/vendor/golang.org/x/sys/unix/ztypes_netbsd_386.go
index a2268b4f62..86736ab6e7 100644
--- a/vendor/golang.org/x/sys/unix/ztypes_netbsd_386.go
+++ b/vendor/golang.org/x/sys/unix/ztypes_netbsd_386.go
@@ -411,6 +411,7 @@ type Ptmget struct {
const (
AT_FDCWD = -0x64
+ AT_SYMLINK_FOLLOW = 0x400
AT_SYMLINK_NOFOLLOW = 0x200
)
diff --git a/vendor/golang.org/x/sys/unix/ztypes_netbsd_amd64.go b/vendor/golang.org/x/sys/unix/ztypes_netbsd_amd64.go
index 59e1da0a6a..3427811f98 100644
--- a/vendor/golang.org/x/sys/unix/ztypes_netbsd_amd64.go
+++ b/vendor/golang.org/x/sys/unix/ztypes_netbsd_amd64.go
@@ -418,6 +418,7 @@ type Ptmget struct {
const (
AT_FDCWD = -0x64
+ AT_SYMLINK_FOLLOW = 0x400
AT_SYMLINK_NOFOLLOW = 0x200
)
diff --git a/vendor/golang.org/x/sys/unix/ztypes_netbsd_arm.go b/vendor/golang.org/x/sys/unix/ztypes_netbsd_arm.go
index 1f1f0f3819..399f37a434 100644
--- a/vendor/golang.org/x/sys/unix/ztypes_netbsd_arm.go
+++ b/vendor/golang.org/x/sys/unix/ztypes_netbsd_arm.go
@@ -416,6 +416,7 @@ type Ptmget struct {
const (
AT_FDCWD = -0x64
+ AT_SYMLINK_FOLLOW = 0x400
AT_SYMLINK_NOFOLLOW = 0x200
)
diff --git a/vendor/golang.org/x/sys/unix/ztypes_netbsd_arm64.go b/vendor/golang.org/x/sys/unix/ztypes_netbsd_arm64.go
index 8dca204a9c..32f0c15d98 100644
--- a/vendor/golang.org/x/sys/unix/ztypes_netbsd_arm64.go
+++ b/vendor/golang.org/x/sys/unix/ztypes_netbsd_arm64.go
@@ -418,6 +418,7 @@ type Ptmget struct {
const (
AT_FDCWD = -0x64
+ AT_SYMLINK_FOLLOW = 0x400
AT_SYMLINK_NOFOLLOW = 0x200
)
diff --git a/vendor/golang.org/x/sys/unix/ztypes_openbsd_386.go b/vendor/golang.org/x/sys/unix/ztypes_openbsd_386.go
index 900fb44622..61ea0019a2 100644
--- a/vendor/golang.org/x/sys/unix/ztypes_openbsd_386.go
+++ b/vendor/golang.org/x/sys/unix/ztypes_openbsd_386.go
@@ -436,6 +436,7 @@ type Winsize struct {
const (
AT_FDCWD = -0x64
+ AT_SYMLINK_FOLLOW = 0x4
AT_SYMLINK_NOFOLLOW = 0x2
)
diff --git a/vendor/golang.org/x/sys/unix/ztypes_openbsd_amd64.go b/vendor/golang.org/x/sys/unix/ztypes_openbsd_amd64.go
index 028fa78d74..87a493f68f 100644
--- a/vendor/golang.org/x/sys/unix/ztypes_openbsd_amd64.go
+++ b/vendor/golang.org/x/sys/unix/ztypes_openbsd_amd64.go
@@ -436,6 +436,7 @@ type Winsize struct {
const (
AT_FDCWD = -0x64
+ AT_SYMLINK_FOLLOW = 0x4
AT_SYMLINK_NOFOLLOW = 0x2
)
diff --git a/vendor/golang.org/x/sys/unix/ztypes_openbsd_arm.go b/vendor/golang.org/x/sys/unix/ztypes_openbsd_arm.go
index b45d5eedff..d80836efab 100644
--- a/vendor/golang.org/x/sys/unix/ztypes_openbsd_arm.go
+++ b/vendor/golang.org/x/sys/unix/ztypes_openbsd_arm.go
@@ -437,6 +437,7 @@ type Winsize struct {
const (
AT_FDCWD = -0x64
+ AT_SYMLINK_FOLLOW = 0x4
AT_SYMLINK_NOFOLLOW = 0x2
)
diff --git a/vendor/golang.org/x/sys/unix/ztypes_openbsd_arm64.go b/vendor/golang.org/x/sys/unix/ztypes_openbsd_arm64.go
index fa369a32a2..4e158746f1 100644
--- a/vendor/golang.org/x/sys/unix/ztypes_openbsd_arm64.go
+++ b/vendor/golang.org/x/sys/unix/ztypes_openbsd_arm64.go
@@ -430,6 +430,7 @@ type Winsize struct {
const (
AT_FDCWD = -0x64
+ AT_SYMLINK_FOLLOW = 0x4
AT_SYMLINK_NOFOLLOW = 0x2
)
diff --git a/vendor/golang.org/x/sys/windows/syscall_windows.go b/vendor/golang.org/x/sys/windows/syscall_windows.go
index be8c50f253..b23050924f 100644
--- a/vendor/golang.org/x/sys/windows/syscall_windows.go
+++ b/vendor/golang.org/x/sys/windows/syscall_windows.go
@@ -294,7 +294,7 @@ func NewCallbackCDecl(fn interface{}) uintptr {
//sys clsidFromString(lpsz *uint16, pclsid *GUID) (ret error) = ole32.CLSIDFromString
//sys stringFromGUID2(rguid *GUID, lpsz *uint16, cchMax int32) (chars int32) = ole32.StringFromGUID2
//sys coCreateGuid(pguid *GUID) (ret error) = ole32.CoCreateGuid
-//sys coTaskMemFree(address unsafe.Pointer) = ole32.CoTaskMemFree
+//sys CoTaskMemFree(address unsafe.Pointer) = ole32.CoTaskMemFree
//sys rtlGetVersion(info *OsVersionInfoEx) (ret error) = ntdll.RtlGetVersion
// syscall interface implementation for other packages
@@ -1302,7 +1302,7 @@ func (t Token) KnownFolderPath(folderID *KNOWNFOLDERID, flags uint32) (string, e
if err != nil {
return "", err
}
- defer coTaskMemFree(unsafe.Pointer(p))
+ defer CoTaskMemFree(unsafe.Pointer(p))
return UTF16ToString((*[(1 << 30) - 1]uint16)(unsafe.Pointer(p))[:]), nil
}
diff --git a/vendor/golang.org/x/sys/windows/zsyscall_windows.go b/vendor/golang.org/x/sys/windows/zsyscall_windows.go
index 9e43e966cf..d461bed98a 100644
--- a/vendor/golang.org/x/sys/windows/zsyscall_windows.go
+++ b/vendor/golang.org/x/sys/windows/zsyscall_windows.go
@@ -2517,7 +2517,7 @@ func coCreateGuid(pguid *GUID) (ret error) {
return
}
-func coTaskMemFree(address unsafe.Pointer) {
+func CoTaskMemFree(address unsafe.Pointer) {
syscall.Syscall(procCoTaskMemFree.Addr(), 1, uintptr(address), 0, 0)
return
}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index d9da4be69d..91397a577b 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -15,6 +15,8 @@ github.com/Unknwon/i18n
github.com/Unknwon/paginater
# github.com/andybalholm/cascadia v0.0.0-20161224141413-349dd0209470
github.com/andybalholm/cascadia
+# github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239
+github.com/anmitsu/go-shlex
# github.com/beorn7/perks v1.0.0
github.com/beorn7/perks/quantile
# github.com/blevesearch/bleve v0.0.0-20190214220507-05d86ea8f6e3
@@ -112,6 +114,8 @@ github.com/facebookgo/grace/gracenet
github.com/facebookgo/httpdown
# github.com/facebookgo/stats v0.0.0-20151006221625-1b76add642e4
github.com/facebookgo/stats
+# github.com/gliderlabs/ssh v0.2.2
+github.com/gliderlabs/ssh
# github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd
github.com/glycerine/go-unsnap-stream
# github.com/go-macaron/binding v0.0.0-20160711225916-9440f336b443
@@ -353,7 +357,7 @@ golang.org/x/crypto/cast5
golang.org/x/crypto/openpgp/elgamal
golang.org/x/crypto/ssh/knownhosts
golang.org/x/crypto/ssh/agent
-# golang.org/x/net v0.0.0-20190613194153-d28f0bde5980
+# golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b
golang.org/x/net/html/charset
golang.org/x/net/html
golang.org/x/net/html/atom
@@ -365,7 +369,7 @@ golang.org/x/net/internal/socks
# golang.org/x/oauth2 v0.0.0-20181101160152-c453e0c75759
golang.org/x/oauth2
golang.org/x/oauth2/internal
-# golang.org/x/sys v0.0.0-20190618155005-516e3c20635f
+# golang.org/x/sys v0.0.0-20190620070143-6f217b454f45
golang.org/x/sys/windows
golang.org/x/sys/windows/svc
golang.org/x/sys/unix