{"action":"created","comment":{"url":"https://api.github.com/repos/gravitational/teleport/pulls/comments/94264942","pull_request_review_id":14796205,"id":94264942,"diff_hunk":"@@ -297,3 +342,85 @@ func KeysEqual(ak, bk ssh.PublicKey) bool {\n \tb := ssh.Marshal(bk)\n \treturn (len(a) == len(b) && subtle.ConstantTimeCompare(a, b) == 1)\n }\n+\n+// HandshakePayload structure is sent as a JSON blob by the teleport\n+// proxy to every SSH server who identifies itself as Teleport server\n+//\n+// It allows teleport proxies to communicate additional data to server\n+type HandshakePayload struct {\n+\t// ClientAddr is the IP address of the remote client\n+\tClientAddr string `json:\"clientAddr,omitempty\"`\n+}\n+\n+// connectionWrapper allows the SSH server to perform custom handshake which\n+// allows teleport proxy servers to relay the remote IP address of the client\n+// to the SSH server (otherwise connection.RemoteAddr would always point to\n+// a proxy, which is useless for audit purposes)\n+type connectionWrapper struct {\n+\tnet.Conn\n+\n+\t// upstreamReader reads from the underlying (wrapped) connection\n+\tupstreamReader io.Reader\n+\n+\t// clientAddr points to the true client address (client is behind\n+\t// a proxy). Keeping this address is the entire point of the\n+\t// connection wrapper.\n+\tclientAddr net.Addr\n+}\n+\n+// RemoteAddr returns the behind-the-proxy client address\n+func (c *connectionWrapper) RemoteAddr() net.Addr {\n+\treturn c.clientAddr\n+}\n+\n+// Read implements io.Read() part of net.Connection which allows the\n+// connection wrapper to peek at the beginning of SSH handshake\n+func (c *connectionWrapper) Read(b []byte) (int, error) {\n+\tif c.upstreamReader == nil {","path":"lib/sshutils/server.go","position":179,"original_position":179,"commit_id":"baaa3eaf37cb15c631a68f6512ef29b15f27f3bb","original_commit_id":"baaa3eaf37cb15c631a68f6512ef29b15f27f3bb","user":{"login":"klizhentas","id":13717,"avatar_url":"https://avatars.githubusercontent.com/u/13717?v=3","gravatar_id":"","url":"https://api.github.com/users/klizhentas","html_url":"https://github.com/klizhentas","followers_url":"https://api.github.com/users/klizhentas/followers","following_url":"https://api.github.com/users/klizhentas/following{/other_user}","gists_url":"https://api.github.com/users/klizhentas/gists{/gist_id}","starred_url":"https://api.github.com/users/klizhentas/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/klizhentas/subscriptions","organizations_url":"https://api.github.com/users/klizhentas/orgs","repos_url":"https://api.github.com/users/klizhentas/repos","events_url":"https://api.github.com/users/klizhentas/events{/privacy}","received_events_url":"https://api.github.com/users/klizhentas/received_events","type":"User","site_admin":false},"body":"to reduce nesting, let's invert this:\r\n\r\n```go\r\nif c.upstreamReader ! = nil {\r\n    return ...\r\n}\r\n... rest of code here\r\n}","created_at":"2016-12-30T23:53:07Z","updated_at":"2016-12-30T23:53:07Z","html_url":"https://github.com/gravitational/teleport/pull/681#discussion_r94264942","pull_request_url":"https://api.github.com/repos/gravitational/teleport/pulls/681","_links":{"self":{"href":"https://api.github.com/repos/gravitational/teleport/pulls/comments/94264942"},"html":{"href":"https://github.com/gravitational/teleport/pull/681#discussion_r94264942"},"pull_request":{"href":"https://api.github.com/repos/gravitational/teleport/pulls/681"}}},"pull_request":{"url":"https://api.github.com/repos/gravitational/teleport/pulls/681","id":99786482,"html_url":"https://github.com/gravitational/teleport/pull/681","diff_url":"https://github.com/gravitational/teleport/pull/681.diff","patch_url":"https://github.com/gravitational/teleport/pull/681.patch","issue_url":"https://api.github.com/repos/gravitational/teleport/issues/681","number":681,"state":"open","locked":false,"title":"Networking Improvements","user":{"login":"kontsevoy","id":461020,"avatar_url":"https://avatars.githubusercontent.com/u/461020?v=3","gravatar_id":"","url":"https://api.github.com/users/kontsevoy","html_url":"https://github.com/kontsevoy","followers_url":"https://api.github.com/users/kontsevoy/followers","following_url":"https://api.github.com/users/kontsevoy/following{/other_user}","gists_url":"https://api.github.com/users/kontsevoy/gists{/gist_id}","starred_url":"https://api.github.com/users/kontsevoy/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kontsevoy/subscriptions","organizations_url":"https://api.github.com/users/kontsevoy/orgs","repos_url":"https://api.github.com/users/kontsevoy/repos","events_url":"https://api.github.com/users/kontsevoy/events{/privacy}","received_events_url":"https://api.github.com/users/kontsevoy/received_events","type":"User","site_admin":false},"body":"## Problem 1: Idle Connections\r\n\r\nUp to this day Teleport did not use read/write timeouts for SSH connections. This can lead to frozen connections. A nicer connection handling would set an \"idle timeout\" on every connection which means it will be dropped after a certain idle interval passes.\r\n\r\n## Problem 2: Real Client IP\r\n\r\nFor audit purposes it is often required to log the IP of every connected SSH client. However, if an SSH bastion is used, it's the address of the bastion what gets logged as \"client\". We need to find a way to log the real actual client IP into the audit log.\r\n\r\n## Changes\r\n\r\nThere are 3 commits here:\r\n\r\n1. Refactoring: added comments, renamed a few things, eliminated dead code. Preparing Teleport for a new developer. ;) Commit 2035ace86\r\n2. Idle connection handling. Commit ed8604f75\r\n3. True client IP forwarding. Commit 4f009e225 (more on this below)\r\n\r\n## Client IP Forwarding Algorithm\r\n\r\nThere is no standard \"SSH way\" to forward the true client IP to a server. The initial conditions are:\r\n* The client does not know its own IP, it's almost always behind NAT.\r\n* Only proxy/bastion knows the external client IP.\r\n* But the proxy does not have the credentials to communicate this information to the SSH server (proxy is a classic unauthenticated man-in-the-middle here).\r\n* We also need to make sure we don't break compatibility with OpenSSH clients and OpenSSH servers.\r\n\r\nThe implemented algo works by proxy injecting a JSON payload in the middle of \"SSH version Exchange\" (see corresponding [RFC](https://tools.ietf.org/html/rfc4253#page-4))\r\n\r\nThe version exchange with a Teleport server is shown below as seen on the wire. It begins by server sending its version string first:\r\n```\r\nserver-to-proxy > SSH-2.0-Teleport\r\nproxy-to-client > SSH-2.0-Teleport\r\nclient-to-proxy > SSH-2.0-Client\r\nproxy-to-server > JSON payload\r\nproxy-to-server > SSH-2.0-Client\r\n```\r\n\r\nand here's what happens with OpenSSH server (or with old Teleport versions):\r\n```\r\nserver-to-proxy > SSH-2.0-Go\r\nproxy-to-client > SSH-2.0-Go\r\nclient-to-proxy > SSH-2.0-Client\r\nproxy-to-server > SSH-2.0-Client\r\n```\r\n\r\nin other words, when a proxy sees that a server sends \"SSH-2.0-Teleport\" version string, it _knows_ that the server is capable of receiving a custom JSON payload right after the SSH version exchange.\r\n\r\n\r\n\r\n\r\n","created_at":"2016-12-30T22:04:41Z","updated_at":"2016-12-30T23:53:07Z","closed_at":null,"merged_at":null,"merge_commit_sha":"4f1d6db03301f5ca7a1481c0e362c4e07aea9295","assignee":{"login":"klizhentas","id":13717,"avatar_url":"https://avatars.githubusercontent.com/u/13717?v=3","gravatar_id":"","url":"https://api.github.com/users/klizhentas","html_url":"https://github.com/klizhentas","followers_url":"https://api.github.com/users/klizhentas/followers","following_url":"https://api.github.com/users/klizhentas/following{/other_user}","gists_url":"https://api.github.com/users/klizhentas/gists{/gist_id}","starred_url":"https://api.github.com/users/klizhentas/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/klizhentas/subscriptions","organizations_url":"https://api.github.com/users/klizhentas/orgs","repos_url":"https://api.github.com/users/klizhentas/repos","events_url":"https://api.github.com/users/klizhentas/events{/privacy}","received_events_url":"https://api.github.com/users/klizhentas/received_events","type":"User","site_admin":false},"assignees":[{"login":"klizhentas","id":13717,"avatar_url":"https://avatars.githubusercontent.com/u/13717?v=3","gravatar_id":"","url":"https://api.github.com/users/klizhentas","html_url":"https://github.com/klizhentas","followers_url":"https://api.github.com/users/klizhentas/followers","following_url":"https://api.github.com/users/klizhentas/following{/other_user}","gists_url":"https://api.github.com/users/klizhentas/gists{/gist_id}","starred_url":"https://api.github.com/users/klizhentas/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/klizhentas/subscriptions","organizations_url":"https://api.github.com/users/klizhentas/orgs","repos_url":"https://api.github.com/users/klizhentas/repos","events_url":"https://api.github.com/users/klizhentas/events{/privacy}","received_events_url":"https://api.github.com/users/klizhentas/received_events","type":"User","site_admin":false}],"milestone":null,"commits_url":"https://api.github.com/repos/gravitational/teleport/pulls/681/commits","review_comments_url":"https://api.github.com/repos/gravitational/teleport/pulls/681/comments","review_comment_url":"https://api.github.com/repos/gravitational/teleport/pulls/comments{/number}","comments_url":"https://api.github.com/repos/gravitational/teleport/issues/681/comments","statuses_url":"https://api.github.com/repos/gravitational/teleport/statuses/baaa3eaf37cb15c631a68f6512ef29b15f27f3bb","head":{"label":"gravitational:ev/net","ref":"ev/net","sha":"baaa3eaf37cb15c631a68f6512ef29b15f27f3bb","user":{"login":"gravitational","id":10781132,"avatar_url":"https://avatars.githubusercontent.com/u/10781132?v=3","gravatar_id":"","url":"https://api.github.com/users/gravitational","html_url":"https://github.com/gravitational","followers_url":"https://api.github.com/users/gravitational/followers","following_url":"https://api.github.com/users/gravitational/following{/other_user}","gists_url":"https://api.github.com/users/gravitational/gists{/gist_id}","starred_url":"https://api.github.com/users/gravitational/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gravitational/subscriptions","organizations_url":"https://api.github.com/users/gravitational/orgs","repos_url":"https://api.github.com/users/gravitational/repos","events_url":"https://api.github.com/users/gravitational/events{/privacy}","received_events_url":"https://api.github.com/users/gravitational/received_events","type":"Organization","site_admin":false},"repo":{"id":31558937,"name":"teleport","full_name":"gravitational/teleport","owner":{"login":"gravitational","id":10781132,"avatar_url":"https://avatars.githubusercontent.com/u/10781132?v=3","gravatar_id":"","url":"https://api.github.com/users/gravitational","html_url":"https://github.com/gravitational","followers_url":"https://api.github.com/users/gravitational/followers","following_url":"https://api.github.com/users/gravitational/following{/other_user}","gists_url":"https://api.github.com/users/gravitational/gists{/gist_id}","starred_url":"https://api.github.com/users/gravitational/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gravitational/subscriptions","organizations_url":"https://api.github.com/users/gravitational/orgs","repos_url":"https://api.github.com/users/gravitational/repos","events_url":"https://api.github.com/users/gravitational/events{/privacy}","received_events_url":"https://api.github.com/users/gravitational/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/gravitational/teleport","description":"Modern SSH server for clusters and teams.","fork":false,"url":"https://api.github.com/repos/gravitational/teleport","forks_url":"https://api.github.com/repos/gravitational/teleport/forks","keys_url":"https://api.github.com/repos/gravitational/teleport/keys{/key_id}","collaborators_url":"https://api.github.com/repos/gravitational/teleport/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/gravitational/teleport/teams","hooks_url":"https://api.github.com/repos/gravitational/teleport/hooks","issue_events_url":"https://api.github.com/repos/gravitational/teleport/issues/events{/number}","events_url":"https://api.github.com/repos/gravitational/teleport/events","assignees_url":"https://api.github.com/repos/gravitational/teleport/assignees{/user}","branches_url":"https://api.github.com/repos/gravitational/teleport/branches{/branch}","tags_url":"https://api.github.com/repos/gravitational/teleport/tags","blobs_url":"https://api.github.com/repos/gravitational/teleport/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/gravitational/teleport/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/gravitational/teleport/git/refs{/sha}","trees_url":"https://api.github.com/repos/gravitational/teleport/git/trees{/sha}","statuses_url":"https://api.github.com/repos/gravitational/teleport/statuses/{sha}","languages_url":"https://api.github.com/repos/gravitational/teleport/languages","stargazers_url":"https://api.github.com/repos/gravitational/teleport/stargazers","contributors_url":"https://api.github.com/repos/gravitational/teleport/contributors","subscribers_url":"https://api.github.com/repos/gravitational/teleport/subscribers","subscription_url":"https://api.github.com/repos/gravitational/teleport/subscription","commits_url":"https://api.github.com/repos/gravitational/teleport/commits{/sha}","git_commits_url":"https://api.github.com/repos/gravitational/teleport/git/commits{/sha}","comments_url":"https://api.github.com/repos/gravitational/teleport/comments{/number}","issue_comment_url":"https://api.github.com/repos/gravitational/teleport/issues/comments{/number}","contents_url":"https://api.github.com/repos/gravitational/teleport/contents/{+path}","compare_url":"https://api.github.com/repos/gravitational/teleport/compare/{base}...{head}","merges_url":"https://api.github.com/repos/gravitational/teleport/merges","archive_url":"https://api.github.com/repos/gravitational/teleport/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/gravitational/teleport/downloads","issues_url":"https://api.github.com/repos/gravitational/teleport/issues{/number}","pulls_url":"https://api.github.com/repos/gravitational/teleport/pulls{/number}","milestones_url":"https://api.github.com/repos/gravitational/teleport/milestones{/number}","notifications_url":"https://api.github.com/repos/gravitational/teleport/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/gravitational/teleport/labels{/name}","releases_url":"https://api.github.com/repos/gravitational/teleport/releases{/id}","deployments_url":"https://api.github.com/repos/gravitational/teleport/deployments","created_at":"2015-03-02T19:36:00Z","updated_at":"2016-12-30T21:54:16Z","pushed_at":"2016-12-30T23:13:59Z","git_url":"git://github.com/gravitational/teleport.git","ssh_url":"git@github.com:gravitational/teleport.git","clone_url":"https://github.com/gravitational/teleport.git","svn_url":"https://github.com/gravitational/teleport","homepage":"http://gravitational.com/teleport","size":32376,"stargazers_count":3692,"watchers_count":3692,"language":"Go","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":161,"mirror_url":null,"open_issues_count":29,"forks":161,"open_issues":29,"watchers":3692,"default_branch":"master"}},"base":{"label":"gravitational:master","ref":"master","sha":"ca5e8d53db34ed7c24f9bcc3693de98b7ab397f3","user":{"login":"gravitational","id":10781132,"avatar_url":"https://avatars.githubusercontent.com/u/10781132?v=3","gravatar_id":"","url":"https://api.github.com/users/gravitational","html_url":"https://github.com/gravitational","followers_url":"https://api.github.com/users/gravitational/followers","following_url":"https://api.github.com/users/gravitational/following{/other_user}","gists_url":"https://api.github.com/users/gravitational/gists{/gist_id}","starred_url":"https://api.github.com/users/gravitational/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gravitational/subscriptions","organizations_url":"https://api.github.com/users/gravitational/orgs","repos_url":"https://api.github.com/users/gravitational/repos","events_url":"https://api.github.com/users/gravitational/events{/privacy}","received_events_url":"https://api.github.com/users/gravitational/received_events","type":"Organization","site_admin":false},"repo":{"id":31558937,"name":"teleport","full_name":"gravitational/teleport","owner":{"login":"gravitational","id":10781132,"avatar_url":"https://avatars.githubusercontent.com/u/10781132?v=3","gravatar_id":"","url":"https://api.github.com/users/gravitational","html_url":"https://github.com/gravitational","followers_url":"https://api.github.com/users/gravitational/followers","following_url":"https://api.github.com/users/gravitational/following{/other_user}","gists_url":"https://api.github.com/users/gravitational/gists{/gist_id}","starred_url":"https://api.github.com/users/gravitational/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gravitational/subscriptions","organizations_url":"https://api.github.com/users/gravitational/orgs","repos_url":"https://api.github.com/users/gravitational/repos","events_url":"https://api.github.com/users/gravitational/events{/privacy}","received_events_url":"https://api.github.com/users/gravitational/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/gravitational/teleport","description":"Modern SSH server for clusters and teams.","fork":false,"url":"https://api.github.com/repos/gravitational/teleport","forks_url":"https://api.github.com/repos/gravitational/teleport/forks","keys_url":"https://api.github.com/repos/gravitational/teleport/keys{/key_id}","collaborators_url":"https://api.github.com/repos/gravitational/teleport/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/gravitational/teleport/teams","hooks_url":"https://api.github.com/repos/gravitational/teleport/hooks","issue_events_url":"https://api.github.com/repos/gravitational/teleport/issues/events{/number}","events_url":"https://api.github.com/repos/gravitational/teleport/events","assignees_url":"https://api.github.com/repos/gravitational/teleport/assignees{/user}","branches_url":"https://api.github.com/repos/gravitational/teleport/branches{/branch}","tags_url":"https://api.github.com/repos/gravitational/teleport/tags","blobs_url":"https://api.github.com/repos/gravitational/teleport/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/gravitational/teleport/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/gravitational/teleport/git/refs{/sha}","trees_url":"https://api.github.com/repos/gravitational/teleport/git/trees{/sha}","statuses_url":"https://api.github.com/repos/gravitational/teleport/statuses/{sha}","languages_url":"https://api.github.com/repos/gravitational/teleport/languages","stargazers_url":"https://api.github.com/repos/gravitational/teleport/stargazers","contributors_url":"https://api.github.com/repos/gravitational/teleport/contributors","subscribers_url":"https://api.github.com/repos/gravitational/teleport/subscribers","subscription_url":"https://api.github.com/repos/gravitational/teleport/subscription","commits_url":"https://api.github.com/repos/gravitational/teleport/commits{/sha}","git_commits_url":"https://api.github.com/repos/gravitational/teleport/git/commits{/sha}","comments_url":"https://api.github.com/repos/gravitational/teleport/comments{/number}","issue_comment_url":"https://api.github.com/repos/gravitational/teleport/issues/comments{/number}","contents_url":"https://api.github.com/repos/gravitational/teleport/contents/{+path}","compare_url":"https://api.github.com/repos/gravitational/teleport/compare/{base}...{head}","merges_url":"https://api.github.com/repos/gravitational/teleport/merges","archive_url":"https://api.github.com/repos/gravitational/teleport/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/gravitational/teleport/downloads","issues_url":"https://api.github.com/repos/gravitational/teleport/issues{/number}","pulls_url":"https://api.github.com/repos/gravitational/teleport/pulls{/number}","milestones_url":"https://api.github.com/repos/gravitational/teleport/milestones{/number}","notifications_url":"https://api.github.com/repos/gravitational/teleport/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/gravitational/teleport/labels{/name}","releases_url":"https://api.github.com/repos/gravitational/teleport/releases{/id}","deployments_url":"https://api.github.com/repos/gravitational/teleport/deployments","created_at":"2015-03-02T19:36:00Z","updated_at":"2016-12-30T21:54:16Z","pushed_at":"2016-12-30T23:13:59Z","git_url":"git://github.com/gravitational/teleport.git","ssh_url":"git@github.com:gravitational/teleport.git","clone_url":"https://github.com/gravitational/teleport.git","svn_url":"https://github.com/gravitational/teleport","homepage":"http://gravitational.com/teleport","size":32376,"stargazers_count":3692,"watchers_count":3692,"language":"Go","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":161,"mirror_url":null,"open_issues_count":29,"forks":161,"open_issues":29,"watchers":3692,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/gravitational/teleport/pulls/681"},"html":{"href":"https://github.com/gravitational/teleport/pull/681"},"issue":{"href":"https://api.github.com/repos/gravitational/teleport/issues/681"},"comments":{"href":"https://api.github.com/repos/gravitational/teleport/issues/681/comments"},"review_comments":{"href":"https://api.github.com/repos/gravitational/teleport/pulls/681/comments"},"review_comment":{"href":"https://api.github.com/repos/gravitational/teleport/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/gravitational/teleport/pulls/681/commits"},"statuses":{"href":"https://api.github.com/repos/gravitational/teleport/statuses/baaa3eaf37cb15c631a68f6512ef29b15f27f3bb"}}}}
{"actor":{"display_login":"klizhentas"}}