ssh: localhost remote by default, auto username, update proto lib

This commit is contained in:
Pavel 2023-11-24 22:01:53 +03:00
parent 57af057708
commit 64fa306f8d
6 changed files with 82 additions and 50 deletions

View File

@ -6,6 +6,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"net" "net"
"os/user"
"path" "path"
"regexp" "regexp"
"strconv" "strconv"
@ -69,9 +70,21 @@ func New(ctx context.Context, name string, params config.DriverParams) (base.Dri
return drv, nil return drv, nil
} }
func (d *SSH) getUser() string {
if d.params.Auth.User != "" {
return d.params.Auth.User
}
userData, err := user.Current()
if err != nil {
d.Log().Warn("cannot get current user name - using 'root'")
return "root"
}
return userData.Username
}
func (d *SSH) forward(val sshtun.Forward, domainMatcher func(string)) conn { func (d *SSH) forward(val sshtun.Forward, domainMatcher func(string)) conn {
tun := sshtun.New(d.params.Address, tun := sshtun.New(d.params.Address,
d.params.Auth.User, d.getUser(),
d.auth, d.auth,
sshtun.TunnelConfig{ sshtun.TunnelConfig{
Forward: val, Forward: val,
@ -307,7 +320,7 @@ func (d *SSH) remoteEndpoint(remoteHost string) sshtun.Endpoint {
} }
if remoteHost == "" && !d.params.FakeRemoteHost { if remoteHost == "" && !d.params.FakeRemoteHost {
// Listen on all interfaces if no host was provided. // Listen on all interfaces if no host was provided.
remoteHost = "0.0.0.0" remoteHost = "127.0.0.1"
} }
return sshtun.Endpoint{ return sshtun.Endpoint{
Host: remoteHost, Host: remoteHost,

View File

@ -151,6 +151,7 @@ func (t *Tunnel) connect(ctx context.Context,
go func() { go func() {
defer wg.Done() defer wg.Done()
_ = sess.Wait() _ = sess.Wait()
t.log.Debug("main session has been terminated")
}() }()
} }

View File

@ -14,6 +14,7 @@ import (
"github.com/Neur0toxine/sshpoke/internal/server/driver/base" "github.com/Neur0toxine/sshpoke/internal/server/driver/base"
"github.com/Neur0toxine/sshpoke/internal/server/driver/plugin" "github.com/Neur0toxine/sshpoke/internal/server/driver/plugin"
"github.com/Neur0toxine/sshpoke/pkg/dto" "github.com/Neur0toxine/sshpoke/pkg/dto"
"go.uber.org/zap"
) )
type Manager struct { type Manager struct {
@ -26,6 +27,7 @@ type Manager struct {
statusLock sync.RWMutex statusLock sync.RWMutex
ctx context.Context ctx context.Context
defaultServer string defaultServer string
log *zap.SugaredLogger
} }
type ServerStatus struct { type ServerStatus struct {
@ -47,23 +49,24 @@ func NewManager(ctx context.Context, servers []config.Server, defaultServer stri
statusMap: make(map[string]ServerStatus), statusMap: make(map[string]ServerStatus),
forwards: make(map[string]bool), forwards: make(map[string]bool),
defaultServer: defaultServer, defaultServer: defaultServer,
log: logger.Sugar.With("component", "manager"),
} }
for _, serverConfig := range servers { for _, serverConfig := range servers {
server, err := driver.New(ctx, serverConfig.Name, serverConfig.Driver, serverConfig.Params) server, err := driver.New(ctx, serverConfig.Name, serverConfig.Driver, serverConfig.Params)
if err != nil { if err != nil {
logger.Sugar.Errorf("cannot initialize server '%s': %s", serverConfig.Name, err) m.log.Errorf("cannot initialize server '%s': %s", serverConfig.Name, err)
continue continue
} }
server.SetEventStatusCallback(m.eventStatusCallback(server.Name())) server.SetEventStatusCallback(m.eventStatusCallback(server.Name()))
if server.Driver() == config.DriverPlugin { if server.Driver() == config.DriverPlugin {
pl := server.(plugin.Plugin) pl := server.(plugin.Plugin)
if pl.Token() == "" { if pl.Token() == "" {
logger.Sugar.Warnf("server '%s' will not work because it doesn't have a token", pl.Name()) m.log.Warnf("server '%s' will not work because it doesn't have a token", pl.Name())
continue continue
} }
existing, found := m.plugins[pl.Token()] existing, found := m.plugins[pl.Token()]
if found { if found {
logger.Sugar.Fatalw("two plugins cannot have the same token", m.log.Fatalw("two plugins cannot have the same token",
"plugin1", existing.Name(), "plugin2", pl.Name(), "token", pl.Token()) "plugin1", existing.Name(), "plugin2", pl.Name(), "token", pl.Token())
} }
m.plugins[pl.Token()] = pl m.plugins[pl.Token()] = pl
@ -114,7 +117,7 @@ func (m *Manager) eventStatusCallback(serverName string) base.EventStatusCallbac
} }
func (m *Manager) processEventStatus(serverName string, event dto.EventStatus) { func (m *Manager) processEventStatus(serverName string, event dto.EventStatus) {
logger.Sugar.Debugw("received EventStatus from server", m.log.Debugw("received EventStatus from server",
"serverName", serverName, "eventStatus", event) "serverName", serverName, "eventStatus", event)
item, found := docker.Default.GetContainer(event.ID, true) item, found := docker.Default.GetContainer(event.ID, true)
if !found { if !found {
@ -175,10 +178,16 @@ func (m *Manager) markAndSweepForwards() {
m.forwards[id] = false // unmark m.forwards[id] = false // unmark
} else { } else {
if state { if state {
m.processEventStatus(serverName, dto.EventStatus{ err := m.ProcessEvent(dto.Event{
Type: dto.EventStop, Type: dto.EventStop,
ID: containerID, Container: dto.Container{
ID: containerID,
Server: serverName,
},
}) })
if err != nil {
m.log.Warnf("cannot process mark-and-sweep event: %s", err)
}
continue continue
} }
m.forwards[id] = true // mark m.forwards[id] = true // mark

View File

@ -1,38 +1,20 @@
diff -Naru cryptolib/ssh/cipher.go pkg/proto/ssh/cipher.go diff -Naru cryptolib/ssh/cipher.go pkg/proto/ssh/cipher.go
--- cryptolib/ssh/cipher.go 2023-11-21 18:52:03.117248053 +0300 --- cryptolib/ssh/cipher.go 2023-11-24 21:59:11.581318943 +0300
+++ pkg/proto/ssh/cipher.go 2023-11-21 17:19:04.688042738 +0300 +++ pkg/proto/ssh/cipher.go 2023-11-24 21:39:11.897832847 +0300
@@ -16,8 +16,8 @@ @@ -16,8 +16,8 @@
"hash" "hash"
"io" "io"
- "golang.org/x/crypto/chacha20" - "golang.org/x/crypto/chacha20"
"github.com/Neur0toxine/sshpoke/pkg/proto/ssh/internal/poly1305" "github.com/Neur0toxine/sshpoke/pkg/proto/ssh/internal/poly1305"
+ "golang.org/x/crypto/chacha20" + "golang.org/x/crypto/chacha20"
) )
const ( const (
diff -Naru cryptolib/ssh/common.go pkg/proto/ssh/common.go diff -Naru cryptolib/ssh/common.go pkg/proto/ssh/common.go
--- cryptolib/ssh/common.go 2023-11-21 18:52:03.217249582 +0300 --- cryptolib/ssh/common.go 2023-11-24 21:59:11.677320636 +0300
+++ pkg/proto/ssh/common.go 2023-11-21 17:28:23.221203344 +0300 +++ pkg/proto/ssh/common.go 2023-11-24 21:39:08.401775359 +0300
@@ -7,14 +7,13 @@ @@ -287,6 +287,9 @@
import (
"crypto"
"crypto/rand"
+ _ "crypto/sha1"
+ _ "crypto/sha256"
+ _ "crypto/sha512"
"fmt"
"io"
"math"
"sync"
-
- _ "crypto/sha1"
- _ "crypto/sha256"
- _ "crypto/sha512"
)
// These are string constants in the SSH protocol.
@@ -279,6 +278,9 @@
// The allowed MAC algorithms. If unspecified then a sensible default is // The allowed MAC algorithms. If unspecified then a sensible default is
// used. Unsupported values are silently ignored. // used. Unsupported values are silently ignored.
MACs []string MACs []string
@ -40,15 +22,15 @@ diff -Naru cryptolib/ssh/common.go pkg/proto/ssh/common.go
+ // Called on every incoming handshake packet for client. Only receives data and extended data packets. + // Called on every incoming handshake packet for client. Only receives data and extended data packets.
+ HandshakePacketReader func(p []byte) + HandshakePacketReader func(p []byte)
} }
// SetDefaults sets sensible values for unset fields in config. This is // SetDefaults sets sensible values for unset fields in config. This is
diff -Naru cryptolib/ssh/handshake.go pkg/proto/ssh/handshake.go diff -Naru cryptolib/ssh/handshake.go pkg/proto/ssh/handshake.go
--- cryptolib/ssh/handshake.go 2023-11-21 18:52:03.209249460 +0300 --- cryptolib/ssh/handshake.go 2023-11-24 21:59:11.673320566 +0300
+++ pkg/proto/ssh/handshake.go 2023-11-21 18:51:58.681180283 +0300 +++ pkg/proto/ssh/handshake.go 2023-11-24 21:59:05.113204819 +0300
@@ -401,6 +401,14 @@ @@ -401,6 +401,14 @@
t.printPacket(p, false) t.printPacket(p, false)
} }
+ if t.config.HandshakePacketReader != nil && p[0] == msgChannelData { + if t.config.HandshakePacketReader != nil && p[0] == msgChannelData {
+ data, err := decode(p) + data, err := decode(p)
+ packetData, ok := data.(*channelDataMsg) + packetData, ok := data.(*channelDataMsg)
@ -61,8 +43,8 @@ diff -Naru cryptolib/ssh/handshake.go pkg/proto/ssh/handshake.go
return nil, fmt.Errorf("ssh: first packet should be msgKexInit") return nil, fmt.Errorf("ssh: first packet should be msgKexInit")
} }
diff -Naru cryptolib/ssh/tcpip.go pkg/proto/ssh/tcpip.go diff -Naru cryptolib/ssh/tcpip.go pkg/proto/ssh/tcpip.go
--- cryptolib/ssh/tcpip.go 2023-11-21 18:52:03.129248237 +0300 --- cryptolib/ssh/tcpip.go 2023-11-24 21:59:11.593319154 +0300
+++ pkg/proto/ssh/tcpip.go 2023-11-21 17:19:04.688042738 +0300 +++ pkg/proto/ssh/tcpip.go 2023-11-24 21:39:08.401775359 +0300
@@ -101,14 +101,18 @@ @@ -101,14 +101,18 @@
// ListenTCP requests the remote peer open a listening socket // ListenTCP requests the remote peer open a listening socket
// on laddr. Incoming connections will be available by calling // on laddr. Incoming connections will be available by calling
@ -73,7 +55,7 @@ diff -Naru cryptolib/ssh/tcpip.go pkg/proto/ssh/tcpip.go
if laddr.Port == 0 && isBrokenOpenSSHVersion(string(c.ServerVersion())) { if laddr.Port == 0 && isBrokenOpenSSHVersion(string(c.ServerVersion())) {
return c.autoPortListenWorkaround(laddr) return c.autoPortListenWorkaround(laddr)
} }
+ host := laddr.IP.String() + host := laddr.IP.String()
+ if len(fakeHost) > 0 { + if len(fakeHost) > 0 {
+ host = fakeHost[0] + host = fakeHost[0]
@ -86,7 +68,7 @@ diff -Naru cryptolib/ssh/tcpip.go pkg/proto/ssh/tcpip.go
// send message // send message
@@ -133,7 +137,12 @@ @@ -133,7 +137,12 @@
} }
// Register this forward, using the port number we obtained. // Register this forward, using the port number we obtained.
- ch := c.forwards.add(laddr) - ch := c.forwards.add(laddr)
+ var ch chan forward + var ch chan forward
@ -95,7 +77,7 @@ diff -Naru cryptolib/ssh/tcpip.go pkg/proto/ssh/tcpip.go
+ } else { + } else {
+ ch = c.forwards.add(laddr) + ch = c.forwards.add(laddr)
+ } + }
return &tcpListener{laddr, c, ch}, nil return &tcpListener{laddr, c, ch}, nil
} }
@@ -142,7 +151,9 @@ @@ -142,7 +151,9 @@
@ -107,12 +89,12 @@ diff -Naru cryptolib/ssh/tcpip.go pkg/proto/ssh/tcpip.go
+ fakeDomainsMap map[string]string + fakeDomainsMap map[string]string
+ entries []forwardEntry + entries []forwardEntry
} }
// forwardEntry represents an established mapping of a laddr on a // forwardEntry represents an established mapping of a laddr on a
@@ -160,17 +171,30 @@ @@ -160,17 +171,30 @@
raddr net.Addr // the raddr of the incoming connection 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) chan forward {
+func (l *forwardList) add(addr net.Addr, fakeHost ...string) chan forward { +func (l *forwardList) add(addr net.Addr, fakeHost ...string) chan forward {
l.Lock() l.Lock()
@ -132,7 +114,7 @@ diff -Naru cryptolib/ssh/tcpip.go pkg/proto/ssh/tcpip.go
l.entries = append(l.entries, f) l.entries = append(l.entries, f)
return f.c return f.c
} }
+func (l *forwardList) ipFromAddr(addr net.Addr) string { +func (l *forwardList) ipFromAddr(addr net.Addr) string {
+ host, _, _ := net.SplitHostPort(addr.String()) + host, _, _ := net.SplitHostPort(addr.String())
+ return host + return host
@ -144,7 +126,7 @@ diff -Naru cryptolib/ssh/tcpip.go pkg/proto/ssh/tcpip.go
@@ -206,6 +230,15 @@ @@ -206,6 +230,15 @@
continue continue
} }
+ l.fdm.RLock() + l.fdm.RLock()
+ if addr, ok := l.fakeDomainsMap[payload.Addr]; ok { + if addr, ok := l.fakeDomainsMap[payload.Addr]; ok {
+ payload.Addr = addr + payload.Addr = addr

View File

@ -307,7 +307,10 @@ func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand
} }
var methods []string var methods []string
var errSigAlgo error var errSigAlgo error
for _, signer := range signers {
origSignersLen := len(signers)
for idx := 0; idx < len(signers); idx++ {
signer := signers[idx]
pub := signer.PublicKey() pub := signer.PublicKey()
as, algo, err := pickSignatureAlgorithm(signer, extensions) as, algo, err := pickSignatureAlgorithm(signer, extensions)
if err != nil && errSigAlgo == nil { if err != nil && errSigAlgo == nil {
@ -321,6 +324,21 @@ func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand
if err != nil { if err != nil {
return authFailure, nil, err return authFailure, nil, err
} }
// OpenSSH 7.2-7.7 advertises support for rsa-sha2-256 and rsa-sha2-512
// in the "server-sig-algs" extension but doesn't support these
// algorithms for certificate authentication, so if the server rejects
// the key try to use the obtained algorithm as if "server-sig-algs" had
// not been implemented if supported from the algorithm signer.
if !ok && idx < origSignersLen && isRSACert(algo) && algo != CertAlgoRSAv01 {
if contains(as.Algorithms(), KeyAlgoRSA) {
// We retry using the compat algorithm after all signers have
// been tried normally.
signers = append(signers, &multiAlgorithmSigner{
AlgorithmSigner: as,
supportedAlgorithms: []string{KeyAlgoRSA},
})
}
}
if !ok { if !ok {
continue continue
} }

View File

@ -7,13 +7,14 @@ package ssh
import ( import (
"crypto" "crypto"
"crypto/rand" "crypto/rand"
_ "crypto/sha1"
_ "crypto/sha256"
_ "crypto/sha512"
"fmt" "fmt"
"io" "io"
"math" "math"
"sync" "sync"
_ "crypto/sha1"
_ "crypto/sha256"
_ "crypto/sha512"
) )
// These are string constants in the SSH protocol. // These are string constants in the SSH protocol.
@ -126,6 +127,14 @@ func isRSA(algo string) bool {
return contains(algos, underlyingAlgo(algo)) return contains(algos, underlyingAlgo(algo))
} }
func isRSACert(algo string) bool {
_, ok := certKeyAlgoNames[algo]
if !ok {
return false
}
return isRSA(algo)
}
// supportedPubKeyAuthAlgos specifies the supported client public key // supportedPubKeyAuthAlgos specifies the supported client public key
// authentication algorithms. Note that this doesn't include certificate types // authentication algorithms. Note that this doesn't include certificate types
// since those use the underlying algorithm. This list is sent to the client if // since those use the underlying algorithm. This list is sent to the client if