ssh: fake remote host support

This commit is contained in:
Pavel 2023-11-18 22:29:18 +03:00
parent 99b5b8c775
commit 273482e3fa
5 changed files with 127 additions and 21 deletions

View File

@ -45,7 +45,7 @@ func New(ctx context.Context, name string, params config.DriverParams) (base.Dri
}
func (d *SSH) forward(val sshtun.Forward) conn {
tun := sshtun.New(d.params.Address, d.params.Auth.User, d.params.DisableRemoteHostResolve, val, d.auth, d.Log())
tun := sshtun.New(d.params.Address, d.params.Auth.User, d.params.FakeRemoteHost, val, d.auth, d.Log())
ctx, cancel := context.WithCancel(d.Context())
go tun.Connect(ctx, sshtun.StdoutPrinterSessionCallback(d.Log().With("ssh-output", val.Remote.String())))
return conn{ctx: ctx, cancel: cancel, tun: tun}

View File

@ -15,7 +15,7 @@ type Params struct {
DomainProto string `mapstructure:"domain_proto"`
DomainExtractRegex string `mapstructure:"domain_extract_regex" validate:"validregexp"`
Mode types.DomainMode `mapstructure:"mode" validate:"required,oneof=single multi"`
DisableRemoteHostResolve bool `mapstructure:"disable_remote_host_resolve"`
FakeRemoteHost bool `mapstructure:"fake_remote_host"`
}
func (p *Params) Validate() error {

View File

@ -4,6 +4,7 @@ import (
"context"
"fmt"
"net"
"strings"
"sync"
"sync/atomic"
"time"
@ -173,15 +174,14 @@ func (c *Tunnel) ipFromAddr(addr net.Addr) net.IP {
func handleReverseForwardConn(client net.Conn, forward Forward, log *zap.SugaredLogger) {
defer client.Close()
log.Debugf("%s connected", client.RemoteAddr())
defer log.Debug("closed")
remote, err := net.Dial("tcp", forward.Local.String())
if err != nil {
log.Errorf("dial INTO local service error: %s", err.Error())
log.Errorf("cannot dial local service: %s", err.Error())
return
}
log.Debugf("proxying %s <-> %s", forward.Local.String(), client.RemoteAddr())
// pipe data in both directions:
// - client => remote
// - remote => client
@ -194,6 +194,10 @@ func handleReverseForwardConn(client net.Conn, forward Forward, log *zap.Sugared
bidipipe.WithName("client", client),
bidipipe.WithName("remote", remote),
); err != nil {
// we can safely ignore those errors.
if strings.Contains(err.Error(), "use of closed network connection") {
return
}
log.Error(err)
}
}

View File

@ -1,5 +1,5 @@
--- pkg/proto/ssh/tcpip.go 2023-11-18 21:39:15.394837005 +0300
+++ pkg/proto/ssh/tcpip.go 2023-11-18 21:38:25.706173351 +0300
--- cryptolib/ssh/tcpip.go 2023-11-18 22:20:50.609146922 +0300
+++ pkg/proto/ssh/tcpip.go 2023-11-18 22:19:08.891684669 +0300
@@ -101,14 +101,18 @@
// ListenTCP requests the remote peer open a listening socket
// on laddr. Incoming connections will be available by calling
@ -21,3 +21,76 @@
uint32(laddr.Port),
}
// send message
@@ -133,7 +137,12 @@
}
// Register this forward, using the port number we obtained.
- ch := c.forwards.add(laddr)
+ var ch chan forward
+ if len(fakeHost) > 0 {
+ ch = c.forwards.add(laddr, fakeHost[0])
+ } else {
+ ch = c.forwards.add(laddr)
+ }
return &tcpListener{laddr, c, ch}, nil
}
@@ -142,7 +151,9 @@
// forward requests and the tcpListeners.
type forwardList struct {
sync.Mutex
- entries []forwardEntry
+ fdm sync.RWMutex
+ fakeDomainsMap map[string]string
+ entries []forwardEntry
}
// forwardEntry represents an established mapping of a laddr on a
@@ -160,17 +171,30 @@
raddr net.Addr // the raddr of the incoming connection
}
-func (l *forwardList) add(addr net.Addr) chan forward {
+func (l *forwardList) add(addr net.Addr, fakeHost ...string) chan forward {
l.Lock()
defer l.Unlock()
f := forwardEntry{
laddr: addr,
c: make(chan forward, 1),
}
+ if len(fakeHost) > 0 {
+ if l.fakeDomainsMap == nil {
+ l.fakeDomainsMap = make(map[string]string)
+ }
+ defer l.fdm.Unlock()
+ l.fdm.Lock()
+ l.fakeDomainsMap[fakeHost[0]] = l.ipFromAddr(addr)
+ }
l.entries = append(l.entries, f)
return f.c
}
+func (l *forwardList) ipFromAddr(addr net.Addr) string {
+ host, _, _ := net.SplitHostPort(addr.String())
+ return host
+}
+
// See RFC 4254, section 7.2
type forwardedTCPPayload struct {
Addr string
@@ -206,6 +230,15 @@
continue
}
+ l.fdm.RLock()
+ if addr, ok := l.fakeDomainsMap[payload.Addr]; ok {
+ payload.Addr = addr
+ }
+ if addr, ok := l.fakeDomainsMap[payload.OriginAddr]; ok {
+ payload.OriginAddr = addr
+ }
+ l.fdm.RUnlock()
+
// RFC 4254 section 7.2 specifies that incoming
// addresses should list the address, in string
// format. It is implied that this should be an IP

View File

@ -137,7 +137,12 @@ func (c *Client) ListenTCP(laddr *net.TCPAddr, fakeHost ...string) (net.Listener
}
// Register this forward, using the port number we obtained.
ch := c.forwards.add(laddr)
var ch chan forward
if len(fakeHost) > 0 {
ch = c.forwards.add(laddr, fakeHost[0])
} else {
ch = c.forwards.add(laddr)
}
return &tcpListener{laddr, c, ch}, nil
}
@ -146,6 +151,8 @@ func (c *Client) ListenTCP(laddr *net.TCPAddr, fakeHost ...string) (net.Listener
// forward requests and the tcpListeners.
type forwardList struct {
sync.Mutex
fdm sync.RWMutex
fakeDomainsMap map[string]string
entries []forwardEntry
}
@ -164,17 +171,30 @@ type forward struct {
raddr net.Addr // the raddr of the incoming connection
}
func (l *forwardList) add(addr net.Addr) chan forward {
func (l *forwardList) add(addr net.Addr, fakeHost ...string) chan forward {
l.Lock()
defer l.Unlock()
f := forwardEntry{
laddr: addr,
c: make(chan forward, 1),
}
if len(fakeHost) > 0 {
if l.fakeDomainsMap == nil {
l.fakeDomainsMap = make(map[string]string)
}
defer l.fdm.Unlock()
l.fdm.Lock()
l.fakeDomainsMap[fakeHost[0]] = l.ipFromAddr(addr)
}
l.entries = append(l.entries, f)
return f.c
}
func (l *forwardList) ipFromAddr(addr net.Addr) string {
host, _, _ := net.SplitHostPort(addr.String())
return host
}
// See RFC 4254, section 7.2
type forwardedTCPPayload struct {
Addr string
@ -210,6 +230,15 @@ func (l *forwardList) handleChannels(in <-chan NewChannel) {
continue
}
l.fdm.RLock()
if addr, ok := l.fakeDomainsMap[payload.Addr]; ok {
payload.Addr = addr
}
if addr, ok := l.fakeDomainsMap[payload.OriginAddr]; ok {
payload.OriginAddr = addr
}
l.fdm.RUnlock()
// RFC 4254 section 7.2 specifies that incoming
// addresses should list the address, in string
// format. It is implied that this should be an IP