Fix tctl insecure flag when TLS Routing is enabled (#10297)

This commit is contained in:
Marek Smoliński 2022-02-15 10:45:47 +01:00 committed by GitHub
parent 9039b71ca3
commit 84b64fe487
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 134 additions and 34 deletions

View file

@ -278,7 +278,10 @@ func (a *Agent) connect() (conn *ssh.Client, err error) {
a.reverseTunnelDetails = a.getReverseTunnelDetails()
}
var opts []proxy.DialerOptionFunc
opts := []proxy.DialerOptionFunc{
proxy.WithInsecureSkipTLSVerify(lib.IsInsecureDevMode()),
}
if a.reverseTunnelDetails != nil && a.reverseTunnelDetails.TLSRoutingEnabled {
opts = append(opts, proxy.WithALPNDialer())
}

View file

@ -32,7 +32,6 @@ import (
apidefaults "github.com/gravitational/teleport/api/defaults"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/api/utils/sshutils"
"github.com/gravitational/teleport/lib"
"github.com/gravitational/teleport/lib/auth"
"github.com/gravitational/teleport/lib/events"
"github.com/gravitational/teleport/lib/utils"
@ -61,6 +60,8 @@ type TunnelAuthDialerConfig struct {
ClientConfig *ssh.ClientConfig
// Log is used for logging.
Log logrus.FieldLogger
// InsecureSkipTLSVerify is whether to skip certificate validation.
InsecureSkipTLSVerify bool
}
func (c *TunnelAuthDialerConfig) CheckAndSetDefaults() error {
@ -79,7 +80,9 @@ type TunnelAuthDialer struct {
// DialContext dials auth server via SSH tunnel
func (t *TunnelAuthDialer) DialContext(ctx context.Context, _, _ string) (net.Conn, error) {
// Connect to the reverse tunnel server.
var opts []proxy.DialerOptionFunc
opts := []proxy.DialerOptionFunc{
proxy.WithInsecureSkipTLSVerify(t.InsecureSkipTLSVerify),
}
addr, err := t.Resolver()
if err != nil {
@ -88,7 +91,7 @@ func (t *TunnelAuthDialer) DialContext(ctx context.Context, _, _ string) (net.Co
}
// Check if t.ProxyAddr is ProxyWebPort and remote Proxy supports TLS ALPNSNIListener.
resp, err := webclient.Find(ctx, addr.Addr, lib.IsInsecureDevMode(), nil)
resp, err := webclient.Find(ctx, addr.Addr, t.InsecureSkipTLSVerify, nil)
if err != nil {
// If TLS Routing is disabled the address is the proxy reverse tunnel
// address thus the ping call will always fail.

View file

@ -23,9 +23,11 @@ import (
"github.com/coreos/go-semver/semver"
"github.com/gravitational/roundtrip"
"github.com/gravitational/teleport"
"github.com/gravitational/trace"
"github.com/sirupsen/logrus"
"golang.org/x/crypto/ssh"
"github.com/gravitational/teleport"
apiclient "github.com/gravitational/teleport/api/client"
"github.com/gravitational/teleport/api/client/proto"
"github.com/gravitational/teleport/api/types"
@ -37,10 +39,6 @@ import (
"github.com/gravitational/teleport/lib/tlsca"
"github.com/gravitational/teleport/lib/utils"
"github.com/gravitational/teleport/lib/utils/interval"
"github.com/gravitational/trace"
"github.com/sirupsen/logrus"
)
// reconnectToAuthService continuously attempts to reconnect to the auth
@ -890,9 +888,10 @@ func (process *TeleportProcess) newClientThroughTunnel(authServers []utils.NetAd
}
dialer, err := reversetunnel.NewTunnelAuthDialer(reversetunnel.TunnelAuthDialerConfig{
Resolver: resolver,
ClientConfig: sshConfig,
Log: process.log,
Resolver: resolver,
ClientConfig: sshConfig,
Log: process.log,
InsecureSkipTLSVerify: lib.IsInsecureDevMode(),
})
if err != nil {
return nil, trace.Wrap(err)

View file

@ -52,7 +52,7 @@ type LocalProxyConfig struct {
RemoteProxyAddr string
// Protocol set for the upstream TLS connection.
Protocol common.Protocol
// Insecure turns off verification for x509 upstream ALPN proxy service certificate.
// InsecureSkipTLSVerify turns off verification for x509 upstream ALPN proxy service certificate.
InsecureSkipVerify bool
// Listener is listener running on local machine.
Listener net.Listener

View file

@ -30,7 +30,6 @@ import (
"github.com/gravitational/teleport"
"github.com/gravitational/teleport/api/utils/sshutils"
"github.com/gravitational/teleport/lib"
alpncommon "github.com/gravitational/teleport/lib/srv/alpnproxy/common"
"github.com/gravitational/teleport/lib/utils"
@ -57,7 +56,7 @@ func dialWithDeadline(network string, addr string, config *ssh.ClientConfig) (*s
// dialALPNWithDeadline allows connecting to Teleport in single-port mode. SSH protocol is wrapped into
// TLS connection where TLS ALPN protocol is set to ProtocolReverseTunnel allowing ALPN Proxy to route the
// incoming connection to ReverseTunnel proxy service.
func dialALPNWithDeadline(network string, addr string, config *ssh.ClientConfig) (*ssh.Client, error) {
func dialALPNWithDeadline(network string, addr string, config *ssh.ClientConfig, insecure bool) (*ssh.Client, error) {
dialer := &net.Dialer{
Timeout: config.Timeout,
}
@ -67,7 +66,7 @@ func dialALPNWithDeadline(network string, addr string, config *ssh.ClientConfig)
}
tlsConn, err := tls.DialWithDialer(dialer, network, addr, &tls.Config{
NextProtos: []string{string(alpncommon.ProtocolReverseTunnel)},
InsecureSkipVerify: lib.IsInsecureDevMode(),
InsecureSkipVerify: insecure,
ServerName: address.Host(),
})
if err != nil {
@ -86,13 +85,16 @@ type Dialer interface {
}
type directDial struct {
useTLS bool
// tlsRoutingEnabled indicates that proxy is running in TLSRouting mode.
tlsRoutingEnabled bool
// insecure is whether to skip certificate validation.
insecure bool
}
// Dial calls ssh.Dial directly.
func (d directDial) Dial(network string, addr string, config *ssh.ClientConfig) (*ssh.Client, error) {
if d.useTLS {
client, err := dialALPNWithDeadline(network, addr, config)
if d.tlsRoutingEnabled {
client, err := dialALPNWithDeadline(network, addr, config, d.insecure)
if err != nil {
return nil, trace.Wrap(err)
}
@ -107,14 +109,14 @@ func (d directDial) Dial(network string, addr string, config *ssh.ClientConfig)
// DialTimeout acts like Dial but takes a timeout.
func (d directDial) DialTimeout(network, address string, timeout time.Duration) (net.Conn, error) {
if d.useTLS {
if d.tlsRoutingEnabled {
addr, err := utils.ParseAddr(address)
if err != nil {
return nil, trace.Wrap(err)
}
tlsConn, err := tls.Dial("tcp", address, &tls.Config{
NextProtos: []string{string(alpncommon.ProtocolReverseTunnel)},
InsecureSkipVerify: lib.IsInsecureDevMode(),
InsecureSkipVerify: d.insecure,
ServerName: addr.Host(),
})
if err != nil {
@ -130,8 +132,12 @@ func (d directDial) DialTimeout(network, address string, timeout time.Duration)
}
type proxyDial struct {
// proxyHost is the HTTPS proxy address.
proxyHost string
useTLS bool
// tlsRoutingEnabled indicates that proxy is running in TLSRouting mode.
tlsRoutingEnabled bool
// insecure is whether to skip certificate validation.
insecure bool
}
// DialTimeout acts like Dial but takes a timeout.
@ -147,14 +153,14 @@ func (d proxyDial) DialTimeout(network, address string, timeout time.Duration) (
if err != nil {
return nil, trace.Wrap(err)
}
if d.useTLS {
if d.tlsRoutingEnabled {
address, err := utils.ParseAddr(address)
if err != nil {
return nil, trace.Wrap(err)
}
conn = tls.Client(conn, &tls.Config{
NextProtos: []string{string(alpncommon.ProtocolReverseTunnel)},
InsecureSkipVerify: lib.IsInsecureDevMode(),
InsecureSkipVerify: d.insecure,
ServerName: address.Host(),
})
}
@ -172,14 +178,14 @@ func (d proxyDial) Dial(network string, addr string, config *ssh.ClientConfig) (
if config.Timeout > 0 {
pconn.SetReadDeadline(time.Now().Add(config.Timeout))
}
if d.useTLS {
if d.tlsRoutingEnabled {
address, err := utils.ParseAddr(addr)
if err != nil {
return nil, trace.Wrap(err)
}
pconn = tls.Client(pconn, &tls.Config{
NextProtos: []string{string(alpncommon.ProtocolReverseTunnel)},
InsecureSkipVerify: lib.IsInsecureDevMode(),
InsecureSkipVerify: d.insecure,
ServerName: address.Host(),
})
}
@ -196,7 +202,10 @@ func (d proxyDial) Dial(network string, addr string, config *ssh.ClientConfig) (
}
type dialerOptions struct {
dialTLS bool
// tlsRoutingEnabled indicates that proxy is running in TLSRouting mode.
tlsRoutingEnabled bool
// insecureSkipTLSVerify is whether to skip certificate validation.
insecureSkipTLSVerify bool
}
// DialerOptionFunc allows setting options as functional arguments to DialerFromEnvironment
@ -205,7 +214,14 @@ type DialerOptionFunc func(options *dialerOptions)
// WithALPNDialer creates a dialer that allows to Teleport running in single-port mode.
func WithALPNDialer() DialerOptionFunc {
return func(options *dialerOptions) {
options.dialTLS = true
options.tlsRoutingEnabled = true
}
}
// WithInsecureSkipTLSVerify skips the certs verifications.
func WithInsecureSkipTLSVerify(insecure bool) DialerOptionFunc {
return func(options *dialerOptions) {
options.insecureSkipTLSVerify = insecure
}
}
@ -226,10 +242,17 @@ func DialerFromEnvironment(addr string, opts ...DialerOptionFunc) Dialer {
// otherwise return a proxy dialer.
if proxyAddr == "" {
log.Debugf("No proxy set in environment, returning direct dialer.")
return directDial{useTLS: options.dialTLS}
return directDial{
tlsRoutingEnabled: options.tlsRoutingEnabled,
insecure: options.insecureSkipTLSVerify,
}
}
log.Debugf("Found proxy %q in environment, returning proxy dialer.", proxyAddr)
return proxyDial{proxyHost: proxyAddr, useTLS: options.dialTLS}
return proxyDial{
proxyHost: proxyAddr,
tlsRoutingEnabled: options.tlsRoutingEnabled,
insecure: options.insecureSkipTLSVerify,
}
}
type DirectDialerOptFunc func(dial *directDial)

View file

@ -19,6 +19,7 @@ package common
import (
"bytes"
"context"
"crypto/x509"
"encoding/base64"
"encoding/json"
"io"
@ -37,7 +38,30 @@ import (
"gopkg.in/yaml.v2"
)
func runResourceCommand(t *testing.T, fc *config.FileConfig, args []string) (*bytes.Buffer, error) {
type options struct {
CertPool *x509.CertPool
Insecure bool
}
type optionsFunc func(o *options)
func withRootCertPool(pool *x509.CertPool) optionsFunc {
return func(o *options) {
o.CertPool = pool
}
}
func withInsecure(insecure bool) optionsFunc {
return func(o *options) {
o.Insecure = insecure
}
}
func runResourceCommand(t *testing.T, fc *config.FileConfig, args []string, opts ...optionsFunc) (*bytes.Buffer, error) {
var options options
for _, v := range opts {
v(&options)
}
var stdoutBuff bytes.Buffer
command := &ResourceCommand{
stdout: &stdoutBuff,
@ -52,10 +76,15 @@ func runResourceCommand(t *testing.T, fc *config.FileConfig, args []string) (*by
var ccf GlobalCLIFlags
ccf.ConfigString = mustGetBase64EncFileConfig(t, fc)
ccf.Insecure = options.Insecure
clientConfig, err := applyConfig(&ccf, cfg)
require.NoError(t, err)
if options.CertPool != nil {
clientConfig.TLS.RootCAs = options.CertPool
}
client, err := connectToAuthService(context.Background(), cfg, clientConfig)
require.NoError(t, err)

View file

@ -17,6 +17,7 @@ limitations under the License.
package common
import (
"crypto/x509"
"fmt"
"io/ioutil"
"path/filepath"
@ -284,6 +285,47 @@ func TestAppResource(t *testing.T) {
))
}
// TestCreateDatabaseInInsecureMode connects to auth server with --insecure mode and creates a DB resource.
func TestCreateDatabaseInInsecureMode(t *testing.T) {
fileConfig := &config.FileConfig{
Global: config.Global{
DataDir: t.TempDir(),
},
Databases: config.Databases{
Service: config.Service{
EnabledFlag: "true",
},
},
Proxy: config.Proxy{
Service: config.Service{
EnabledFlag: "true",
},
WebAddr: mustGetFreeLocalListenerAddr(t),
TunAddr: mustGetFreeLocalListenerAddr(t),
},
Auth: config.Auth{
Service: config.Service{
EnabledFlag: "true",
ListenAddress: mustGetFreeLocalListenerAddr(t),
},
},
}
makeAndRunTestAuthServer(t, withFileConfig(fileConfig))
// Create the databases yaml file.
dbYAMLPath := filepath.Join(t.TempDir(), "db.yaml")
require.NoError(t, ioutil.WriteFile(dbYAMLPath, []byte(dbYAML), 0644))
// Reset RootCertPool and run tctl command with --insecure flag.
opts := []optionsFunc{
withRootCertPool(x509.NewCertPool()),
withInsecure(true),
}
_, err := runResourceCommand(t, fileConfig, []string{"create", dbYAMLPath}, opts...)
require.NoError(t, err)
}
const (
dbYAML = `kind: db
version: v3

View file

@ -226,9 +226,10 @@ func connectToAuthService(ctx context.Context, cfg *service.Config, clientConfig
// reversetunnel.TunnelAuthDialer will take care of creating a net.Conn
// within an SSH tunnel.
dialer, err := reversetunnel.NewTunnelAuthDialer(reversetunnel.TunnelAuthDialerConfig{
Resolver: reversetunnel.WebClientResolver(ctx, cfg.AuthServers, clientConfig.TLS.InsecureSkipVerify),
ClientConfig: clientConfig.SSH,
Log: cfg.Log,
Resolver: reversetunnel.WebClientResolver(ctx, cfg.AuthServers, clientConfig.TLS.InsecureSkipVerify),
ClientConfig: clientConfig.SSH,
Log: cfg.Log,
InsecureSkipTLSVerify: clientConfig.TLS.InsecureSkipVerify,
})
if err != nil {
return nil, trace.Wrap(err)