Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dynamic Port Forwarding #2261

Merged
merged 3 commits into from Oct 15, 2018
Merged

Dynamic Port Forwarding #2261

merged 3 commits into from Oct 15, 2018

Conversation

russjones
Copy link
Contributor

Purpose

Build off the work of @steven-aerts in #1694 and add support for dynamic port forwarding (SOCKS5).

Implementation

  • Add the -D flag to tsh.
  • Add support for a SOCKS5 handshake to discover the remote address to proxy.
  • Proxy connections using a direct-tcpip channel.

Related Issues

Fixes #1693
Fixes #1694

@@ -476,6 +512,11 @@ func (c *Config) LoadProfile(profileDir string, proxyName string) error {
log.Warnf("Error parsing user profile: %v", err)
}

c.DynamicForwardedPorts, err = ParseDynamicPortForwardSpec(cp.DynamicForwardedPorts)
if err != nil {
log.Warnf("Error parsing user profile: %v", err)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing dot. Also it's a warning, but log message says error what is not consistent.

@@ -834,6 +875,15 @@ func (tc *TeleportClient) startPortForwarding(nodeClient *NodeClient) error {
go nodeClient.listenAndForward(socket, net.JoinHostPort(fp.DestHost, strconv.Itoa(fp.DestPort)))
}
}
if len(tc.Config.DynamicForwardedPorts) > 0 {
for _, fp := range tc.Config.DynamicForwardedPorts {
socket, err := net.Listen("tcp", net.JoinHostPort(fp.SrcIP, strconv.Itoa(fp.SrcPort)))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you created 3 listeners and failed to bind on 4th listener, remaining 3 listeners will linger and leak? Can you make sure the code closes the listeners on client close or error return (I would use contexts)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a better UX would be to just log that we failed to bind to a particular address but continue to bind to the ones that we can.

What do you think?


// ToStringSpec returns the same string spec which can be parsed by
// ParseDynamicPortForwardSpec.
func (fp DynamicForwardedPorts) ToStringSpec() (retval []string) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

String will work


// ParseDynamicPortForwardSpec parses parameter to -D flag, i.e. strings like "remote.host:3000"
// The opposite of this function (spec generation) is DynamicForwardPorts.ToStringSpec.
func ParseDynamicPortForwardSpec(spec []string) (ports DynamicForwardedPorts, err error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's not use named return values in go in larger functions without a good reason:

https://github.com/golang/go/wiki/CodeReviewComments#named-result-parameters

Naked returns are okay if the function is a handful of lines. Once it's a medium sized function, be explicit with your return values. Corollary: it's not worth it to name result parameters just because it enables you to use named returns. Clarity of docs is always more important than saving a line or two in your function.

for i, str := range spec {
parts := strings.Split(str, ":")
if len(parts) > 2 {
return nil, fmt.Errorf(errTemplate, str)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

replace fmt.Errorf with trace here and everywhere else. Error message should be compliant with our style (should not start with upper case and have dots, it's not a full sentence.)

log.Debugf("SOCKS5 proxy forwarding requests to %v.", remoteAddr)

// Proxy the connection.
go c.proxyConnection(conn, remoteAddr)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use context.

limitations under the License.
*/

package socks
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing package level documentation


"github.com/gravitational/teleport"

"github.com/gravitational/trace"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logrus and trace are both external deps, so extra line is not necessary

}
return int(len), nil
default:
return 0, trace.BadParameter("unsupported address type")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

include the type that is not supported in the error

// Write the response to the client. Similar to OpenSSH only success is written.
// https://github.com/openssh/openssh-portable/blob/5d14019/channels.c#L1442-L1452
func writeReply(conn net.Conn) error {
// Write success reply.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

redundant comment line, it's clear what the function does

@russjones russjones force-pushed the rjones/socks5 branch 4 times, most recently from feb062d to afe29b6 Compare October 5, 2018 23:28
Copy link
Contributor

@klizhentas klizhentas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

got one last note

log.Errorf("Connection attempt %v: %v", attempt, err)
// failed to establish an outbound connection? try again:
time.Sleep(time.Millisecond * time.Duration(100*attempt))
func (client *NodeClient) proxyConnection(ctx context.Context, incoming net.Conn, remoteAddr string) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find it confusing to have two identical functions with similar names proxyConn and proxyConnection with only difference being the first one returning error vs. the other one not.

Just use the local closure instead of proxyConnection function.

// and proxy them to 'remoteAddr'

// Proxy the connection to the remote address.
go c.proxyConnection(ctx, conn, remoteAddr)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use closure here

log.Debugf("SOCKS5 proxy forwarding requests to %v.", remoteAddr)

// Proxy the connection.
go c.proxyConnection(ctx, conn, remoteAddr)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and here

steven-aerts and others added 3 commits October 15, 2018 11:33
Implements the -D flag of ssh, but only with a SOCKS5 proxy.
OpenSSH also supports a SOCKS4 proxy.

This commit also fixes a bug in the server which prevented it from
forwarding raw IPv6 sockets, as the addresses were incorrectly
escaped.
@russjones russjones merged commit 7dd0533 into master Oct 15, 2018
@russjones russjones deleted the rjones/socks5 branch October 15, 2018 18:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants