Join address for web, reverse tunnel, fixes #1544

Support configuration for web and reverse tunnel
proxies to listen on the same port.

* Default config are not changed for backwards compatibility.
* If administrator configures web and reverse tunnel
addresses to be on the same port, multiplexing is turned on
* In trusted clusters configuration reverse_tunnel_addr
defaults to web_addr.
This commit is contained in:
Sasha Klizhentas 2018-01-05 16:20:56 -08:00
parent 19a6e5ed4b
commit ef473d809e
12 changed files with 463 additions and 178 deletions

View file

@ -108,45 +108,63 @@ func (s *InstanceSecrets) String() string {
return string(bytes)
}
// InstanceConfig is an instance configuration
type InstanceConfig struct {
// ClusterName is a cluster name of the instance
ClusterName string
// HostID is a host id of the instance
HostID string
// NodeName is a node name of the instance
NodeName string
// Ports is a list of assigned ports to use
Ports []int
// Priv is SSH private key of the instance
Priv []byte
// Pub is SSH public key of the instance
Pub []byte
// MultiplexProxy uses the same port for web and SSH reverse tunnel proxy
MultiplexProxy bool
}
// NewInstance creates a new Teleport process instance
func NewInstance(clusterName string, hostID string, nodeName string, ports []int, priv, pub []byte) *TeleInstance {
func NewInstance(cfg InstanceConfig) *TeleInstance {
var err error
if len(ports) < 5 {
fatalIf(fmt.Errorf("not enough free ports given: %v", ports))
if len(cfg.Ports) < 5 {
fatalIf(fmt.Errorf("not enough free ports given: %v", cfg.Ports))
}
if nodeName == "" {
nodeName, err = os.Hostname()
if cfg.NodeName == "" {
cfg.NodeName, err = os.Hostname()
fatalIf(err)
}
// generate instance secrets (keys):
keygen := native.New()
if priv == nil || pub == nil {
priv, pub, _ = keygen.GenerateKeyPair("")
if cfg.Priv == nil || cfg.Pub == nil {
cfg.Priv, cfg.Pub, _ = keygen.GenerateKeyPair("")
}
rsaKey, err := ssh.ParseRawPrivateKey(priv)
rsaKey, err := ssh.ParseRawPrivateKey(cfg.Priv)
fatalIf(err)
tlsCAKey, tlsCACert, err := tlsca.GenerateSelfSignedCAWithPrivateKey(rsaKey.(*rsa.PrivateKey), pkix.Name{
CommonName: clusterName,
Organization: []string{clusterName},
CommonName: cfg.ClusterName,
Organization: []string{cfg.ClusterName},
}, nil, defaults.CATTL)
fatalIf(err)
cert, err := keygen.GenerateHostCert(services.HostCertParams{
PrivateCASigningKey: priv,
PublicHostKey: pub,
HostID: hostID,
NodeName: nodeName,
ClusterName: clusterName,
PrivateCASigningKey: cfg.Priv,
PublicHostKey: cfg.Pub,
HostID: cfg.HostID,
NodeName: cfg.NodeName,
ClusterName: cfg.ClusterName,
Roles: teleport.Roles{teleport.RoleAdmin},
TTL: time.Duration(time.Hour * 24),
})
fatalIf(err)
tlsCA, err := tlsca.New(tlsCACert, tlsCAKey)
fatalIf(err)
cryptoPubKey, err := sshutils.CryptoPublicKey(pub)
cryptoPubKey, err := sshutils.CryptoPublicKey(cfg.Pub)
identity := tlsca.Identity{
Username: fmt.Sprintf("%v.%v", hostID, clusterName),
Username: fmt.Sprintf("%v.%v", cfg.HostID, cfg.ClusterName),
Groups: []string{string(teleport.RoleAdmin)},
}
clock := clockwork.NewRealClock()
@ -159,20 +177,23 @@ func NewInstance(clusterName string, hostID string, nodeName string, ports []int
fatalIf(err)
i := &TeleInstance{
Ports: ports,
Hostname: nodeName,
Ports: cfg.Ports,
Hostname: cfg.NodeName,
}
secrets := InstanceSecrets{
SiteName: clusterName,
PrivKey: priv,
PubKey: pub,
SiteName: cfg.ClusterName,
PrivKey: cfg.Priv,
PubKey: cfg.Pub,
Cert: cert,
TLSCACert: tlsCACert,
TLSCert: tlsCert,
ListenAddr: net.JoinHostPort(nodeName, strconv.Itoa(ports[4])),
WebProxyAddr: net.JoinHostPort(nodeName, i.GetPortWeb()),
ListenAddr: net.JoinHostPort(cfg.NodeName, i.GetPortReverseTunnel()),
WebProxyAddr: net.JoinHostPort(cfg.NodeName, i.GetPortWeb()),
Users: make(map[string]*User),
}
if cfg.MultiplexProxy {
secrets.ListenAddr = secrets.WebProxyAddr
}
i.Secrets = secrets
return i
}
@ -258,6 +279,10 @@ func (i *TeleInstance) GetPortWeb() string {
return strconv.Itoa(i.Ports[3])
}
func (i *TeleInstance) GetPortReverseTunnel() string {
return strconv.Itoa(i.Ports[4])
}
// GetSiteAPI() is a helper which returns an API endpoint to a site with
// a given name. i endpoint implements HTTP-over-SSH access to the
// site's auth server.

View file

@ -114,7 +114,7 @@ func (s *IntSuite) SetUpSuite(c *check.C) {
// newTeleport helper returns a running Teleport instance pre-configured
// with the current user os.user.Current().
func (s *IntSuite) newTeleport(c *check.C, logins []string, enableSSH bool) *TeleInstance {
t := NewInstance(Site, HostID, Host, s.getPorts(5), s.priv, s.pub)
t := NewInstance(InstanceConfig{ClusterName: Site, HostID: HostID, NodeName: Host, Ports: s.getPorts(5), Priv: s.priv, Pub: s.pub})
// use passed logins, but use suite's default login if nothing was passed
if logins == nil || len(logins) == 0 {
logins = []string{s.me.Username}
@ -135,7 +135,7 @@ func (s *IntSuite) newTeleport(c *check.C, logins []string, enableSSH bool) *Tel
// Teleport instance with the passed in user, instance secrets, and Teleport
// configuration.
func (s *IntSuite) newTeleportWithConfig(c *check.C, logins []string, instanceSecrets []*InstanceSecrets, teleportConfig *service.Config) *TeleInstance {
t := NewInstance(Site, HostID, Host, s.getPorts(5), s.priv, s.pub)
t := NewInstance(InstanceConfig{ClusterName: Site, HostID: HostID, NodeName: Host, Ports: s.getPorts(5), Priv: s.priv, Pub: s.pub})
// use passed logins, but use suite's default login if nothing was passed
if logins == nil || len(logins) == 0 {
@ -617,8 +617,8 @@ func (s *IntSuite) TestTwoClusters(c *check.C) {
username := s.me.Username
a := NewInstance("site-A", HostID, Host, s.getPorts(5), s.priv, s.pub)
b := NewInstance("site-B", HostID, Host, s.getPorts(5), s.priv, s.pub)
a := NewInstance(InstanceConfig{ClusterName: "site-A", HostID: HostID, NodeName: Host, Ports: s.getPorts(5), Priv: s.priv, Pub: s.pub})
b := NewInstance(InstanceConfig{ClusterName: "site-B", HostID: HostID, NodeName: Host, Ports: s.getPorts(5), Priv: s.priv, Pub: s.pub})
a.AddUser(username, []string{username})
b.AddUser(username, []string{username})
@ -776,8 +776,8 @@ func (s *IntSuite) TestTwoClustersProxy(c *check.C) {
username := s.me.Username
a := NewInstance("site-A", HostID, Host, s.getPorts(5), s.priv, s.pub)
b := NewInstance("site-B", HostID, Host, s.getPorts(5), s.priv, s.pub)
a := NewInstance(InstanceConfig{ClusterName: "site-A", HostID: HostID, NodeName: Host, Ports: s.getPorts(5), Priv: s.priv, Pub: s.pub})
b := NewInstance(InstanceConfig{ClusterName: "site-B", HostID: HostID, NodeName: Host, Ports: s.getPorts(5), Priv: s.priv, Pub: s.pub})
a.AddUser(username, []string{username})
b.AddUser(username, []string{username})
@ -810,8 +810,8 @@ func (s *IntSuite) TestTwoClustersProxy(c *check.C) {
func (s *IntSuite) TestHA(c *check.C) {
username := s.me.Username
a := NewInstance("cluster-a", HostID, Host, s.getPorts(5), s.priv, s.pub)
b := NewInstance("cluster-b", HostID, Host, s.getPorts(5), s.priv, s.pub)
a := NewInstance(InstanceConfig{ClusterName: "cluster-a", HostID: HostID, NodeName: Host, Ports: s.getPorts(5), Priv: s.priv, Pub: s.pub})
b := NewInstance(InstanceConfig{ClusterName: "cluster-b", HostID: HostID, NodeName: Host, Ports: s.getPorts(5), Priv: s.priv, Pub: s.pub})
a.AddUser(username, []string{username})
b.AddUser(username, []string{username})
@ -879,8 +879,8 @@ func (s *IntSuite) TestMapRoles(c *check.C) {
clusterMain := "cluster-main"
clusterAux := "cluster-aux"
main := NewInstance(clusterMain, HostID, Host, s.getPorts(5), s.priv, s.pub)
aux := NewInstance(clusterAux, HostID, Host, s.getPorts(5), s.priv, s.pub)
main := NewInstance(InstanceConfig{ClusterName: clusterMain, HostID: HostID, NodeName: Host, Ports: s.getPorts(5), Priv: s.priv, Pub: s.pub})
aux := NewInstance(InstanceConfig{ClusterName: clusterAux, HostID: HostID, NodeName: Host, Ports: s.getPorts(5), Priv: s.priv, Pub: s.pub})
// main cluster has a local user and belongs to role "main-devs"
mainDevs := "main-devs"
@ -1067,15 +1067,25 @@ func (s *IntSuite) TestMapRoles(c *check.C) {
c.Assert(aux.Stop(true), check.IsNil)
}
// TestRemoteClusters tests disconnecting remote clusters
// using remote cluster feature
func (s *IntSuite) TestRemoteClusters(c *check.C) {
// TestTrustedClusters tests remote clusters scenarios
// using trusted clusters feature
func (s *IntSuite) TestTrustedClusters(c *check.C) {
s.trustedClusters(c, false)
}
// TestMultiplexingTrustedClusters tests remote clusters scenarios
// using trusted clusters feature
func (s *IntSuite) TestMultiplexingTrustedClusters(c *check.C) {
s.trustedClusters(c, true)
}
func (s *IntSuite) trustedClusters(c *check.C, multiplex bool) {
username := s.me.Username
clusterMain := "cluster-main"
clusterAux := "cluster-aux"
main := NewInstance(clusterMain, HostID, Host, s.getPorts(5), s.priv, s.pub)
aux := NewInstance(clusterAux, HostID, Host, s.getPorts(5), s.priv, s.pub)
main := NewInstance(InstanceConfig{ClusterName: clusterMain, HostID: HostID, NodeName: Host, Ports: s.getPorts(5), Priv: s.priv, Pub: s.pub, MultiplexProxy: multiplex})
aux := NewInstance(InstanceConfig{ClusterName: clusterAux, HostID: HostID, NodeName: Host, Ports: s.getPorts(5), Priv: s.priv, Pub: s.pub})
// main cluster has a local user and belongs to role "main-devs"
mainDevs := "main-devs"
@ -1254,8 +1264,8 @@ func (s *IntSuite) TestDiscovery(c *check.C) {
go lb.Serve()
defer lb.Close()
remote := NewInstance("cluster-remote", HostID, Host, s.getPorts(5), s.priv, s.pub)
main := NewInstance("cluster-main", HostID, Host, s.getPorts(5), s.priv, s.pub)
remote := NewInstance(InstanceConfig{ClusterName: "cluster-remote", HostID: HostID, NodeName: Host, Ports: s.getPorts(5), Priv: s.priv, Pub: s.pub})
main := NewInstance(InstanceConfig{ClusterName: "cluster-main", HostID: HostID, NodeName: Host, Ports: s.getPorts(5), Priv: s.priv, Pub: s.pub})
remote.AddUser(username, []string{username})
main.AddUser(username, []string{username})

View file

@ -261,6 +261,10 @@ func ApplyFileConfig(fc *FileConfig, cfg *service.Config) error {
}
// apply "proxy_service" section
cfg.Proxy.EnableProxyProtocol, err = utils.ParseOnOff("proxy_protocol", fc.Proxy.ProxyProtocol, true)
if err != nil {
return trace.Wrap(err)
}
if fc.Proxy.ListenAddress != "" {
addr, err := utils.ParseHostPortAddr(fc.Proxy.ListenAddress, int(defaults.SSHProxyListenPort))
if err != nil {

View file

@ -654,14 +654,25 @@ type CommandLabel struct {
Period time.Duration `yaml:"period"`
}
// Proxy is `proxy_service` section of the config file:
// Proxy is a `proxy_service` section of the config file:
type Proxy struct {
Service `yaml:",inline"`
WebAddr string `yaml:"web_listen_addr,omitempty"`
TunAddr string `yaml:"tunnel_listen_addr,omitempty"`
KeyFile string `yaml:"https_key_file,omitempty"`
CertFile string `yaml:"https_cert_file,omitempty"`
// Service is a generic service configuration section
Service `yaml:",inline"`
// WebAddr is a web UI listen address
WebAddr string `yaml:"web_listen_addr,omitempty"`
// TunAddr is a reverse tunnel address
TunAddr string `yaml:"tunnel_listen_addr,omitempty"`
// KeyFile is a TLS key file
KeyFile string `yaml:"https_key_file,omitempty"`
// CertFile is a TLS Certificate file
CertFile string `yaml:"https_cert_file,omitempty"`
// PublicAddr is a publicly advertised address of the proxy
PublicAddr string `yaml:"public_addr,omitempty"`
// ProxyProtocol turns on support for HAProxy proxy protocol
// this is the option that has be turned on only by administrator,
// as only admin knows whether service is in front of trusted load balancer
// or not.
ProxyProtocol string `yaml:"proxy_protocol,omitempty"`
}
// ReverseTunnel is a SSH reverse tunnel maintained by one cluster's

View file

@ -53,6 +53,10 @@ type Config struct {
Clock clockwork.Clock
// EnableProxyProtocol enables proxy protocol
EnableProxyProtocol bool
// DisableSSH disables SSH socket
DisableSSH bool
// DisableTLS disables TLS socket
DisableTLS bool
}
// CheckAndSetDefaults verifies configuration and sets defaults
@ -161,13 +165,13 @@ func (m *Mux) Serve() error {
for {
conn, err := m.Listener.Accept()
if err == nil {
if tcpConn, ok := conn.(*net.TCPConn); ok {
tcpConn.SetKeepAlive(true)
tcpConn.SetKeepAlivePeriod(3 * time.Minute)
}
go m.detectAndForward(conn)
continue
}
if tcpConn, ok := conn.(*net.TCPConn); ok {
tcpConn.SetKeepAlive(true)
tcpConn.SetKeepAlivePeriod(3 * time.Minute)
}
if m.isClosed() {
return nil
}
@ -205,6 +209,11 @@ func (m *Mux) detectAndForward(conn net.Conn) {
switch connWrapper.protocol {
case ProtoTLS:
if m.DisableTLS {
m.Debug("Closing TLS connection: TLS listener is disabled.")
conn.Close()
return
}
select {
case m.tlsListener.connC <- connWrapper:
case <-m.context.Done():
@ -212,6 +221,11 @@ func (m *Mux) detectAndForward(conn net.Conn) {
return
}
case ProtoSSH:
if m.DisableSSH {
m.Debug("Closing SSH connection: SSH listener is disabled.")
conn.Close()
return
}
select {
case m.sshListener.connC <- connWrapper:
case <-m.context.Done():

View file

@ -309,6 +309,121 @@ func (s *MuxSuite) TestUnknownProtocol(c *check.C) {
c.Assert(err, check.Equals, io.EOF)
}
// TestDisableSSH disables SSH
func (s *MuxSuite) TestDisableSSH(c *check.C) {
ports, err := utils.GetFreeTCPPorts(1)
c.Assert(err, check.IsNil)
listener, err := net.Listen("tcp", net.JoinHostPort("127.0.0.1", ports[0]))
c.Assert(err, check.IsNil)
mux, err := New(Config{
Listener: listener,
EnableProxyProtocol: true,
DisableSSH: true,
})
c.Assert(err, check.IsNil)
go mux.Serve()
defer mux.Close()
backend1 := &httptest.Server{
Listener: mux.TLS(),
Config: &http.Server{Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "backend 1")
}),
},
}
backend1.StartTLS()
defer backend1.Close()
_, err = ssh.Dial("tcp", listener.Addr().String(), &ssh.ClientConfig{
Auth: []ssh.AuthMethod{ssh.Password("abc123")},
Timeout: time.Second,
})
c.Assert(err, check.NotNil)
// TLS requests will succeed
client := testClient(backend1)
re, err := client.Get(backend1.URL)
c.Assert(err, check.IsNil)
bytes, err := ioutil.ReadAll(re.Body)
c.Assert(err, check.IsNil)
c.Assert(string(bytes), check.Equals, "backend 1")
// Close mux, new requests should fail
mux.Close()
mux.Wait()
// use new client to use new connection pool
client = testClient(backend1)
_, err = client.Get(backend1.URL)
c.Assert(err, check.NotNil)
}
// TestDisableTLS tests scenario with disabled TLS
func (s *MuxSuite) TestDisableTLS(c *check.C) {
ports, err := utils.GetFreeTCPPorts(1)
c.Assert(err, check.IsNil)
listener, err := net.Listen("tcp", net.JoinHostPort("127.0.0.1", ports[0]))
c.Assert(err, check.IsNil)
mux, err := New(Config{
Listener: listener,
EnableProxyProtocol: true,
DisableTLS: true,
})
c.Assert(err, check.IsNil)
go mux.Serve()
defer mux.Close()
backend1 := &httptest.Server{
Listener: mux.TLS(),
Config: &http.Server{Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "backend 1")
}),
},
}
backend1.StartTLS()
defer backend1.Close()
called := false
sshHandler := sshutils.NewChanHandlerFunc(func(_ net.Conn, conn *ssh.ServerConn, nch ssh.NewChannel) {
called = true
nch.Reject(ssh.Prohibited, "nothing to see here")
})
srv, err := sshutils.NewServer(
"test",
utils.NetAddr{AddrNetwork: "tcp", Addr: "localhost:0"},
sshHandler,
s.signers,
sshutils.AuthMethods{Password: pass("abc123")},
)
c.Assert(err, check.IsNil)
go srv.Serve(mux.SSH())
defer srv.Close()
clt, err := ssh.Dial("tcp", listener.Addr().String(), &ssh.ClientConfig{
Auth: []ssh.AuthMethod{ssh.Password("abc123")},
Timeout: time.Second,
})
c.Assert(err, check.IsNil)
defer clt.Close()
// call new session to initiate opening new channel
clt.NewSession()
// make sure the channel handler was called OK
c.Assert(called, check.Equals, true)
client := testClient(backend1)
_, err = client.Get(backend1.URL)
c.Assert(err, check.NotNil)
// Close mux, new requests should fail
mux.Close()
mux.Wait()
}
// clientConfig returns tls client config from test http server
// set up to listen on TLS
func clientConfig(srv *httptest.Server) *tls.Config {

View file

@ -104,8 +104,8 @@ type Config struct {
// ClientTLS is a TLS config associated with this proxy
// used to connect to remote auth servers on remote clusters
ClientTLS *tls.Config
// ListenAddr is a listening address for reverse tunnel server
ListenAddr utils.NetAddr
// Listener is a listener address for reverse tunnel server
Listener net.Listener
// HostSigners is a list of host signers
HostSigners []ssh.Signer
// HostKeyCallback
@ -152,8 +152,8 @@ func (cfg *Config) CheckAndSetDefaults() error {
if cfg.ClientTLS == nil {
return trace.BadParameter("missing parameter ClientTLS")
}
if cfg.ListenAddr.IsEmpty() {
return trace.BadParameter("missing parameter ListenAddr")
if cfg.Listener == nil {
return trace.BadParameter("missing parameter Listener")
}
if cfg.Context == nil {
cfg.Context = context.TODO()
@ -205,7 +205,9 @@ func NewServer(cfg Config) (Server, error) {
var err error
s, err := sshutils.NewServer(
teleport.ComponentReverseTunnelServer,
cfg.ListenAddr,
// TODO(klizhentas): improve interface, use struct instead of parameter list
// this address is not used
utils.NetAddr{Addr: "127.0.0.1:1", AddrNetwork: "tcp"},
srv,
cfg.HostSigners,
sshutils.AuthMethods{
@ -404,7 +406,8 @@ func (s *server) Wait() {
}
func (s *server) Start() error {
return s.srv.Start()
go s.srv.Serve(s.Listener)
return nil
}
func (s *server) Close() error {

View file

@ -210,6 +210,9 @@ type ProxyConfig struct {
// ReverseTunnelListenAddr is address where reverse tunnel dialers connect to
ReverseTunnelListenAddr utils.NetAddr
// EnableProxyProtocol enables proxy protocol support
EnableProxyProtocol bool
// WebAddr is address for web portal of the proxy
WebAddr utils.NetAddr

View file

@ -808,6 +808,99 @@ func (process *TeleportProcess) initProxy() error {
return nil
}
type proxyListeners struct {
mux *multiplexer.Mux
web net.Listener
reverseTunnel net.Listener
}
func (l *proxyListeners) Close() {
if l.mux != nil {
l.mux.Close()
}
if l.web != nil {
l.web.Close()
}
if l.reverseTunnel != nil {
l.reverseTunnel.Close()
}
}
// setupProxyListeners sets up web proxy listeners based on the configuration
func (process *TeleportProcess) setupProxyListeners() (*proxyListeners, error) {
cfg := process.Config
log.Debugf("Setup Proxy: Web Proxy Address: %v, Reverse Tunnel Proxy Address: %v", cfg.Proxy.WebAddr.Addr, cfg.Proxy.ReverseTunnelListenAddr.Addr)
var err error
var listeners proxyListeners
switch {
case cfg.Proxy.DisableWebService && cfg.Proxy.DisableReverseTunnel:
log.Debugf("Setup Proxy: Reverse tunnel proxy and web proxy are disabled.")
return &listeners, nil
case cfg.Proxy.ReverseTunnelListenAddr.Equals(cfg.Proxy.WebAddr):
log.Debugf("Setup Proxy: Reverse tunnel proxy and web proxy listen on the same port, multiplexing is on.")
listener, err := net.Listen("tcp", cfg.Proxy.WebAddr.Addr)
if err != nil {
return nil, trace.Wrap(err)
}
listeners.mux, err = multiplexer.New(multiplexer.Config{
EnableProxyProtocol: cfg.Proxy.EnableProxyProtocol,
Listener: listener,
DisableTLS: cfg.Proxy.DisableWebService,
DisableSSH: cfg.Proxy.DisableReverseTunnel,
})
if err != nil {
listener.Close()
return nil, trace.Wrap(err)
}
listeners.web = listeners.mux.TLS()
listeners.reverseTunnel = listeners.mux.SSH()
go listeners.mux.Serve()
return &listeners, nil
case cfg.Proxy.EnableProxyProtocol && !cfg.Proxy.DisableWebService:
log.Debugf("Setup Proxy: Proxy protocol is enabled for web service, multiplexing is on.")
listener, err := net.Listen("tcp", cfg.Proxy.WebAddr.Addr)
if err != nil {
return nil, trace.Wrap(err)
}
listeners.mux, err = multiplexer.New(multiplexer.Config{
EnableProxyProtocol: cfg.Proxy.EnableProxyProtocol,
Listener: listener,
DisableTLS: false,
DisableSSH: true,
})
if err != nil {
listener.Close()
return nil, trace.Wrap(err)
}
listeners.web = listeners.mux.TLS()
listeners.reverseTunnel, err = net.Listen("tcp", cfg.Proxy.ReverseTunnelListenAddr.Addr)
if err != nil {
listener.Close()
listeners.Close()
return nil, trace.Wrap(err)
}
go listeners.mux.Serve()
return &listeners, nil
default:
log.Debugf("Proxy reverse tunnel are listening on the separate ports")
if !cfg.Proxy.DisableReverseTunnel {
listeners.reverseTunnel, err = net.Listen("tcp", cfg.Proxy.ReverseTunnelListenAddr.Addr)
if err != nil {
listeners.Close()
return nil, trace.Wrap(err)
}
}
if !cfg.Proxy.DisableWebService {
listeners.web, err = net.Listen("tcp", cfg.Proxy.WebAddr.Addr)
if err != nil {
listeners.Close()
return nil, trace.Wrap(err)
}
}
return &listeners, nil
}
}
func (process *TeleportProcess) initProxyEndpoint(conn *Connector) error {
var (
askedToExit = true
@ -836,32 +929,111 @@ func (process *TeleportProcess) initProxyEndpoint(conn *Connector) error {
return trace.Wrap(err)
}
tsrv, err := reversetunnel.NewServer(
reversetunnel.Config{
ID: process.Config.HostUUID,
ClusterName: conn.Identity.Cert.Extensions[utils.CertExtensionAuthority],
ClientTLS: tlsConfig,
ListenAddr: cfg.Proxy.ReverseTunnelListenAddr,
HostSigners: []ssh.Signer{conn.Identity.KeySigner},
LocalAuthClient: conn.Client,
LocalAccessPoint: accessPoint,
NewCachingAccessPoint: process.newLocalCache,
Limiter: reverseTunnelLimiter,
DirectClusters: []reversetunnel.DirectCluster{
{
Name: conn.Identity.Cert.Extensions[utils.CertExtensionAuthority],
Client: conn.Client,
},
},
Ciphers: cfg.Ciphers,
KEXAlgorithms: cfg.KEXAlgorithms,
MACAlgorithms: cfg.MACAlgorithms,
})
listeners, err := process.setupProxyListeners()
if err != nil {
return trace.Wrap(err)
}
SSHProxy, err := regular.New(cfg.Proxy.SSHAddr,
// Register reverse tunnel agents pool
agentPool, err := reversetunnel.NewAgentPool(reversetunnel.AgentPoolConfig{
HostUUID: conn.Identity.ID.HostUUID,
Client: conn.Client,
AccessPoint: accessPoint,
HostSigners: []ssh.Signer{conn.Identity.KeySigner},
Cluster: conn.Identity.Cert.Extensions[utils.CertExtensionAuthority],
})
if err != nil {
return trace.Wrap(err)
}
// register SSH reverse tunnel server that accepts connections
// from remote teleport nodes
var tsrv reversetunnel.Server
if !process.Config.Proxy.DisableReverseTunnel {
tsrv, err = reversetunnel.NewServer(
reversetunnel.Config{
ID: process.Config.HostUUID,
ClusterName: conn.Identity.Cert.Extensions[utils.CertExtensionAuthority],
ClientTLS: tlsConfig,
Listener: listeners.reverseTunnel,
HostSigners: []ssh.Signer{conn.Identity.KeySigner},
LocalAuthClient: conn.Client,
LocalAccessPoint: accessPoint,
NewCachingAccessPoint: process.newLocalCache,
Limiter: reverseTunnelLimiter,
DirectClusters: []reversetunnel.DirectCluster{
{
Name: conn.Identity.Cert.Extensions[utils.CertExtensionAuthority],
Client: conn.Client,
},
},
Ciphers: cfg.Ciphers,
KEXAlgorithms: cfg.KEXAlgorithms,
MACAlgorithms: cfg.MACAlgorithms,
})
if err != nil {
return trace.Wrap(err)
}
process.RegisterFunc("proxy.reveresetunnel.server", func() error {
utils.Consolef(cfg.Console, "Starting reverse tunnel service is starting on %v using %v", cfg.Proxy.ReverseTunnelListenAddr.Addr, process.Config.CachePolicy)
if err := tsrv.Start(); err != nil {
utils.Consolef(cfg.Console, "Error: %v", err)
return trace.Wrap(err)
}
// notify parties that we've started reverse tunnel server
process.BroadcastEvent(Event{Name: ProxyReverseTunnelServerEvent, Payload: tsrv})
tsrv.Wait()
if askedToExit {
log.Infof("Reverse tunnel exited.")
}
return nil
})
}
// Register web proxy server
if !process.Config.Proxy.DisableWebService {
process.RegisterFunc("proxy.web", func() error {
utils.Consolef(cfg.Console, "Web proxy service is starting on %v.", cfg.Proxy.WebAddr.Addr)
webHandler, err := web.NewHandler(
web.Config{
Proxy: tsrv,
AuthServers: cfg.AuthServers[0],
DomainName: cfg.Hostname,
ProxyClient: conn.Client,
DisableUI: process.Config.Proxy.DisableWebInterface,
ProxySSHAddr: cfg.Proxy.SSHAddr,
ProxyWebAddr: cfg.Proxy.WebAddr,
})
if err != nil {
return trace.Wrap(err)
}
defer webHandler.Close()
proxyLimiter.WrapHandle(webHandler)
process.BroadcastEvent(Event{Name: ProxyWebServerEvent, Payload: webHandler})
if !process.Config.Proxy.DisableTLS {
tlsConfig, err := utils.CreateTLSConfiguration(cfg.Proxy.TLSCert, cfg.Proxy.TLSKey)
if err != nil {
return trace.Wrap(err)
}
listeners.web = tls.NewListener(listeners.web, tlsConfig)
}
if err = http.Serve(listeners.web, proxyLimiter); err != nil {
if askedToExit {
log.Infof("Proxy web server exited.")
return nil
}
log.Error(err)
}
return nil
})
} else {
log.Infof("Web UI is disabled.")
}
// Register SSH proxy server - SSH jumphost proxy server
sshProxy, err := regular.New(cfg.Proxy.SSHAddr,
cfg.Hostname,
[]ssh.Signer{conn.Identity.KeySigner},
accessPoint,
@ -881,94 +1053,9 @@ func (process *TeleportProcess) initProxyEndpoint(conn *Connector) error {
return trace.Wrap(err)
}
// Register reverse tunnel agents pool
agentPool, err := reversetunnel.NewAgentPool(reversetunnel.AgentPoolConfig{
HostUUID: conn.Identity.ID.HostUUID,
Client: conn.Client,
AccessPoint: accessPoint,
HostSigners: []ssh.Signer{conn.Identity.KeySigner},
Cluster: conn.Identity.Cert.Extensions[utils.CertExtensionAuthority],
})
if err != nil {
return trace.Wrap(err)
}
// register SSH reverse tunnel server that accepts connections
// from remote teleport nodes
if !process.Config.Proxy.DisableReverseTunnel {
process.RegisterFunc("proxy.reveresetunnel.server", func() error {
utils.Consolef(cfg.Console, "[PROXY] Reverse tunnel service is starting on %v using %v", cfg.Proxy.ReverseTunnelListenAddr.Addr, process.Config.CachePolicy)
if err := tsrv.Start(); err != nil {
utils.Consolef(cfg.Console, "[PROXY] Error: %v", err)
return trace.Wrap(err)
}
// notify parties that we've started reverse tunnel server
process.BroadcastEvent(Event{Name: ProxyReverseTunnelServerEvent, Payload: tsrv})
tsrv.Wait()
if askedToExit {
log.Infof("Reverse tunnel exited.")
}
return nil
})
}
// Register web proxy server
var webListener net.Listener
if !process.Config.Proxy.DisableWebService {
process.RegisterFunc("proxy.web", func() error {
utils.Consolef(cfg.Console, "[PROXY] Web proxy service is starting on %v", cfg.Proxy.WebAddr.Addr)
webHandler, err := web.NewHandler(
web.Config{
Proxy: tsrv,
AuthServers: cfg.AuthServers[0],
DomainName: cfg.Hostname,
ProxyClient: conn.Client,
DisableUI: process.Config.Proxy.DisableWebInterface,
ProxySSHAddr: cfg.Proxy.SSHAddr,
ProxyWebAddr: cfg.Proxy.WebAddr,
})
if err != nil {
utils.Consolef(cfg.Console, "[PROXY] starting the web server: %v", err)
return trace.Wrap(err)
}
defer webHandler.Close()
proxyLimiter.WrapHandle(webHandler)
process.BroadcastEvent(Event{Name: ProxyWebServerEvent, Payload: webHandler})
log.Infof("init TLS listeners")
if !process.Config.Proxy.DisableTLS {
webListener, err = utils.ListenTLS(
cfg.Proxy.WebAddr.Addr,
cfg.Proxy.TLSCert,
cfg.Proxy.TLSKey)
if err != nil {
return trace.Wrap(err)
}
} else {
webListener, err = net.Listen("tcp", cfg.Proxy.WebAddr.Addr)
if err != nil {
return trace.Wrap(err)
}
}
if err = http.Serve(webListener, proxyLimiter); err != nil {
if askedToExit {
log.Infof("Proxy web server exited.")
return nil
}
log.Error(err)
}
return nil
})
} else {
log.Infof("Web UI is disabled.")
}
// Register ssh proxy server
process.RegisterFunc("proxy.ssh", func() error {
utils.Consolef(cfg.Console, "[PROXY] SSH proxy service is starting on %v", cfg.Proxy.SSHAddr.Addr)
if err := SSHProxy.Start(); err != nil {
if err := sshProxy.Start(); err != nil {
if askedToExit {
log.Infof("SSH proxy exited")
return nil
@ -991,12 +1078,12 @@ func (process *TeleportProcess) initProxyEndpoint(conn *Connector) error {
// execute this when process is asked to exit:
process.onExit(func(payload interface{}) {
tsrv.Close()
SSHProxy.Close()
agentPool.Stop()
if webListener != nil {
webListener.Close()
listeners.Close()
if tsrv != nil {
tsrv.Close()
}
sshProxy.Close()
agentPool.Stop()
log.Infof("Proxy service exited.")
})
return nil

View file

@ -260,6 +260,11 @@ func (c *TrustedClusterV2) CheckAndSetDefaults() error {
},
}
}
// Imply that by default proxy listens on the same port for
// web and reverse tunnel connections
if c.Spec.ReverseTunnelAddress == "" {
c.Spec.ReverseTunnelAddress = c.Spec.ProxyAddress
}
if err := c.Spec.RoleMap.Check(); err != nil {
return trace.Wrap(err)
}

View file

@ -444,6 +444,14 @@ func (s *SrvSuite) testClient(c *C, proxyAddr, targetAddr, remoteAddr string, ss
c.Assert(string(out), Equals, "hello\n")
}
func mustListen(a utils.NetAddr) net.Listener {
l, err := net.Listen("tcp", a.Addr)
if err != nil {
panic(err)
}
return l
}
func (s *SrvSuite) TestProxyReverseTunnel(c *C) {
log.Infof("[TEST START] TestProxyReverseTunnel")
@ -453,7 +461,7 @@ func (s *SrvSuite) TestProxyReverseTunnel(c *C) {
ClientTLS: s.proxyClient.TLSConfig(),
ID: hostID,
ClusterName: s.server.ClusterName(),
ListenAddr: reverseTunnelAddress,
Listener: mustListen(reverseTunnelAddress),
HostSigners: []ssh.Signer{s.signer},
LocalAuthClient: s.proxyClient,
LocalAccessPoint: s.proxyClient,
@ -622,7 +630,7 @@ func (s *SrvSuite) TestProxyRoundRobin(c *C) {
ClusterName: s.server.ClusterName(),
ClientTLS: s.proxyClient.TLSConfig(),
ID: hostID,
ListenAddr: reverseTunnelAddress,
Listener: mustListen(reverseTunnelAddress),
HostSigners: []ssh.Signer{s.signer},
LocalAuthClient: s.proxyClient,
LocalAccessPoint: s.proxyClient,
@ -722,7 +730,7 @@ func (s *SrvSuite) TestProxyDirectAccess(c *C) {
ClientTLS: s.proxyClient.TLSConfig(),
ID: hostID,
ClusterName: s.server.ClusterName(),
ListenAddr: reverseTunnelAddress,
Listener: mustListen(reverseTunnelAddress),
HostSigners: []ssh.Signer{s.signer},
LocalAuthClient: s.proxyClient,
LocalAccessPoint: s.proxyClient,

View file

@ -184,12 +184,12 @@ func (s *WebSuite) SetUpTest(c *C) {
s.proxyClient, err = s.server.NewClient(auth.TestBuiltin(teleport.RoleProxy))
c.Assert(err, IsNil)
revTunListener, err := net.Listen("tcp", fmt.Sprintf("%v:0", s.server.ClusterName()))
c.Assert(err, IsNil)
revTunServer, err := reversetunnel.NewServer(reversetunnel.Config{
ID: node.ID(),
ListenAddr: utils.NetAddr{
AddrNetwork: "tcp",
Addr: fmt.Sprintf("%v:0", s.server.ClusterName()),
},
ID: node.ID(),
Listener: revTunListener,
ClientTLS: s.proxyClient.TLSConfig(),
ClusterName: s.server.ClusterName(),
HostSigners: []ssh.Signer{signer},