diff --git a/examples/config.yml b/examples/config.yml index 75bebc3..79ab758 100644 --- a/examples/config.yml +++ b/examples/config.yml @@ -39,14 +39,11 @@ servers: # Set this to true if you're using sish, otherwise you'll get weird domains with IP's in them. # Default: false fake_remote_host: true - # Disables PTY request for this server. Default: false - nopty: true # Requests interactive shell for SSH sessions. Should be `true` for the `commands`. # You can also pass a string with shell binary, for example, "/bin/sh". # Note: commands will be executed using provided shell binary. - # Note (2): some servers won't send you any data until you request a shell even without a PTY. - # You can use a combination of `nopty: true` & `shell: true`. Also, even with PTY you may need `shell` to be `true`. - # Default: false + # Note (2): some servers won't send you any data until you request a shell. + # Default: true shell: false # Spoof client version with provided (value below is taken directly from OpenSSH). This value must be compliant with RFC-4253. # Default: SSH-2.0-Go @@ -97,7 +94,6 @@ servers: address: "your2.server" forward_port: 80 fake_remote_host: true - nopty: false shell: "/usr/bin/bash" mode: single keepalive: diff --git a/internal/server/driver/ssh/driver.go b/internal/server/driver/ssh/driver.go index 9e97df2..fc2b5cd 100644 --- a/internal/server/driver/ssh/driver.go +++ b/internal/server/driver/ssh/driver.go @@ -76,8 +76,7 @@ func (d *SSH) forward(val sshtun.Forward, domainMatcher func(string)) conn { sshtun.TunnelConfig{ Forward: val, HostKeyCallback: d.hostKeyCallback, - NoPTY: d.params.NoPTY, - Shell: sshtun.BoolOrStr(d.params.Shell), + Shell: d.params.Shell, ClientVersion: d.clientVersion, FakeRemoteHost: d.params.FakeRemoteHost, KeepAliveInterval: uint(d.params.KeepAlive.Interval), diff --git a/internal/server/driver/ssh/params.go b/internal/server/driver/ssh/params.go index 0bd0599..259bd85 100644 --- a/internal/server/driver/ssh/params.go +++ b/internal/server/driver/ssh/params.go @@ -1,36 +1,31 @@ package ssh import ( - "errors" - + "github.com/Neur0toxine/sshpoke/internal/server/driver/ssh/sshtun" "github.com/Neur0toxine/sshpoke/internal/server/driver/ssh/types" "github.com/Neur0toxine/sshpoke/internal/server/driver/util" ) type Params struct { - Address string `mapstructure:"address" validate:"required"` - HostKeys string `mapstructure:"host_keys"` - DefaultHost *string `mapstructure:"default_host,omitempty"` - ForwardPort uint16 `mapstructure:"forward_port"` - Auth types.Auth `mapstructure:"auth"` - KeepAlive types.KeepAlive `mapstructure:"keepalive"` - DomainExtractRegex string `mapstructure:"domain_extract_regex" validate:"validregexp"` - ReadRawPackets bool `mapstructure:"read_raw_packets"` - ReadSessionsOutput *bool `mapstructure:"read_sessions_output"` - Mode types.DomainMode `mapstructure:"mode" validate:"required,oneof=single multi"` - FakeRemoteHost bool `mapstructure:"fake_remote_host"` - NoPTY bool `mapstructure:"nopty"` - Shell string `mapstructure:"shell"` - ClientVersion string `mapstructure:"client_version"` - Commands types.Commands `mapstructure:"commands"` + Address string `mapstructure:"address" validate:"required"` + HostKeys string `mapstructure:"host_keys"` + DefaultHost *string `mapstructure:"default_host,omitempty"` + ForwardPort uint16 `mapstructure:"forward_port"` + Auth types.Auth `mapstructure:"auth"` + KeepAlive types.KeepAlive `mapstructure:"keepalive"` + DomainExtractRegex string `mapstructure:"domain_extract_regex" validate:"validregexp"` + ReadRawPackets bool `mapstructure:"read_raw_packets"` + ReadSessionsOutput *bool `mapstructure:"read_sessions_output"` + Mode types.DomainMode `mapstructure:"mode" validate:"required,oneof=single multi"` + FakeRemoteHost bool `mapstructure:"fake_remote_host"` + Shell *sshtun.BoolOrStr `mapstructure:"shell"` + ClientVersion string `mapstructure:"client_version"` + Commands types.Commands `mapstructure:"commands"` } func (p *Params) Validate() error { if err := util.Validator.Struct(p); err != nil { return err } - if p.NoPTY && (len(p.Commands.OnConnect) > 0 || len(p.Commands.OnDisconnect) > 0) { - return errors.New("commands aren't available without PTY (nopty = true)") - } return p.Auth.Validate() } diff --git a/internal/server/driver/ssh/sshtun/connect.go b/internal/server/driver/ssh/sshtun/connect.go index 9cd84b0..35e3b72 100644 --- a/internal/server/driver/ssh/sshtun/connect.go +++ b/internal/server/driver/ssh/sshtun/connect.go @@ -28,8 +28,7 @@ type Tunnel struct { type TunnelConfig struct { Forward Forward HostKeyCallback ssh.HostKeyCallback - NoPTY bool - Shell BoolOrStr + Shell *BoolOrStr ClientVersion string FakeRemoteHost bool KeepAliveInterval uint @@ -38,16 +37,27 @@ type TunnelConfig struct { type BoolOrStr string -func (b BoolOrStr) IsBool() bool { - return b == "true" || b == "false" || b == "1" || b == "0" || b == "" +func (b *BoolOrStr) IsBool() bool { + if b == nil { + return false + } + v := *b + return v == "true" || v == "false" || v == "1" || v == "0" || v == "" } -func (b BoolOrStr) Falsy() bool { - return b == "" || b == "0" || b == "false" +func (b *BoolOrStr) Falsy() bool { + if b == nil { + return true + } + v := *b + return v == "" || v == "0" || v == "false" } -func (b BoolOrStr) String() string { - return string(b) +func (b *BoolOrStr) String() string { + if b == nil { + return "" + } + return string(*b) } func New(address, user string, auth []ssh.AuthMethod, sc TunnelConfig, log *zap.SugaredLogger) *Tunnel { @@ -127,17 +137,8 @@ func (t *Tunnel) connect(ctx context.Context, sessionCb = func(*ssh.Session) {} } - if !t.tunConfig.NoPTY { - err = sess.RequestPty("xterm", 80, 40, ssh.TerminalModes{ - ssh.ECHO: 0, - ssh.IGNCR: 1, - }) - if err != nil { - t.log.Warnf("PTY allocation failed: %s", err.Error()) - } - } - if !t.tunConfig.Shell.Falsy() { - if t.tunConfig.Shell.IsBool() { + if t.tunConfig.Shell == nil || !t.tunConfig.Shell.Falsy() { + if t.tunConfig.Shell == nil || t.tunConfig.Shell.IsBool() { if err := sess.Shell(); err != nil { t.log.Warnf("failed to start empty shell: %s", err.Error()) }