Skip to content

Commit 91a4ecc

Browse files
committed
feat(agent): use multistream and yamux muxer instead of HTTP server
This change replaces the legacy per-connection HTTP/revdial reverse-tunnel flow with a multiplexed, per-stream protocol (v2) based on HashiCorp's yamux and multistream-select. Yamux sessions allow a single agent websocket to host many logical streams without repeating the HTTP reverse-listen handshake for each session. Multistream-select provides explicit per-stream protocol negotiation, making each logical stream's intent (SSH open/close, HTTP proxy) explicit and versionable. The new approach reduces handshake overhead and makes future protocol extensions easier and safer. Over time the legacy reverse-tunnel implementation (per-request HTTP/revdial) has become a maintenance burden and a performance limiter: each logical session required a full HTTP handshake and a separate reverse listener, which increased latency and duplicated logic across server components. This change replaces that model with a single multiplexed transport per agent using HashiCorp's yamux, and introduces explicit per-stream protocol negotiation via multistream-select. Practically, when an agent connects we now bind its websocket to a yamux session (Manager.Bind) and keep that session alive with a light ping loop. Individual logical operations—opening an SSH session, closing it, or proxying HTTP—are created as yamux streams. Each stream negotiates its intent using multistream identifiers (`/ssh/open/1.0.0`, `/ssh/close/1.0.0`, `/http/proxy/1.0.0`) and then exchanges a small JSON envelope for any parameters. The dialer package centralizes version handling and prepares connections for callers via DialTo(ctx, tenant, uid, target), while Target implementations encapsulate the version-specific bootstrap (legacy HTTP v1 vs yamux+multistream v2). We kept backward compatibility in mind: the Manager still recognizes existing revdial-based connections and will return a v1 connection when appropriate. That lets the system run both v1 and v2 agents concurrently and supports staged rollouts. I also removed the duplicated tunnel wiring and replaced it with a focused HTTP sidecar (http) that exposes both the legacy endpoints and the new v2 bind endpoint; server and session code now call into dialer.Dialer and Target helpers rather than performing raw HTTP handshakes.
1 parent 9c59e54 commit 91a4ecc

File tree

27 files changed

+1448
-840
lines changed

27 files changed

+1448
-840
lines changed

agent/agent.go

Lines changed: 130 additions & 122 deletions
Large diffs are not rendered by default.

