forked from gravitational/teleport
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtcpip.go
132 lines (117 loc) · 3.72 KB
/
tcpip.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
/*
* Teleport
* Copyright (C) 2023 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package sshutils
import (
"context"
"io"
"net"
"github.com/gravitational/trace"
log "github.com/sirupsen/logrus"
"golang.org/x/crypto/ssh"
"github.com/gravitational/teleport"
"github.com/gravitational/teleport/lib/utils"
)
// DirectTCPIPReq represents the payload of an SSH "direct-tcpip" or
// "forwarded-tcpip" request.
type DirectTCPIPReq struct {
// Host is the receiver-side address to forward to.
Host string
// Port is the receiver-side port to forward to.
Port uint32
// Orig is the sender-side address to forward from.
Orig string
// OrigPort is the sender-side port to forward from.
OrigPort uint32
}
// ParseDirectTCPIPReq parses an SSH request's payload into a DirectTCPIPReq.
func ParseDirectTCPIPReq(data []byte) (*DirectTCPIPReq, error) {
var r DirectTCPIPReq
if err := ssh.Unmarshal(data, &r); err != nil {
log.Infof("failed to parse Direct TCP IP request: %v", err)
return nil, trace.Wrap(err)
}
return &r, nil
}
// TCPIPForwardReq represents the payload of an SSH "tcpip-forward" or
// "cancel-tcpip-forward" request.
type TCPIPForwardReq struct {
// Addr is the address to listen on.
Addr string
// Port is the port to listen on.
Port uint32
}
// ParseTCPIPForwardReq parses an SSH request's payload into a TCPIPForwardReq.
func ParseTCPIPForwardReq(data []byte) (*TCPIPForwardReq, error) {
var r TCPIPForwardReq
if err := ssh.Unmarshal(data, &r); err != nil {
log.Infof("failed to parse TCP IP Forward request: %v", err)
return nil, trace.Wrap(err)
}
return &r, nil
}
type channelOpener interface {
OpenChannel(name string, data []byte) (ssh.Channel, <-chan *ssh.Request, error)
}
// StartRemoteListener listens on the given listener and forwards any accepted
// connections over a new "forwarded-tcpip" channel.
func StartRemoteListener(ctx context.Context, sshConn channelOpener, srcAddr string, listener net.Listener) error {
srcHost, srcPort, err := SplitHostPort(srcAddr)
if err != nil {
return trace.Wrap(err)
}
go func() {
for {
conn, err := listener.Accept()
if err != nil {
if !utils.IsOKNetworkError(err) {
log.WithError(err).Warn("failed to accept connection")
}
return
}
logger := log.WithFields(log.Fields{
"srcAddr": srcAddr,
"remoteAddr": conn.RemoteAddr().String(),
})
dstHost, dstPort, err := SplitHostPort(conn.RemoteAddr().String())
if err != nil {
logger.WithError(err).Warn("failed to parse addr")
return
}
req := ForwardedTCPIPRequest{
Addr: srcHost,
Port: srcPort,
OrigAddr: dstHost,
OrigPort: dstPort,
}
if err := req.CheckAndSetDefaults(); err != nil {
logger.WithError(err).Warn("failed to create forwarded tcpip request")
return
}
reqBytes := ssh.Marshal(req)
ch, rch, err := sshConn.OpenChannel(teleport.ChanForwardedTCPIP, reqBytes)
if err != nil {
logger.WithError(err).Warn("failed to open channel")
continue
}
go ssh.DiscardRequests(rch)
go io.Copy(io.Discard, ch.Stderr())
go utils.ProxyConn(ctx, conn, ch)
}
}()
return nil
}