mirror of
https://github.com/gravitational/teleport.git
synced 2026-09-24 16:17:11 +08:00
Add ability to run Mongo proxy on separate listener (#9194)
This commit is contained in:
@@ -262,6 +262,10 @@ type DBProxySettings struct {
|
||||
MySQLListenAddr string `json:"mysql_listen_addr,omitempty"`
|
||||
// MySQLPublicAddr is advertised to MySQL clients.
|
||||
MySQLPublicAddr string `json:"mysql_public_addr,omitempty"`
|
||||
// MongoListenAddr is Mongo proxy listen address.
|
||||
MongoListenAddr string `json:"mongo_listen_addr,omitempty"`
|
||||
// MongoPublicAddr is advertised to Mongo clients.
|
||||
MongoPublicAddr string `json:"mongo_public_addr,omitempty"`
|
||||
}
|
||||
|
||||
// AuthenticationSettings contains information about server authentication
|
||||
|
||||
@@ -65,6 +65,9 @@ type Profile struct {
|
||||
// MySQLProxyAddr is the host:port the MySQL proxy can be accessed at.
|
||||
MySQLProxyAddr string `yaml:"mysql_proxy_addr,omitempty"`
|
||||
|
||||
// MongoProxyAddr is the host:port the Mongo proxy can be accessed at.
|
||||
MongoProxyAddr string `yaml:"mongo_proxy_addr,omitempty"`
|
||||
|
||||
// Username is the Teleport username for the client.
|
||||
Username string `yaml:"user,omitempty"`
|
||||
|
||||
|
||||
@@ -388,6 +388,36 @@ func TestDatabaseAccessPostgresSeparateListener(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// TestDatabaseAccessMongoSeparateListener tests mongo proxy listener running on separate port.
|
||||
func TestDatabaseAccessMongoSeparateListener(t *testing.T) {
|
||||
pack := setupDatabaseTest(t,
|
||||
withPortSetupDatabaseTest(separateMongoPortSetup),
|
||||
)
|
||||
|
||||
// Connect to the database service in root cluster.
|
||||
client, err := mongodb.MakeTestClient(context.Background(), common.TestClientConfig{
|
||||
AuthClient: pack.root.cluster.GetSiteAPI(pack.root.cluster.Secrets.SiteName),
|
||||
AuthServer: pack.root.cluster.Process.GetAuthServer(),
|
||||
Address: net.JoinHostPort(Loopback, pack.root.cluster.GetPortMongo()),
|
||||
Cluster: pack.root.cluster.Secrets.SiteName,
|
||||
Username: pack.root.user.GetName(),
|
||||
RouteToDatabase: tlsca.RouteToDatabase{
|
||||
ServiceName: pack.root.mongoService.Name,
|
||||
Protocol: pack.root.mongoService.Protocol,
|
||||
Username: "admin",
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Execute a query.
|
||||
_, err = client.Database("test").Collection("test").Find(context.Background(), bson.M{})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Disconnect.
|
||||
err = client.Disconnect(context.Background())
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func waitForAuditEventTypeWithBackoff(t *testing.T, cli *auth.Server, startTime time.Time, eventType string) []apievents.AuditEvent {
|
||||
max := time.Second
|
||||
timeout := time.After(max)
|
||||
|
||||
@@ -599,6 +599,10 @@ func (i *TeleInstance) GenerateConfig(t *testing.T, trustedSecrets []*InstanceSe
|
||||
// Postgres proxy port was configured on a separate listener.
|
||||
tconf.Proxy.PostgresAddr.Addr = net.JoinHostPort(i.Hostname, i.GetPortPostgres())
|
||||
}
|
||||
if i.Mongo != nil {
|
||||
// Mongo proxy port was configured on a separate listener.
|
||||
tconf.Proxy.MongoAddr.Addr = net.JoinHostPort(i.Hostname, i.GetPortMongo())
|
||||
}
|
||||
}
|
||||
tconf.AuthServers = append(tconf.AuthServers, tconf.Auth.SSHAddr)
|
||||
tconf.Auth.StorageConfig = backend.Config{
|
||||
|
||||
@@ -99,6 +99,18 @@ func separatePostgresPortSetup() *InstancePorts {
|
||||
}
|
||||
}
|
||||
|
||||
func separateMongoPortSetup() *InstancePorts {
|
||||
return &InstancePorts{
|
||||
Web: newInstancePort(),
|
||||
SSH: newInstancePort(),
|
||||
Auth: newInstancePort(),
|
||||
SSHProxy: newInstancePort(),
|
||||
ReverseTunnel: newInstancePort(),
|
||||
MySQL: newInstancePort(),
|
||||
Mongo: newInstancePort(),
|
||||
}
|
||||
}
|
||||
|
||||
type InstancePorts struct {
|
||||
Host string
|
||||
Web *InstancePort
|
||||
@@ -110,6 +122,7 @@ type InstancePorts struct {
|
||||
ReverseTunnel *InstancePort
|
||||
MySQL *InstancePort
|
||||
Postgres *InstancePort
|
||||
Mongo *InstancePort
|
||||
|
||||
isSinglePortSetup bool
|
||||
}
|
||||
@@ -121,6 +134,7 @@ func (i *InstancePorts) GetPortProxy() string { return i.SSHProxy.String
|
||||
func (i *InstancePorts) GetPortWeb() string { return i.Web.String() }
|
||||
func (i *InstancePorts) GetPortMySQL() string { return i.MySQL.String() }
|
||||
func (i *InstancePorts) GetPortPostgres() string { return i.Postgres.String() }
|
||||
func (i *InstancePorts) GetPortMongo() string { return i.Mongo.String() }
|
||||
func (i *InstancePorts) GetPortReverseTunnel() string { return i.ReverseTunnel.String() }
|
||||
|
||||
func (i *InstancePorts) GetSSHAddr() string {
|
||||
|
||||
+35
-1
@@ -199,6 +199,9 @@ type Config struct {
|
||||
// PostgresProxyAddr is the host:port the Postgres proxy can be accessed at.
|
||||
PostgresProxyAddr string
|
||||
|
||||
// MongoProxyAddr is the host:port the Mongo proxy can be accessed at.
|
||||
MongoProxyAddr string
|
||||
|
||||
// MySQLProxyAddr is the host:port the MySQL proxy can be accessed at.
|
||||
MySQLProxyAddr string
|
||||
|
||||
@@ -800,6 +803,7 @@ func (c *Config) LoadProfile(profileDir string, proxyName string) error {
|
||||
c.SSHProxyAddr = cp.SSHProxyAddr
|
||||
c.PostgresProxyAddr = cp.PostgresProxyAddr
|
||||
c.MySQLProxyAddr = cp.MySQLProxyAddr
|
||||
c.MongoProxyAddr = cp.MongoProxyAddr
|
||||
c.TLSRoutingEnabled = cp.TLSRoutingEnabled
|
||||
|
||||
c.LocalForwardPorts, err = ParsePortForwardSpec(cp.ForwardedPorts)
|
||||
@@ -831,6 +835,7 @@ func (c *Config) SaveProfile(dir string, makeCurrent bool) error {
|
||||
cp.KubeProxyAddr = c.KubeProxyAddr
|
||||
cp.PostgresProxyAddr = c.PostgresProxyAddr
|
||||
cp.MySQLProxyAddr = c.MySQLProxyAddr
|
||||
cp.MongoProxyAddr = c.MongoProxyAddr
|
||||
cp.ForwardedPorts = c.LocalForwardPorts.String()
|
||||
cp.SiteName = c.SiteName
|
||||
cp.TLSRoutingEnabled = c.TLSRoutingEnabled
|
||||
@@ -996,6 +1001,17 @@ func (c *Config) PostgresProxyHostPort() (string, int) {
|
||||
return c.WebProxyHostPort()
|
||||
}
|
||||
|
||||
// MongoProxyHostPort returns the host and port of Mongo proxy.
|
||||
func (c *Config) MongoProxyHostPort() (string, int) {
|
||||
if c.MongoProxyAddr != "" {
|
||||
addr, err := utils.ParseAddr(c.MongoProxyAddr)
|
||||
if err == nil {
|
||||
return addr.Host(), addr.Port(defaults.MongoListenPort)
|
||||
}
|
||||
}
|
||||
return c.WebProxyHostPort()
|
||||
}
|
||||
|
||||
// MySQLProxyHostPort returns the host and port of MySQL proxy.
|
||||
func (c *Config) MySQLProxyHostPort() (string, int) {
|
||||
if c.MySQLProxyAddr != "" {
|
||||
@@ -1016,7 +1032,7 @@ func (c *Config) DatabaseProxyHostPort(db tlsca.RouteToDatabase) (string, int) {
|
||||
case defaults.ProtocolMySQL:
|
||||
return c.MySQLProxyHostPort()
|
||||
case defaults.ProtocolMongoDB:
|
||||
return c.WebProxyHostPort()
|
||||
return c.MongoProxyHostPort()
|
||||
}
|
||||
return c.WebProxyHostPort()
|
||||
}
|
||||
@@ -2663,6 +2679,24 @@ func (tc *TeleportClient) applyProxySettings(proxySettings webclient.ProxySettin
|
||||
tc.PostgresProxyAddr = net.JoinHostPort(webProxyHost, strconv.Itoa(webProxyPort))
|
||||
}
|
||||
|
||||
// Read Mongo proxy settings.
|
||||
switch {
|
||||
case proxySettings.DB.MongoPublicAddr != "":
|
||||
addr, err := utils.ParseAddr(proxySettings.DB.MongoPublicAddr)
|
||||
if err != nil {
|
||||
return trace.BadParameter("failed to parse Mongo public address received from server: %q, contact your administrator for help",
|
||||
proxySettings.DB.MongoPublicAddr)
|
||||
}
|
||||
tc.MongoProxyAddr = net.JoinHostPort(addr.Host(), strconv.Itoa(addr.Port(tc.WebProxyPort())))
|
||||
case proxySettings.DB.MongoListenAddr != "":
|
||||
addr, err := utils.ParseAddr(proxySettings.DB.MongoListenAddr)
|
||||
if err != nil {
|
||||
return trace.BadParameter("failed to parse Mongo listen address received from server: %q, contact your administrator for help",
|
||||
proxySettings.DB.MongoListenAddr)
|
||||
}
|
||||
tc.MongoProxyAddr = net.JoinHostPort(tc.WebProxyHost(), strconv.Itoa(addr.Port(defaults.MongoListenPort)))
|
||||
}
|
||||
|
||||
// Read MySQL proxy settings if enabled on the server.
|
||||
switch {
|
||||
case proxySettings.DB.MySQLPublicAddr != "":
|
||||
|
||||
@@ -671,6 +671,13 @@ func applyProxyConfig(fc *FileConfig, cfg *service.Config) error {
|
||||
}
|
||||
cfg.Proxy.PostgresAddr = *addr
|
||||
}
|
||||
if fc.Proxy.MongoAddr != "" {
|
||||
addr, err := utils.ParseHostPortAddr(fc.Proxy.MongoAddr, int(defaults.MongoListenPort))
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
cfg.Proxy.MongoAddr = *addr
|
||||
}
|
||||
|
||||
// This is the legacy format. Continue to support it forever, but ideally
|
||||
// users now use the list format below.
|
||||
@@ -815,6 +822,17 @@ func applyProxyConfig(fc *FileConfig, cfg *service.Config) error {
|
||||
cfg.Proxy.MySQLPublicAddrs = addrs
|
||||
}
|
||||
|
||||
if len(fc.Proxy.MongoPublicAddr) != 0 {
|
||||
if fc.Proxy.MongoAddr == "" {
|
||||
return trace.BadParameter("mongo_listen_addr must be set when mongo_public_addr is set")
|
||||
}
|
||||
addrs, err := utils.AddrsFromStrings(fc.Proxy.MongoPublicAddr, defaults.MongoListenPort)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
cfg.Proxy.MongoPublicAddrs = addrs
|
||||
}
|
||||
|
||||
acme, err := fc.Proxy.ACME.Parse()
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
|
||||
@@ -679,10 +679,13 @@ func TestApplyConfig(t *testing.T) {
|
||||
require.Equal(t, "tcp://webhost:3080", cfg.Proxy.WebAddr.FullAddress())
|
||||
require.Equal(t, "tcp://tunnelhost:1001", cfg.Proxy.ReverseTunnelListenAddr.FullAddress())
|
||||
require.Equal(t, "tcp://webhost:3336", cfg.Proxy.MySQLAddr.FullAddress())
|
||||
require.Equal(t, "tcp://webhost:27017", cfg.Proxy.MongoAddr.FullAddress())
|
||||
require.Len(t, cfg.Proxy.PostgresPublicAddrs, 1)
|
||||
require.Equal(t, "tcp://postgres.example:5432", cfg.Proxy.PostgresPublicAddrs[0].FullAddress())
|
||||
require.Len(t, cfg.Proxy.MySQLPublicAddrs, 1)
|
||||
require.Equal(t, "tcp://mysql.example:3306", cfg.Proxy.MySQLPublicAddrs[0].FullAddress())
|
||||
require.Len(t, cfg.Proxy.MongoPublicAddrs, 1)
|
||||
require.Equal(t, "tcp://mongo.example:27017", cfg.Proxy.MongoPublicAddrs[0].FullAddress())
|
||||
|
||||
require.Equal(t, "tcp://127.0.0.1:3000", cfg.DiagnosticAddr.FullAddress())
|
||||
|
||||
|
||||
@@ -1121,6 +1121,12 @@ type Proxy struct {
|
||||
// PostgresPublicAddr is the hostport the proxy advertises for Postgres
|
||||
// client connections.
|
||||
PostgresPublicAddr apiutils.Strings `yaml:"postgres_public_addr,omitempty"`
|
||||
|
||||
// MongoAddr is Mongo proxy listen address.
|
||||
MongoAddr string `yaml:"mongo_listen_addr,omitempty"`
|
||||
// MongoPublicAddr is the hostport the proxy advertises for Mongo
|
||||
// client connections.
|
||||
MongoPublicAddr apiutils.Strings `yaml:"mongo_public_addr,omitempty"`
|
||||
}
|
||||
|
||||
// ACME configures ACME protocol - automatic X.509 certificates
|
||||
|
||||
@@ -159,6 +159,8 @@ proxy_service:
|
||||
postgres_public_addr: postgres.example:5432
|
||||
mysql_listen_addr: webhost:3336
|
||||
mysql_public_addr: mysql.example:3306
|
||||
mongo_listen_addr: webhost:27017
|
||||
mongo_public_addr: mongo.example:27017
|
||||
`
|
||||
|
||||
// NoServicesConfigString is a configuration file with no services enabled
|
||||
|
||||
@@ -63,6 +63,9 @@ const (
|
||||
// PostgresListenPort is the default listen port for PostgreSQL proxy.
|
||||
PostgresListenPort = 5432
|
||||
|
||||
// MongoListenPort is the default listen port for Mongo proxy.
|
||||
MongoListenPort = 27017
|
||||
|
||||
// MetricsListenPort is the default listen port for the metrics service.
|
||||
MetricsListenPort = 3081
|
||||
|
||||
|
||||
@@ -361,6 +361,9 @@ type ProxyConfig struct {
|
||||
// PostgresAddr is address of Postgres proxy.
|
||||
PostgresAddr utils.NetAddr
|
||||
|
||||
// MongoAddr is address of Mongo proxy.
|
||||
MongoAddr utils.NetAddr
|
||||
|
||||
Limiter limiter.Config
|
||||
|
||||
// PublicAddrs is a list of the public addresses the proxy advertises
|
||||
@@ -386,6 +389,10 @@ type ProxyConfig struct {
|
||||
// advertises for MySQL clients.
|
||||
MySQLPublicAddrs []utils.NetAddr
|
||||
|
||||
// MongoPublicAddrs is a list of the public addresses the proxy
|
||||
// advertises for Mongo clients.
|
||||
MongoPublicAddrs []utils.NetAddr
|
||||
|
||||
// Kube specifies kubernetes proxy configuration
|
||||
Kube KubeProxyConfig
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ var (
|
||||
listenerProxyTunnel = listenerType(teleport.Component(teleport.ComponentProxy, "tunnel"))
|
||||
listenerProxyMySQL = listenerType(teleport.Component(teleport.ComponentProxy, "mysql"))
|
||||
listenerProxyPostgres = listenerType(teleport.Component(teleport.ComponentProxy, "postgres"))
|
||||
listenerProxyMongo = listenerType(teleport.Component(teleport.ComponentProxy, "mongo"))
|
||||
listenerMetrics = listenerType(teleport.ComponentMetrics)
|
||||
listenerWindowsDesktop = listenerType(teleport.ComponentWindowsDesktop)
|
||||
)
|
||||
|
||||
@@ -81,6 +81,10 @@ func (p *proxySettings) buildProxySettings(proxyListenerMode types.ProxyListener
|
||||
proxySettings.DB.PostgresListenAddr = p.cfg.Proxy.PostgresAddr.String()
|
||||
}
|
||||
|
||||
if !p.cfg.Proxy.MongoAddr.IsEmpty() {
|
||||
proxySettings.DB.MongoListenAddr = p.cfg.Proxy.MongoAddr.String()
|
||||
}
|
||||
|
||||
if p.cfg.Proxy.Kube.Enabled {
|
||||
proxySettings.Kube.ListenAddr = p.cfg.Proxy.Kube.ListenAddr.String()
|
||||
}
|
||||
@@ -117,6 +121,9 @@ func (p *proxySettings) setProxyPublicAddressesSettings(settings *webclient.Prox
|
||||
if len(p.cfg.Proxy.MySQLPublicAddrs) > 0 {
|
||||
settings.DB.MySQLPublicAddr = p.cfg.Proxy.MySQLPublicAddrs[0].String()
|
||||
}
|
||||
if len(p.cfg.Proxy.MongoPublicAddrs) > 0 {
|
||||
settings.DB.MongoPublicAddr = p.cfg.Proxy.MongoPublicAddrs[0].String()
|
||||
}
|
||||
settings.DB.PostgresPublicAddr = p.getPostgresPublicAddr()
|
||||
}
|
||||
|
||||
|
||||
@@ -2526,6 +2526,8 @@ type dbListeners struct {
|
||||
postgres net.Listener
|
||||
// mysql serves MySQL clients.
|
||||
mysql net.Listener
|
||||
// mongo serves Mongo clients.
|
||||
mongo net.Listener
|
||||
// tls serves database clients that use plain TLS handshake.
|
||||
tls net.Listener
|
||||
}
|
||||
@@ -2546,6 +2548,9 @@ func (l *dbListeners) Close() {
|
||||
if l.tls != nil {
|
||||
l.tls.Close()
|
||||
}
|
||||
if l.mongo != nil {
|
||||
l.mongo.Close()
|
||||
}
|
||||
}
|
||||
|
||||
// Close closes all proxy listeners.
|
||||
@@ -2603,6 +2608,15 @@ func (process *TeleportProcess) setupProxyListeners() (*proxyListeners, error) {
|
||||
listeners.db.mysql = listener
|
||||
}
|
||||
|
||||
if !cfg.Proxy.MongoAddr.IsEmpty() && !cfg.Proxy.DisableDatabaseProxy {
|
||||
process.log.Debugf("Setup Proxy: Mongo proxy address: %v.", cfg.Proxy.MongoAddr.Addr)
|
||||
listener, err := process.importOrCreateListener(listenerProxyMongo, cfg.Proxy.MongoAddr.Addr)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
listeners.db.mongo = listener
|
||||
}
|
||||
|
||||
switch {
|
||||
case cfg.Proxy.DisableWebService && cfg.Proxy.DisableReverseTunnel:
|
||||
process.log.Debugf("Setup Proxy: Reverse tunnel proxy and web proxy are disabled.")
|
||||
@@ -3170,6 +3184,16 @@ func (process *TeleportProcess) initProxyEndpoint(conn *Connector) error {
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
if listeners.db.mongo != nil {
|
||||
process.RegisterCriticalFunc("proxy.db.mongo", func() error {
|
||||
log.Infof("Starting Database Mongo proxy server on %v.", cfg.Proxy.MongoAddr.Addr)
|
||||
if err := dbProxyServer.ServeMongo(listeners.db.mongo, tlsConfigWeb.Clone()); err != nil {
|
||||
log.WithError(err).Warn("Database Mongo proxy server exited with error.")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var alpnServer *alpnproxy.Proxy
|
||||
|
||||
@@ -193,6 +193,33 @@ func (s *ProxyServer) ServeMySQL(listener net.Listener) error {
|
||||
}
|
||||
}
|
||||
|
||||
// ServeMongo starts accepting Mongo client connections.
|
||||
func (s *ProxyServer) ServeMongo(listener net.Listener, tlsConfig *tls.Config) error {
|
||||
s.log.Debug("Started Mongo proxy.")
|
||||
defer s.log.Debug("Mongo proxy exited.")
|
||||
for {
|
||||
clientConn, err := listener.Accept()
|
||||
if err != nil {
|
||||
if utils.IsOKNetworkError(err) || trace.IsConnectionProblem(err) {
|
||||
return nil
|
||||
}
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
go func() {
|
||||
defer clientConn.Close()
|
||||
tlsConn := tls.Server(clientConn, tlsConfig)
|
||||
if err := tlsConn.Handshake(); err != nil {
|
||||
s.log.WithError(err).Error("Mongo TLS handshake failed.")
|
||||
return
|
||||
}
|
||||
err := s.handleConnection(tlsConn)
|
||||
if err != nil {
|
||||
s.log.WithError(err).Error("Failed to handle Mongo client connection.")
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
// ServeTLS starts accepting database connections that use plain TLS connection.
|
||||
func (s *ProxyServer) ServeTLS(listener net.Listener) error {
|
||||
s.log.Debug("Started database TLS proxy.")
|
||||
|
||||
Reference in New Issue
Block a user