agent/go.mod

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@ require (
99
github.com/docker/docker v28.5.1+incompatible
1010
github.com/gliderlabs/ssh v0.3.5
1111
github.com/go-playground/assert/v2 v2.2.0
12-
github.com/labstack/echo/v4 v4.13.4
12+
github.com/gorilla/websocket v1.5.0
13+
github.com/hashicorp/yamux v0.1.2
1314
github.com/mattn/go-shellwords v1.0.12
15+
github.com/multiformats/go-multistream v0.6.1
1416
github.com/openwall/yescrypt-go v1.0.0
1517
github.com/pkg/errors v0.9.1
1618
github.com/pkg/sftp v1.13.9
@@ -22,14 +24,6 @@ require (
2224
golang.org/x/sys v0.37.0
2325
)
2426

25-
require (
26-
github.com/labstack/gommon v0.4.2 // indirect
27-
github.com/mattn/go-colorable v0.1.14 // indirect
28-
github.com/mattn/go-isatty v0.0.20 // indirect
29-
github.com/valyala/bytebufferpool v1.0.0 // indirect
30-
github.com/valyala/fasttemplate v1.2.2 // indirect
31-
)
32-
3327
require (
3428
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
3529
github.com/Microsoft/go-winio v0.6.2 // indirect
@@ -48,12 +42,12 @@ require (
4842
github.com/go-playground/validator/v10 v10.11.2 // indirect
4943
github.com/go-resty/resty/v2 v2.7.0 // indirect
5044
github.com/google/uuid v1.6.0 // indirect
51-
github.com/gorilla/websocket v1.5.0 // indirect
5245
github.com/inconshreveable/mousetrap v1.1.0 // indirect
5346
github.com/kr/fs v0.1.0 // indirect
5447
github.com/leodido/go-urn v1.2.2 // indirect
5548
github.com/moby/docker-image-spec v1.3.1 // indirect
5649
github.com/moby/sys/atomicwriter v0.1.0 // indirect
50+
github.com/multiformats/go-varint v0.0.6 // indirect
5751
github.com/opencontainers/go-digest v1.0.0 // indirect
5852
github.com/opencontainers/image-spec v1.1.0 // indirect
5953
github.com/pmezard/go-difflib v1.0.0 // indirect
@@ -69,6 +63,7 @@ require (
6963
go.opentelemetry.io/proto/otlp v1.2.0 // indirect
7064
golang.org/x/net v0.45.0 // indirect
7165
golang.org/x/text v0.30.0 // indirect
66+
golang.org/x/time v0.11.0 // indirect
7267
gopkg.in/yaml.v3 v3.0.1 // indirect
7368
gotest.tools/v3 v3.5.1 // indirect
7469
)

agent/go.sum

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWm
5656
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
5757
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 h1:/c3QmbOGMGTOumP2iT/rCwB7b0QDGLKzqOmktBjT+Is=
5858
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1/go.mod h1:5SN9VR2LTsRFsrEC6FHgRbTWrTHu6tqPeKxEQv15giM=
59+
github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8=
60+
github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns=
5961
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
6062
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
6163
github.com/jarcoal/httpmock v1.3.1 h1:iUx3whfZWVf3jT01hQTO/Eo5sAYtB2/rqaUuOtpInww=
@@ -66,16 +68,8 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
6668
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
6769
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
6870
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
69-
github.com/labstack/echo/v4 v4.13.4 h1:oTZZW+T3s9gAu5L8vmzihV7/lkXGZuITzTQkTEhcXEA=
70-
github.com/labstack/echo/v4 v4.13.4/go.mod h1:g63b33BZ5vZzcIUF8AtRH40DrTlXnx4UMC8rBdndmjQ=
71-
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
72-
github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU=
7371
github.com/leodido/go-urn v1.2.2 h1:7z68G0FCGvDk646jz1AelTYNYWrTNm0bEcFAo147wt4=
7472
github.com/leodido/go-urn v1.2.2/go.mod h1:kUaIbLZWttglzwNuG0pgsh5vuV6u2YcGBYz1hIPjtOQ=
75-
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
76-
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
77-
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
78-
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
7973
github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk=
8074
github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
8175
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
@@ -88,6 +82,10 @@ github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
8882
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
8983
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
9084
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
85+
github.com/multiformats/go-multistream v0.6.1 h1:4aoX5v6T+yWmc2raBHsTvzmFhOI8WVOer28DeBBEYdQ=
86+
github.com/multiformats/go-multistream v0.6.1/go.mod h1:ksQf6kqHAb6zIsyw7Zm+gAuVo57Qbq84E27YlYqavqw=
87+
github.com/multiformats/go-varint v0.0.6 h1:gk85QWKxh3TazbLxED/NlDVv8+q+ReFJk7Y2W/KhfNY=
88+
github.com/multiformats/go-varint v0.0.6/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE=
9189
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
9290
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
9391
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
@@ -125,10 +123,6 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
125123
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
126124
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
127125
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
128-
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
129-
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
130-
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
131-
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
132126
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
133127
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
134128
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
@@ -193,7 +187,6 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
193187
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
194188
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
195189
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
196-
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
197190
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
198191
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
199192
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=

agent/pkg/tunnel/context.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package tunnel
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"errors"
7+
"io"
8+
"time"
9+
10+
log "github.com/sirupsen/logrus"
11+
)
12+
13+
type Context struct {
14+
ctx context.Context
15+
16+
encoder *json.Encoder
17+
decoder *json.Decoder
18+
}
19+
20+
func (c Context) Deadline() (deadline time.Time, ok bool) {
21+
return c.ctx.Deadline()
22+
}
23+
24+
func (c Context) Done() <-chan struct{} {
25+
return c.ctx.Done()
26+
}
27+
28+
func (c Context) Err() error {
29+
return c.ctx.Err()
30+
}
31+
32+
func (c Context) Value(key any) any {
33+
return c.ctx.Value(key)
34+
}
35+
36+
func (c Context) Status(status string) error {
37+
if err := c.encoder.Encode(map[string]string{"status": status}); err != nil {
38+
log.WithError(err).Error("failed to send status response")
39+
40+
return errors.Join(errors.New("failed to send status response"), err)
41+
}
42+
43+
return nil
44+
}
45+
46+
func (c Context) Error(err error) error {
47+
if err := c.encoder.Encode(map[string]string{"error": err.Error()}); err != nil {
48+
log.WithError(err).Error("failed to send error response")
49+
50+
return errors.Join(errors.New("failed to send error response"), err)
51+
}
52+
53+
return nil
54+
}
55+
56+
type Headers map[string]string
57+
58+
func (c Context) Headers() (Headers, error) {
59+
// TODO: cache the headers after the first call.
60+
var header Headers
61+
62+
if err := c.decoder.Decode(&header); err != nil {
63+
log.WithError(err).Error("failed to decode the header")
64+
65+
return nil, err
66+
}
67+
68+
return header, nil
69+
}
70+
71+
func NewContext(ctx context.Context, rwc io.ReadWriteCloser) Context {
72+
return Context{
73+
ctx: ctx,
74+
encoder: json.NewEncoder(rwc),
75+
decoder: json.NewDecoder(rwc),
76+
}
77+
}
78+
79+
type Handler func(ctx Context, rwc io.ReadWriteCloser) error

agent/pkg/tunnel/tunnel.go

Lines changed: 59 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -2,102 +2,85 @@ package tunnel
22

33
import (
44
"context"
5+
"errors"
6+
"io"
57
"net"
6-
"net/http"
8+
"os"
9+
"time"
710

8-
"github.com/labstack/echo/v4"
9-
"github.com/shellhub-io/shellhub/pkg/revdial"
11+
"github.com/gorilla/websocket"
12+
"github.com/hashicorp/yamux"
13+
"github.com/multiformats/go-multistream"
14+
log "github.com/sirupsen/logrus"
1015
)
1116

1217
type Tunnel struct {
13-
router *echo.Echo
14-
srv *http.Server
15-
HTTPProxyHandler func(e echo.Context) error
16-
SSHHandler func(e echo.Context) error
17-
SSHCloseHandler func(e echo.Context) error
18+
mux *multistream.MultistreamMuxer[string]
1819
}
1920

20-
type Builder struct {
21-
tunnel *Tunnel
22-
}
23-
24-
func NewBuilder() *Builder {
25-
return &Builder{
26-
tunnel: NewTunnel(),
21+
func NewTunnel() *Tunnel {
22+
return &Tunnel{
23+
mux: multistream.NewMultistreamMuxer[string](),
2724
}
2825
}
2926

30-
func (t *Builder) WithHTTPProxyHandler(handler func(e echo.Context) error) *Builder {
31-
t.tunnel.HTTPProxyHandler = handler
27+
func (t *Tunnel) Handle(protocol string, handler Handler) {
28+
t.mux.AddHandler(protocol, func(protocol string, rwc io.ReadWriteCloser) error {
29+
log.WithField("protocol", protocol).Debug("handling connection")
30+
defer log.WithField("protocol", protocol).Debug("handling connection closed")
3231

33-
return t
32+
// TODO: Should we receive a context from outside?
33+
return handler(NewContext(context.TODO(), rwc), rwc)
34+
})
3435
}
3536

36-
func (t *Builder) WithSSHHandler(handler func(e echo.Context) error) *Builder {
37-
t.tunnel.SSHHandler = handler
37+
// ErrTunnelDisconnect is returned when the tunnel connection is closed.
38+
var ErrTunnelDisconnect = errors.New("tunnel disconnected")
39+
40+
func (t *Tunnel) Listen(conn net.Conn) error {
41+
session, err := yamux.Server(conn, &yamux.Config{
42+
AcceptBacklog: 256,
43+
EnableKeepAlive: true,
44+
KeepAliveInterval: 35 * time.Second,
45+
ConnectionWriteTimeout: 15 * time.Second,
46+
MaxStreamWindowSize: 256 * 1024,
47+
StreamCloseTimeout: 5 * time.Minute,
48+
StreamOpenTimeout: 75 * time.Second,
49+
LogOutput: os.Stderr,
50+
})
51+
if err != nil {
52+
log.WithError(err).Error("failed to create muxed session")
3853

39-
return t
40-
}
54+
return err
55+
}
4156

42-
func (t *Builder) WithSSHCloseHandler(handler func(e echo.Context) error) *Builder {
43-
t.tunnel.SSHCloseHandler = handler
57+
for {
58+
stream, err := session.Accept()
59+
if err != nil {
60+
defer session.Close()
4461

45-
return t
46-
}
62+
log.WithError(err).Trace("failed to accept stream")
4763

48-
func (t *Builder) Build() *Tunnel {
49-
return t.tunnel
50-
}
64+
switch {
65+
case websocket.IsCloseError(err, websocket.CloseAbnormalClosure):
66+
return errors.Join(ErrTunnelDisconnect, err)
67+
}
5168

52-
func NewTunnel() *Tunnel {
53-
e := echo.New()
54-
55-
t := &Tunnel{
56-
router: e,
57-
srv: &http.Server{ //nolint:gosec
58-
Handler: e,
59-
ConnContext: func(ctx context.Context, c net.Conn) context.Context {
60-
return context.WithValue(ctx, "http-conn", c) //nolint:revive
61-
},
62-
},
63-
SSHHandler: func(_ echo.Context) error {
64-
panic("ConnHandler can not be nil")
65-
},
66-
SSHCloseHandler: func(_ echo.Context) error {
67-
panic("CloseHandler can not be nil")
68-
},
69-
HTTPProxyHandler: func(_ echo.Context) error {
70-
panic("ProxyHandler can not be nil")
71-
},
72-
}
73-
e.GET("/ssh/:id", func(e echo.Context) error {
74-
return t.SSHHandler(e)
75-
})
76-
e.GET("/ssh/close/:id", func(e echo.Context) error {
77-
return t.SSHCloseHandler(e)
78-
})
79-
e.CONNECT("/http/proxy/:addr", func(e echo.Context) error {
80-
// NOTE: The CONNECT HTTP method requests that a proxy establish a HTTP tunnel to this server, and if
81-
// successful, blindly forward data in both directions until the tunnel is closed.
82-
//
83-
// https://en.wikipedia.org/wiki/HTTP_tunnel
84-
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/CONNECT
85-
return t.HTTPProxyHandler(e)
86-
})
69+
return err
70+
}
8771

88-
return t
89-
}
72+
log.Trace("new stream accepted")
9073

91-
// Listen to reverse listener.
92-
func (t *Tunnel) Listen(l *revdial.Listener) error {
93-
return t.srv.Serve(l)
94-
}
74+
go func() {
75+
log.Trace("handling stream")
9576

96-
// Close closes the tunnel.
97-
func (t *Tunnel) Close() error {
98-
if err := t.router.Close(); err != nil {
99-
return err
100-
}
77+
if err := t.mux.Handle(stream); err != nil {
78+
log.WithError(err).Trace("failed to handle stream")
10179

102-
return t.srv.Close()
80+
_ = stream.Close()
81+
}
82+
83+
log.Trace("stream handled")
84+
}()
85+
}
10386
}

gateway/nginx/conf.d/shellhub.conf

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,29 @@ server {
368368
proxy_redirect off;
369369
}
370370

371+
location /connection {
372+
set $upstream ssh:8080;
373+
374+
auth_request /auth;
375+
auth_request_set $tenant_id $upstream_http_x_tenant_id;
376+
auth_request_set $device_uid $upstream_http_x_device_uid;
377+
proxy_pass http://$upstream;
378+
proxy_set_header Connection $connection_upgrade;
379+
proxy_set_header Host $host;
380+
proxy_set_header Upgrade $http_upgrade;
381+
{{ if $cfg.EnableProxyProtocol -}}
382+
proxy_set_header X-Real-IP $proxy_protocol_addr;
383+
{{ else -}}
384+
proxy_set_header X-Real-IP $x_real_ip;
385+
{{ end -}}
386+
proxy_set_header X-Device-UID $device_uid;
387+
proxy_set_header X-Tenant-ID $tenant_id;
388+
proxy_set_header X-Request-ID $request_id;
389+
proxy_http_version 1.1;
390+
proxy_cache_bypass $http_upgrade;
391+
proxy_redirect off;
392+
}
393+
371394
location /ssh/auth {
372395
set $upstream api:8080;
373396

pkg/api/client/client.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,12 @@ type publicAPI interface {
2828
Endpoints() (*models.Endpoints, error)
2929
AuthDevice(req *models.DeviceAuthRequest) (*models.DeviceAuthResponse, error)
3030
AuthPublicKey(req *models.PublicKeyAuthRequest, token string) (*models.PublicKeyAuthResponse, error)
31+
// NewReverseListener creates a new reverse listener to be used by the Agent to connect to ShellHub's SSH server.
32+
//
33+
//Deprecated: Use Connect instead.
3134
NewReverseListener(ctx context.Context, token string, connPath string) (*revdial.Listener, error)
35+
// Connect creates a new connection to be used by the Agent to connect to ShellHub's SSH server.
36+
Connect(ctx context.Context, token string, path string) (net.Conn, error)
3237
}
3338

3439
//go:generate mockery --name=Client --filename=client.go

0 commit comments

Comments
 (0)