Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 52 additions & 1 deletion integration/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import (

"github.com/google/uuid"
"github.com/gravitational/trace"
"github.com/gravitational/trace/trail"
"github.com/jonboulle/clockwork"
"github.com/pkg/sftp"
log "github.com/sirupsen/logrus"
Expand All @@ -61,9 +62,12 @@ import (

"github.com/gravitational/teleport"
"github.com/gravitational/teleport/api/breaker"
apiclient "github.com/gravitational/teleport/api/client"
"github.com/gravitational/teleport/api/client/proto"
"github.com/gravitational/teleport/api/constants"
"github.com/gravitational/teleport/api/defaults"
apidefaults "github.com/gravitational/teleport/api/defaults"
"github.com/gravitational/teleport/api/metadata"
tracessh "github.com/gravitational/teleport/api/observability/tracing/ssh"
"github.com/gravitational/teleport/api/profile"
"github.com/gravitational/teleport/api/types"
Expand Down Expand Up @@ -7581,7 +7585,7 @@ func testJoinOverReverseTunnelOnly(t *testing.T, suite *integrationTestSuite) {
for _, proxyProtocolEnabled := range []bool{false, true} {
t.Run(fmt.Sprintf("proxy protocol: %v", proxyProtocolEnabled), func(t *testing.T) {
lib.SetInsecureDevMode(true)
defer lib.SetInsecureDevMode(false)
t.Cleanup(func() { lib.SetInsecureDevMode(false) })

// Create a Teleport instance with Auth/Proxy.
mainConfig := suite.defaultServiceConfig()
Expand Down Expand Up @@ -7610,6 +7614,53 @@ func testJoinOverReverseTunnelOnly(t *testing.T, suite *integrationTestSuite) {
require.NoError(t, err, "Node failed to join over reverse tunnel")
})
}

// Assert that gRPC-based join methods work over reverse tunnel.
t.Run("gRPC join service", func(t *testing.T) {
lib.SetInsecureDevMode(true)
defer lib.SetInsecureDevMode(false)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit:

Suggested change
defer lib.SetInsecureDevMode(false)
t.Cleanup(func() { lib.SetInsecureDevMode(false) })


// Create a Teleport instance with Auth/Proxy.
mainConfig := suite.defaultServiceConfig()
mainConfig.Auth.Enabled = true

mainConfig.Proxy.Enabled = true
mainConfig.Proxy.DisableWebService = false
mainConfig.Proxy.DisableWebInterface = true

mainConfig.SSH.Enabled = false

main := suite.NewTeleportWithConfig(t, nil, nil, mainConfig)
t.Cleanup(func() { require.NoError(t, main.StopAll()) })

ctx, cancel := context.WithCancel(context.Background())
t.Cleanup(cancel)
dialer := apiclient.NewDialer(
ctx,
apidefaults.DefaultIdleTimeout,
apidefaults.DefaultIOTimeout,
)
tlsConfig := utils.TLSConfig(nil)
tlsConfig.InsecureSkipVerify = true
tlsConfig.NextProtos = []string{string(common.ProtocolProxyGRPCInsecure)}
conn, err := grpc.Dial(
main.ReverseTunnel,
grpc.WithContextDialer(apiclient.GRPCContextDialer(dialer)),
grpc.WithUnaryInterceptor(metadata.UnaryClientInterceptor),
grpc.WithStreamInterceptor(metadata.StreamClientInterceptor),
grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)),
)
require.NoError(t, err)
joinServiceClient := apiclient.NewJoinServiceClient(proto.NewJoinServiceClient(conn))
_, err = joinServiceClient.RegisterUsingAzureMethod(ctx, func(challenge string) (*proto.RegisterUsingAzureMethodRequest, error) {
return &proto.RegisterUsingAzureMethodRequest{
RegisterUsingTokenRequest: &types.RegisterUsingTokenRequest{},
}, nil
})
// We don't care about the join succeeding, we just want to confirm
// that gRPC works.
require.True(t, trace.IsBadParameter(trail.FromGRPC(err)), err)
})
}

func getRemoteAddrString(sshClientString string) string {
Expand Down
90 changes: 80 additions & 10 deletions lib/service/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -3286,16 +3286,23 @@ type proxyListeners struct {
kube net.Listener
db dbListeners
alpn net.Listener
proxyPeer net.Listener
// reverseTunnelALPN handles ALPN traffic on the reverse tunnel port when TLS routing
// is not enabled. It's used to redirect traffic on that port to the gRPC
// listener.
reverseTunnelALPN net.Listener
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can you add a short comment on why we add them and how is using them for the new listeners?

proxyPeer net.Listener
// grpcPublic receives gRPC traffic that has the TLS ALPN protocol common.ProtocolProxyGRPCInsecure. This
// listener is only enabled when TLS routing is enabled and does not enforce mTLS authentication since
// it's used to handle cluster join requests.
// listener does not enforce mTLS authentication since it's used to handle cluster join requests.
grpcPublic net.Listener
// grpcMTLS receives gRPC traffic that has the TLS ALPN protocol common.ProtocolProxyGRPCSecure. This
// listener is only enabled when TLS routing is enabled and the gRPC server will enforce mTLS authentication.
grpcMTLS net.Listener
reverseTunnelMux *multiplexer.Mux
minimalTLS *multiplexer.WebListener
// minimalWeb handles traffic on the reverse tunnel port when TLS routing
// is not enabled. It serves only the subset of web traffic required for
// agents to join the cluster.
minimalWeb net.Listener
minimalTLS *multiplexer.WebListener
}

// Close closes all proxy listeners.
Expand Down Expand Up @@ -3328,6 +3335,9 @@ func (l *proxyListeners) Close() {
if l.alpn != nil {
l.alpn.Close()
}
if l.reverseTunnelALPN != nil {
l.reverseTunnelALPN.Close()
}
if l.proxyPeer != nil {
l.proxyPeer.Close()
}
Expand All @@ -3340,6 +3350,9 @@ func (l *proxyListeners) Close() {
if l.reverseTunnelMux != nil {
l.reverseTunnelMux.Close()
}
if l.minimalWeb != nil {
l.minimalWeb.Close()
}
if l.minimalTLS != nil {
l.minimalTLS.Close()
}
Expand Down Expand Up @@ -3631,6 +3644,7 @@ func (process *TeleportProcess) initMinimalReverseTunnelListener(cfg *servicecfg
process.log.WithError(err).Debug("Minimal reverse tunnel mux exited with error")
}
}()
listeners.minimalWeb = listeners.reverseTunnelMux.TLS()
return nil
}

Expand Down Expand Up @@ -3754,7 +3768,7 @@ func (process *TeleportProcess) initProxyEndpoint(conn *Connector) error {
if err != nil {
return trace.Wrap(err)
}
alpnRouter := setupALPNRouter(listeners, serverTLSConfig, cfg)
alpnRouter, reverseTunnelALPNRouter := setupALPNRouter(listeners, serverTLSConfig, cfg)

alpnAddr := ""
if listeners.alpn != nil {
Expand Down Expand Up @@ -4498,6 +4512,7 @@ func (process *TeleportProcess) initProxyEndpoint(conn *Connector) error {
}

var alpnServer *alpnproxy.Proxy
var reverseTunnelALPNServer *alpnproxy.Proxy
if !cfg.Proxy.DisableTLS && !cfg.Proxy.DisableALPNSNIListener && listeners.web != nil {
authDialerService := alpnproxyauth.NewAuthProxyDialerService(
tsrv,
Expand Down Expand Up @@ -4538,6 +4553,28 @@ func (process *TeleportProcess) initProxyEndpoint(conn *Connector) error {
}
return nil
})

if reverseTunnelALPNRouter != nil {
reverseTunnelALPNServer, err = alpnproxy.New(alpnproxy.ProxyConfig{
WebTLSConfig: tlsConfigWeb.Clone(),
IdentityTLSConfig: identityTLSConf,
Router: reverseTunnelALPNRouter,
Listener: listeners.reverseTunnelALPN,
ClusterName: clusterName,
AccessPoint: accessPoint,
})
if err != nil {
return trace.Wrap(err)
}

process.RegisterCriticalFunc("proxy.tls.alpn.sni.proxy.reverseTunnel", func() error {
log.Infof("Starting TLS ALPN SNI reverse tunnel proxy server on %v.", listeners.reverseTunnelALPN.Addr())
if err := reverseTunnelALPNServer.Serve(process.ExitContext()); err != nil {
log.WithError(err).Warn("TLS ALPN SNI proxy proxy on reverse tunnel server exited with error.")
}
return nil
})
}
}

// execute this when process is asked to exit:
Expand Down Expand Up @@ -4578,6 +4615,9 @@ func (process *TeleportProcess) initProxyEndpoint(conn *Connector) error {
if alpnServer != nil {
warnOnErr(alpnServer.Close(), log)
}
if reverseTunnelALPNServer != nil {
warnOnErr(reverseTunnelALPNServer.Close(), log)
}
} else {
log.Infof("Shutting down gracefully.")
ctx := payloadContext(payload, log)
Expand Down Expand Up @@ -4613,6 +4653,9 @@ func (process *TeleportProcess) initProxyEndpoint(conn *Connector) error {
if alpnServer != nil {
warnOnErr(alpnServer.Close(), log)
}
if reverseTunnelALPNServer != nil {
warnOnErr(reverseTunnelALPNServer.Close(), log)
}

// Explicitly deleting proxy heartbeats helps the behavior of
// reverse tunnel agents during rollouts, as otherwise they'll keep
Expand Down Expand Up @@ -4654,7 +4697,7 @@ func (process *TeleportProcess) getPROXYSigner(ident *auth.Identity) (multiplexe
}

func (process *TeleportProcess) initMinimalReverseTunnel(listeners *proxyListeners, tlsConfigWeb *tls.Config, cfg *servicecfg.Config, webConfig web.Config, log *logrus.Entry) (*web.Server, error) {
internalListener := listeners.reverseTunnelMux.TLS()
internalListener := listeners.minimalWeb
if !cfg.Proxy.DisableTLS {
internalListener = tls.NewListener(internalListener, tlsConfigWeb)
}
Expand Down Expand Up @@ -4828,15 +4871,20 @@ func (process *TeleportProcess) setupALPNTLSConfigForWeb(serverTLSConfig *tls.Co
return tlsConfig
}

func setupALPNRouter(listeners *proxyListeners, serverTLSConfig *tls.Config, cfg *servicecfg.Config) *alpnproxy.Router {
func setupALPNRouter(listeners *proxyListeners, serverTLSConfig *tls.Config, cfg *servicecfg.Config) (router, rtRouter *alpnproxy.Router) {
if listeners.web == nil || cfg.Proxy.DisableTLS || cfg.Proxy.DisableALPNSNIListener {
return nil
return nil, nil
}
// ALPN proxy service will use web listener where listener.web will be overwritten by alpn wrapper
// that allows to dispatch the http/1.1 and h2 traffic to webService.
listeners.alpn = listeners.web
router = alpnproxy.NewRouter()

if listeners.minimalWeb != nil {
listeners.reverseTunnelALPN = listeners.minimalWeb
rtRouter = alpnproxy.NewRouter()
}

router := alpnproxy.NewRouter()
if cfg.Proxy.Kube.Enabled {
kubeListener := alpnproxy.NewMuxListenerWrapper(listeners.kube, listeners.web)
router.AddKubeHandler(kubeListener.HandleConnection)
Expand All @@ -4849,6 +4897,21 @@ func setupALPNRouter(listeners *proxyListeners, serverTLSConfig *tls.Config, cfg
Handler: reverseTunnel.HandleConnection,
})
listeners.reverseTunnel = reverseTunnel

if rtRouter != nil {
minimalWeb := alpnproxy.NewMuxListenerWrapper(nil, listeners.reverseTunnelALPN)
rtRouter.Add(alpnproxy.HandlerDecs{
MatchFunc: alpnproxy.MatchByProtocol(
alpncommon.ProtocolHTTP,
alpncommon.ProtocolHTTP2,
alpncommon.ProtocolDefault,
),
Handler: minimalWeb.HandleConnection,
ForwardTLS: true,
})
listeners.minimalWeb = minimalWeb
}

}

if !cfg.Proxy.DisableWebService {
Expand All @@ -4868,10 +4931,17 @@ func setupALPNRouter(listeners *proxyListeners, serverTLSConfig *tls.Config, cfg
// It must not be used for any services that require authentication and currently
// it is only used by the join service which nodes rely on to join the cluster.
grpcPublicListener := alpnproxy.NewMuxListenerWrapper(nil /* serviceListener */, listeners.web)
grpcPublicListener = alpnproxy.NewMuxListenerWrapper(grpcPublicListener, listeners.reverseTunnel)
router.Add(alpnproxy.HandlerDecs{
MatchFunc: alpnproxy.MatchByProtocol(alpncommon.ProtocolProxyGRPCInsecure),
Handler: grpcPublicListener.HandleConnection,
})
if rtRouter != nil {
rtRouter.Add(alpnproxy.HandlerDecs{
MatchFunc: alpnproxy.MatchByProtocol(alpncommon.ProtocolProxyGRPCInsecure),
Handler: grpcPublicListener.HandleConnection,
})
}
listeners.grpcPublic = grpcPublicListener

// grpcSecureListener is a listener that is used by a gRPC server that enforces
Expand Down Expand Up @@ -4907,7 +4977,7 @@ func setupALPNRouter(listeners *proxyListeners, serverTLSConfig *tls.Config, cfg
router.AddDBTLSHandler(webTLSDB.HandleConnection)
listeners.db.tls = webTLSDB

return router
return router, rtRouter
}

// waitForAppDepend waits until all dependencies for an application service
Expand Down