mirror of
https://github.com/gravitational/teleport
synced 2024-10-22 18:23:25 +00:00
f8dba76147
This commit aadds multiplexer library of SSH/TLS on the same listener socket. The multiplexer detects the protocol by the first 3 bytes of the incoming connection and forwards wrapped connection either to the SSH ot TLS listeners. The library also supports PROXY line protocol and wraps connection information with connection details from the proxy line received by the server
125 lines
3.4 KiB
Go
125 lines
3.4 KiB
Go
/**
|
|
* Copyright 2013 Rackspace
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
* Note: original copyright is preserved on purpose
|
|
*/
|
|
|
|
package multiplexer
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"net"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/gravitational/trace"
|
|
)
|
|
|
|
const (
|
|
// TCP4 is TCP over IPv4
|
|
TCP4 = "TCP4"
|
|
// TCP6 is tCP over IPv6
|
|
TCP6 = "TCP6"
|
|
// Unknown is unsupported or unknown protocol
|
|
UNKNOWN = "UNKNOWN"
|
|
)
|
|
|
|
var (
|
|
proxyCRLF = "\r\n"
|
|
proxySep = " "
|
|
)
|
|
|
|
// ProxyLine is HA Proxy protocol version 1
|
|
// https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
|
|
// Original implementation here: https://github.com/racker/go-proxy-protocol
|
|
type ProxyLine struct {
|
|
Protocol string
|
|
Source net.TCPAddr
|
|
Destination net.TCPAddr
|
|
}
|
|
|
|
// String returns on-the wire string representation of the proxy line
|
|
func (p *ProxyLine) String() string {
|
|
return fmt.Sprintf("PROXY %s %s %s %d %d\r\n", p.Protocol, p.Source.IP.String(), p.Destination.IP.String(), p.Source.Port, p.Destination.Port)
|
|
}
|
|
|
|
// ReadProxyLine reads proxy line protocol from the reader
|
|
func ReadProxyLine(reader *bufio.Reader) (*ProxyLine, error) {
|
|
line, err := reader.ReadString('\n')
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
if !strings.HasSuffix(line, proxyCRLF) {
|
|
return nil, trace.BadParameter("expected CRLF in proxy protocol, got something else")
|
|
}
|
|
tokens := strings.Split(line[:len(line)-2], proxySep)
|
|
ret := ProxyLine{}
|
|
if len(tokens) < 6 {
|
|
return nil, trace.BadParameter("malformed PROXY line protocol string")
|
|
}
|
|
switch tokens[1] {
|
|
case TCP4:
|
|
ret.Protocol = TCP4
|
|
case TCP6:
|
|
ret.Protocol = TCP6
|
|
default:
|
|
ret.Protocol = UNKNOWN
|
|
}
|
|
sourceIP, err := parseIP(ret.Protocol, tokens[2])
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
destIP, err := parseIP(ret.Protocol, tokens[3])
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
sourcePort, err := parsePortNumber(tokens[4])
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
destPort, err := parsePortNumber(tokens[5])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
ret.Source = net.TCPAddr{IP: sourceIP, Port: sourcePort}
|
|
ret.Destination = net.TCPAddr{IP: destIP, Port: destPort}
|
|
return &ret, nil
|
|
}
|
|
|
|
func parsePortNumber(portString string) (int, error) {
|
|
port, err := strconv.Atoi(portString)
|
|
if err != nil {
|
|
return -1, trace.BadParameter("bad port %q: %v", port, err)
|
|
}
|
|
if port < 0 || port > 65535 {
|
|
return -1, trace.BadParameter("port %q not in supported range [0...65535]", portString)
|
|
}
|
|
return port, nil
|
|
}
|
|
|
|
func parseIP(protocol string, addrString string) (net.IP, error) {
|
|
addr := net.ParseIP(addrString)
|
|
switch {
|
|
case len(addr) == 0:
|
|
return nil, trace.BadParameter("failed to parse address")
|
|
case addr.To4() != nil && protocol != TCP4:
|
|
return nil, trace.BadParameter("got IPV4 address %q for IPV6 proto %q", addr.String(), protocol)
|
|
case addr.To4() == nil && protocol == TCP6:
|
|
return nil, trace.BadParameter("got IPV6 address %v %q for IPV4 proto %q", len(addr), addr.String(), protocol)
|
|
}
|
|
return addr, nil
|
|
}
|