diff --git a/docs/pages/setup/reference/config.mdx b/docs/pages/setup/reference/config.mdx index 78bf93f395522..4080d626c90c5 100644 --- a/docs/pages/setup/reference/config.mdx +++ b/docs/pages/setup/reference/config.mdx @@ -65,8 +65,8 @@ By default, it is stored in `/etc/teleport.yaml`. ```yaml # By default, this file should be stored in /etc/teleport.yaml -# Configuration file version. The current version is "v2". -version: v2 +# Configuration file version. The current version is "v3". +version: v3 # This section of the configuration file applies to all teleport # services. @@ -131,12 +131,16 @@ teleport: # Auth Server address and port to connect to. If you enable the Teleport # Auth Server to run in High Availability configuration, the address should # point to a Load Balancer. - # If adding a node located behind NAT, use the Proxy URL. e.g. - # auth_servers: - # - teleport-proxy.example.com:443 + # If adding a node located behind NAT, specify `proxy_servers` instead auth_servers: - 10.1.0.5:3025 + # Proxy Server address and port to connect to. If you enable the Teleport + # Proxy Server to run in High Availability configuration, the address should + # point to a Load Balancer. + proxy_servers: + - teleport-proxy.example.com:443 + # cache: # # The cache is enabled by default, it can be disabled with this flag # enabled: true diff --git a/integration/ec2_test.go b/integration/ec2_test.go index bdc279fc681ba..16e35b1e1c6e9 100644 --- a/integration/ec2_test.go +++ b/integration/ec2_test.go @@ -71,7 +71,7 @@ func newNodeConfig(t *testing.T, authAddr utils.NetAddr, tokenName string, joinM func newProxyConfig(t *testing.T, authAddr utils.NetAddr, tokenName string, joinMethod types.JoinMethod) *service.Config { config := service.MakeDefaultConfig() - config.Version = defaults.TeleportConfigVersionV2 + config.Version = defaults.TeleportConfigVersionV3 config.SetToken(tokenName) config.JoinMethod = joinMethod config.SSH.Enabled = false diff --git a/integration/proxy_test.go b/integration/proxy_test.go index f262779b6779a..d6765dc8d0ad5 100644 --- a/integration/proxy_test.go +++ b/integration/proxy_test.go @@ -363,10 +363,10 @@ func TestALPNSNIProxyKubeV2Leaf(t *testing.T) { suite := newProxySuite(t, withRootClusterConfig(rootClusterStandardConfig(t), func(config *service.Config) { config.Proxy.Kube.Enabled = true - config.Version = defaults.TeleportConfigVersionV2 + config.Version = defaults.TeleportConfigVersionV3 }), withLeafClusterConfig(leafClusterStandardConfig(t), func(config *service.Config) { - config.Version = defaults.TeleportConfigVersionV2 + config.Version = defaults.TeleportConfigVersionV3 config.Proxy.Kube.Enabled = true config.Kube.Enabled = true diff --git a/lib/config/configuration.go b/lib/config/configuration.go index 8bd7680e3520a..937b23daa1eb4 100644 --- a/lib/config/configuration.go +++ b/lib/config/configuration.go @@ -68,6 +68,8 @@ type CommandLineFlags struct { NodeName string // --auth-server flag AuthServerAddr []string + // --proxy-server flag + ProxyServerAddr []string // --token flag AuthToken string // CAPins are the SKPI hashes of the CAs used to verify the Auth Server. @@ -268,6 +270,22 @@ func ApplyFileConfig(fc *FileConfig, cfg *service.Config) error { } } + // config file has auth servers in there? + if len(fc.ProxyServers) > 0 { + cfg.ProxyServers = make([]utils.NetAddr, 0, len(fc.ProxyServers)) + for _, as := range fc.ProxyServers { + addr, err := utils.ParseHostPortAddr(as, defaults.AuthListenPort) + if err != nil { + return trace.Wrap(err) + } + + if err != nil { + return trace.Errorf("cannot parse proxy server address: '%v'", as) + } + cfg.ProxyServers = append(cfg.ProxyServers, *addr) + } + } + if err := applyTokenConfig(fc, cfg); err != nil { return trace.Wrap(err) } @@ -855,8 +873,8 @@ func applyProxyConfig(fc *FileConfig, cfg *service.Config) error { case legacyKube && newKube: return trace.BadParameter("proxy_service should either set kube_listen_addr/kube_public_addr or kubernetes.enabled, not both; keep kubernetes.enabled if you don't enable kubernetes_service, or keep kube_listen_addr otherwise") case !legacyKube && !newKube: - if fc.Version == defaults.TeleportConfigVersionV2 { - // Always enable kube service if using config V2 (TLS routing is supported) + if fc.Version != defaults.TeleportConfigVersionV1 { + // Always enable kube service if using config version 2 onwards (TLS routing is supported) cfg.Proxy.Kube.Enabled = true } } @@ -953,8 +971,8 @@ func getPostgresDefaultPort(cfg *service.Config) int { } func applyDefaultProxyListenerAddresses(cfg *service.Config) { - if cfg.Version == defaults.TeleportConfigVersionV2 { - // For v2 configuration if an address is not provided don't fallback to the default values. + if cfg.Version != defaults.TeleportConfigVersionV1 { + // From v2 onwards, if an address is not provided don't fallback to the default values. return } diff --git a/lib/config/configuration_test.go b/lib/config/configuration_test.go index dc5b8805fb0b1..e887c0b5b9ecb 100644 --- a/lib/config/configuration_test.go +++ b/lib/config/configuration_test.go @@ -1753,6 +1753,15 @@ func TestProxyKube(t *testing.T) { }, checkErr: require.NoError, }, + { + desc: "v3 kube service should be enabled by default", + version: defaults.TeleportConfigVersionV3, + cfg: Proxy{}, + want: service.KubeProxyConfig{ + Enabled: true, + }, + checkErr: require.NoError, + }, { desc: "v2 kube service should be enabled by default", version: defaults.TeleportConfigVersionV2, @@ -1788,9 +1797,9 @@ func TestProxyConfigurationVersion(t *testing.T) { checkErr require.ErrorAssertionFunc }{ { - desc: "v2 config with default web address", + desc: "v3 config with default web address", fc: FileConfig{ - Version: defaults.TeleportConfigVersionV2, + Version: defaults.TeleportConfigVersionV3, Proxy: Proxy{ Service: Service{ defaultEnabled: true, @@ -1811,6 +1820,31 @@ func TestProxyConfigurationVersion(t *testing.T) { }, checkErr: require.NoError, }, + { + desc: "v3 config with custom web address", + fc: FileConfig{ + Version: defaults.TeleportConfigVersionV3, + Proxy: Proxy{ + Service: Service{ + defaultEnabled: true, + }, + WebAddr: "0.0.0.0:9999", + }, + }, + want: service.ProxyConfig{ + Enabled: true, + EnableProxyProtocol: true, + WebAddr: *utils.MustParseAddr("0.0.0.0:9999"), + Kube: service.KubeProxyConfig{ + Enabled: true, + }, + Limiter: limiter.Config{ + MaxConnections: defaults.LimiterMaxConnections, + MaxNumberOfUsers: 250, + }, + }, + checkErr: require.NoError, + }, { desc: "v2 config with custom web address", fc: FileConfig{ diff --git a/lib/config/fileconf.go b/lib/config/fileconf.go index 0b283e0b23f06..c092144e9467f 100644 --- a/lib/config/fileconf.go +++ b/lib/config/fileconf.go @@ -155,6 +155,8 @@ type SampleFlags struct { Roles string // AuthServer is the address of the auth server AuthServer string + // ProxyServer is the address of the proxy server + ProxyServer string // AppName is the name of the application to start AppName string // AppURI is the internal address of the application to proxy @@ -217,6 +219,10 @@ func MakeSampleFileConfig(flags SampleFlags) (fc *FileConfig, err error) { g.AuthServers = []string{flags.AuthServer} } + if flags.ProxyServer != "" { + g.ProxyServers = []string{flags.ProxyServer} + } + g.CAPin = strings.Split(flags.CAPin, ",") roles := roleMapFromFlags(flags) @@ -306,7 +312,7 @@ func makeSampleAuthConfig(conf *service.Config, flags SampleFlags, enabled bool) a.LicenseFile = flags.LicensePath } - if flags.Version == defaults.TeleportConfigVersionV2 { + if flags.Version != defaults.TeleportConfigVersionV1 { a.ProxyListenerMode = types.ProxyListenerMode_Multiplex } } else { @@ -559,15 +565,16 @@ type Global struct { // AuthToken is the old way of configuring the token to be used by the // node to join the Teleport cluster. `JoinParams.TokenName` should be // used instead with `JoinParams.JoinMethod = types.JoinMethodToken`. - AuthToken string `yaml:"auth_token,omitempty"` - JoinParams JoinParams `yaml:"join_params,omitempty"` - AuthServers []string `yaml:"auth_servers,omitempty"` - Limits ConnectionLimits `yaml:"connection_limits,omitempty"` - Logger Log `yaml:"log,omitempty"` - Storage backend.Config `yaml:"storage,omitempty"` - AdvertiseIP string `yaml:"advertise_ip,omitempty"` - CachePolicy CachePolicy `yaml:"cache,omitempty"` - SeedConfig *bool `yaml:"seed_config,omitempty"` + AuthToken string `yaml:"auth_token,omitempty"` + JoinParams JoinParams `yaml:"join_params,omitempty"` + AuthServers []string `yaml:"auth_servers,omitempty"` + ProxyServers []string `yaml:"proxy_servers,omitempty"` + Limits ConnectionLimits `yaml:"connection_limits,omitempty"` + Logger Log `yaml:"log,omitempty"` + Storage backend.Config `yaml:"storage,omitempty"` + AdvertiseIP string `yaml:"advertise_ip,omitempty"` + CachePolicy CachePolicy `yaml:"cache,omitempty"` + SeedConfig *bool `yaml:"seed_config,omitempty"` // CipherSuites is a list of TLS ciphersuites that Teleport supports. If // omitted, a Teleport selected list of defaults will be used. diff --git a/lib/defaults/defaults.go b/lib/defaults/defaults.go index 107f14f0ac7bb..92a5d070971f2 100644 --- a/lib/defaults/defaults.go +++ b/lib/defaults/defaults.go @@ -764,13 +764,24 @@ func Transport() (*http.Transport, error) { return tr, nil } +// When adding a new version, please add it to TeleportVersions below const ( // TeleportConfigVersionV1 is the teleport proxy configuration v1 version. TeleportConfigVersionV1 string = "v1" // TeleportConfigVersionV2 is the teleport proxy configuration v2 version. TeleportConfigVersionV2 string = "v2" + // TeleportConfigVersionV3 is the teleport proxy configuration v3 version. + TeleportConfigVersionV3 string = "v3" ) +// TeleportVersions is an exported slice of the allowed versions in the config file, +//for convenience (looping through, etc) +var TeleportVersions = []string{ + TeleportConfigVersionV1, + TeleportConfigVersionV2, + TeleportConfigVersionV3, +} + // Default values for tsh and tctl commands. const ( TshTctlSessionListLimit = "50" diff --git a/lib/service/cfg.go b/lib/service/cfg.go index d3acc4bc0551b..dfbeda08ab99e 100644 --- a/lib/service/cfg.go +++ b/lib/service/cfg.go @@ -90,11 +90,15 @@ type Config struct { // JoinMethod is the method the instance will use to join the auth server JoinMethod types.JoinMethod - // AuthServers is a list of auth servers, proxies and peer auth servers to - // connect to. Yes, this is not just auth servers, the field name is - // misleading. + // AuthServers is a list of auth servers and peer auth servers to + // connect to. + // Previously, proxy servers could be passed here, but going foward this + //should only be auth servers AuthServers []utils.NetAddr + // ProxyServers is a list of proxies to tunnel to + ProxyServers []utils.NetAddr + // Identities is an optional list of pre-generated key pairs // for teleport roles, this is helpful when server is preconfigured Identities []*auth.Identity diff --git a/lib/service/connect.go b/lib/service/connect.go index c1fa151d59611..68d7a20d4bec7 100644 --- a/lib/service/connect.go +++ b/lib/service/connect.go @@ -252,7 +252,7 @@ func (process *TeleportProcess) connect(role types.SystemRole, opts ...certOptio }, nil } process.log.Infof("Connecting to the cluster %v with TLS client certificate.", identity.ClusterName) - clt, err := process.newClient(process.Config.AuthServers, identity) + clt, err := process.newClient(process.Config.AuthServers, process.Config.ProxyServers, identity) if err != nil { // In the event that a user is attempting to connect a machine to // a different cluster it will give a cryptic warning about an @@ -281,7 +281,7 @@ func (process *TeleportProcess) connect(role types.SystemRole, opts ...certOptio ServerIdentity: identity, }, nil } - clt, err := process.newClient(process.Config.AuthServers, identity) + clt, err := process.newClient(process.Config.AuthServers, process.Config.ProxyServers, identity) if err != nil { return nil, trace.Wrap(err) } @@ -303,7 +303,7 @@ func (process *TeleportProcess) connect(role types.SystemRole, opts ...certOptio ServerIdentity: identity, }, nil } - clt, err := process.newClient(process.Config.AuthServers, newIdentity) + clt, err := process.newClient(process.Config.AuthServers, process.Config.ProxyServers, newIdentity) if err != nil { return nil, trace.Wrap(err) } @@ -325,7 +325,7 @@ func (process *TeleportProcess) connect(role types.SystemRole, opts ...certOptio ServerIdentity: newIdentity, }, nil } - clt, err := process.newClient(process.Config.AuthServers, newIdentity) + clt, err := process.newClient(process.Config.AuthServers, process.Config.ProxyServers, newIdentity) if err != nil { return nil, trace.Wrap(err) } @@ -345,7 +345,7 @@ func (process *TeleportProcess) connect(role types.SystemRole, opts ...certOptio ServerIdentity: identity, }, nil } - clt, err := process.newClient(process.Config.AuthServers, identity) + clt, err := process.newClient(process.Config.AuthServers, process.Config.ProxyServers, identity) if err != nil { return nil, trace.Wrap(err) } @@ -510,7 +510,7 @@ func (process *TeleportProcess) firstTimeConnectWithAssertions(role types.System } process.deleteKeyPair(role, reason) - clt, err := process.newClient(process.Config.AuthServers, identity) + clt, err := process.newClient(process.Config.AuthServers, process.Config.ProxyServers, identity) if err != nil { return nil, trace.Wrap(err) } @@ -626,7 +626,7 @@ func (process *TeleportProcess) firstTimeConnect(role types.SystemRole) (*Connec ServerIdentity: identity, } } else { - clt, err := process.newClient(process.Config.AuthServers, identity) + clt, err := process.newClient(process.Config.AuthServers, process.Config.ProxyServers, identity) if err != nil { return nil, trace.Wrap(err) } @@ -1041,12 +1041,34 @@ func (process *TeleportProcess) rotate(conn *Connector, localState auth.StateV2, // falls back to trying to connect to the Auth Server through the proxy. // The proxy address might be configured in process environment as apidefaults.TunnelPublicAddrEnvar // in which case, no attempt at discovering the reverse tunnel address is made. -func (process *TeleportProcess) newClient(authServers []utils.NetAddr, identity *auth.Identity) (*auth.Client, error) { +func (process *TeleportProcess) newClient(authServers, proxyServers []utils.NetAddr, identity *auth.Identity) (*auth.Client, error) { tlsConfig, err := identity.TLSConfig(process.Config.CipherSuites) if err != nil { return nil, trace.Wrap(err) } + sshClientConfig, err := identity.SSHClientConfig(process.Config.FIPS) + if err != nil { + return nil, trace.Wrap(err) + } + + // try and connect to the proxy server first to avoid the extra direct dial when using the value from auth servers + if len(proxyServers) > 0 { + logger := process.log.WithField("proxy-addrs", utils.NetAddrsToStrings(proxyServers)) + logger.Debug("Attempting to connect to Proxy Server through tunnel.") + + tunnelClient, err := process.newClientThroughTunnel(proxyServers, tlsConfig, sshClientConfig) + if err != nil { + logger.WithError(err).Debug("Failed to connect to Proxy Server through tunnel.") + return nil, trace.Errorf("Failed to connect to Proxy Server through tunnel, no methods remaining.") + } + + logger.Debug("Connected to Proxy Server through tunnel.") + + return tunnelClient, nil + } + + // we can assume there are auth servers specified logger := process.log.WithField("auth-addrs", utils.NetAddrsToStrings(authServers)) logger.Debug("Attempting to connect to Auth Server directly.") directClient, directErr := process.newClientDirect(authServers, tlsConfig, identity.ID.Role) @@ -1054,6 +1076,7 @@ func (process *TeleportProcess) newClient(authServers []utils.NetAddr, identity logger.Debug("Connected to Auth Server with direct connection.") return directClient, nil } + logger.Debug("Failed to connect to Auth Server directly.") // Don't attempt to connect through a tunnel as a proxy or auth server. @@ -1063,11 +1086,8 @@ func (process *TeleportProcess) newClient(authServers []utils.NetAddr, identity logger.Debug("Attempting to discover reverse tunnel address.") - logger.Debug("Attempting to connect to Auth Server through tunnel.") - sshClientConfig, err := identity.SSHClientConfig(process.Config.FIPS) - if err != nil { - return nil, trace.Wrap(err) - } + logger.Debug("Attempting to connect to Proxy Server through tunnel.") + tunnelClient, err := process.newClientThroughTunnel(authServers, tlsConfig, sshClientConfig) if err != nil { process.log.Errorf("Node failed to establish connection to Teleport Proxy. We have tried the following endpoints:") @@ -1083,10 +1103,14 @@ func (process *TeleportProcess) newClient(authServers []utils.NetAddr, identity process.log.Errorf("- connecting to auth server through tunnel: %v", err) return nil, trace.WrapWithMessage( trace.NewAggregate(directErr, err), - trace.Errorf("Failed to connect to Auth Server directly or over tunnel, no methods remaining.")) + trace.Errorf("Failed to connect to the Auth Server or Proxy Server directly or over tunnel, no methods remaining.")) } + logger.Warn("TIP: Upgrade your config to v3 and specify proxy_address in your config") + logger.Warn("See: https://goteleport.com/docs/link-here") + logger.Debug("Connected to Auth Server through tunnel.") + return tunnelClient, nil } diff --git a/lib/service/proxy_settings.go b/lib/service/proxy_settings.go index 1e85d4e5a9b28..9a99394e9ec0f 100644 --- a/lib/service/proxy_settings.go +++ b/lib/service/proxy_settings.go @@ -47,12 +47,11 @@ func (p *proxySettings) GetProxySettings(ctx context.Context) (*webclient.ProxyS return nil, trace.Wrap(err) } - switch p.cfg.Version { - case defaults.TeleportConfigVersionV2: - return p.buildProxySettingsV2(resp.GetProxyListenerMode()), nil - default: + if p.cfg.Version == defaults.TeleportConfigVersionV1 { return p.buildProxySettings(resp.GetProxyListenerMode()), nil } + + return p.buildProxySettingsV2(resp.GetProxyListenerMode()), nil } // buildProxySettings builds standard proxy configuration where proxy services are diff --git a/lib/service/service.go b/lib/service/service.go index e25d248202120..6276f7aea8f17 100644 --- a/lib/service/service.go +++ b/lib/service/service.go @@ -4555,47 +4555,6 @@ func (process *TeleportProcess) Close() error { return trace.NewAggregate(errors...) } -func validateConfig(cfg *Config) error { - if !cfg.Auth.Enabled && !cfg.SSH.Enabled && !cfg.Proxy.Enabled && !cfg.Kube.Enabled && !cfg.Apps.Enabled && !cfg.Databases.Enabled && !cfg.WindowsDesktop.Enabled { - return trace.BadParameter( - "config: enable at least one of auth_service, ssh_service, proxy_service, app_service, database_service, kubernetes_service or windows_desktop_service") - } - - if cfg.DataDir == "" { - return trace.BadParameter("config: please supply data directory") - } - - if cfg.Console == nil { - cfg.Console = io.Discard - } - - if cfg.Log == nil { - cfg.Log = logrus.StandardLogger() - } - - if len(cfg.AuthServers) == 0 { - return trace.BadParameter("auth_servers is empty") - } - for i := range cfg.Auth.Authorities { - if err := services.ValidateCertAuthority(cfg.Auth.Authorities[i]); err != nil { - return trace.Wrap(err) - } - } - for _, tun := range cfg.ReverseTunnels { - if err := services.ValidateReverseTunnel(tun); err != nil { - return trace.Wrap(err) - } - } - - if cfg.PollingPeriod == 0 { - cfg.PollingPeriod = defaults.LowResPollingPeriod - } - - cfg.SSH.Namespace = types.ProcessNamespace(cfg.SSH.Namespace) - - return nil -} - // initSelfSignedHTTPSCert generates and self-signs a TLS key+cert pair for https connection // to the proxy server. func initSelfSignedHTTPSCert(cfg *Config) (err error) { diff --git a/lib/service/validateconfig.go b/lib/service/validateconfig.go new file mode 100644 index 0000000000000..c4c4e8cdc4bb7 --- /dev/null +++ b/lib/service/validateconfig.go @@ -0,0 +1,143 @@ +/* + * + * Copyright 2015-2022 Gravitational, Inc. + * + * 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. + * / + * + */ + +package service + +import ( + "io" + "strings" + + "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/api/utils" + "github.com/gravitational/teleport/lib/defaults" + "github.com/gravitational/teleport/lib/services" + "github.com/gravitational/trace" + + "github.com/sirupsen/logrus" +) + +func validateConfig(cfg *Config) error { + applyDefaults(cfg) + + if err := validateVersion(cfg); err != nil { + return err + } + + if err := verifyEnabledService(cfg); err != nil { + return err + } + + if err := validateAuthOrProxyServices(cfg); err != nil { + return err + } + + if cfg.DataDir == "" { + return trace.BadParameter("config: please supply data directory") + } + + for i := range cfg.Auth.Authorities { + if err := services.ValidateCertAuthority(cfg.Auth.Authorities[i]); err != nil { + return trace.Wrap(err) + } + } + + for _, tun := range cfg.ReverseTunnels { + if err := services.ValidateReverseTunnel(tun); err != nil { + return trace.Wrap(err) + } + } + + cfg.SSH.Namespace = types.ProcessNamespace(cfg.SSH.Namespace) + + return nil +} + +func applyDefaults(cfg *Config) { + if cfg.Version == "" { + cfg.Version = defaults.TeleportConfigVersionV3 + } + + if cfg.Console == nil { + cfg.Console = io.Discard + } + + if cfg.Log == nil { + cfg.Log = logrus.StandardLogger() + } + + if cfg.PollingPeriod == 0 { + cfg.PollingPeriod = defaults.LowResPollingPeriod + } +} + +func validateAuthOrProxyServices(cfg *Config) error { + haveAuthServers := len(cfg.AuthServers) > 0 + haveProxyServers := len(cfg.ProxyServers) > 0 + + if haveProxyServers && cfg.Version != defaults.TeleportConfigVersionV3 { + return trace.BadParameter("config: proxy_servers is supported from config version v3 onwards") + } + + if haveAuthServers && haveProxyServers { + return trace.BadParameter("config: cannot use both auth_servers and proxy_servers") + } + + if !haveAuthServers && !haveProxyServers { + return trace.BadParameter("config: auth_servers or proxy_servers is required") + } + + return nil +} + +func validateVersion(cfg *Config) error { + hasVersion := utils.SliceContainsStr(defaults.TeleportVersions, cfg.Version) + if !hasVersion { + return trace.BadParameter("config: version must be one of %s", strings.Join(defaults.TeleportVersions, ", ")) + } + + return nil +} + +func verifyEnabledService(cfg *Config) error { + enabled := []bool{ + cfg.Auth.Enabled, + cfg.SSH.Enabled, + cfg.Proxy.Enabled, + cfg.Kube.Enabled, + cfg.Apps.Enabled, + cfg.Databases.Enabled, + cfg.WindowsDesktop.Enabled, + } + + has := false + for _, item := range enabled { + if item { + has = true + + break + } + } + + if !has { + return trace.BadParameter( + "config: enable at least one of auth_service, ssh_service, proxy_service, app_service, database_service, kubernetes_service or windows_desktop_service") + } + + return nil +} diff --git a/lib/service/validateconfig_test.go b/lib/service/validateconfig_test.go new file mode 100644 index 0000000000000..91d1d18f501c5 --- /dev/null +++ b/lib/service/validateconfig_test.go @@ -0,0 +1,85 @@ +/* + * + * Copyright 2015-2022 Gravitational, Inc. + * + * 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. + * / + * + */ + +package service + +import ( + "fmt" + "strings" + "testing" + + "github.com/gravitational/teleport/lib/defaults" + "github.com/gravitational/teleport/lib/utils" + + "github.com/stretchr/testify/require" +) + +func TestValidateConfig(t *testing.T) { + tests := []struct { + desc string + config *Config + err string + }{ + { + desc: "invalid version", + config: &Config{ + Version: "v1.1", + }, + err: fmt.Sprintf("config: version must be one of %s", strings.Join(defaults.TeleportVersions, ", ")), + }, + { + desc: "no service enabled", + config: &Config{ + Version: defaults.TeleportConfigVersionV2, + }, + err: "config: enable at least one of auth_service, ssh_service, proxy_service, app_service, database_service, kubernetes_service or windows_desktop_service", + }, + { + desc: "no auth_servers or proxy_servers specified", + config: &Config{ + Version: defaults.TeleportConfigVersionV2, + Auth: AuthConfig{ + Enabled: true, + }, + }, + err: "config: auth_servers or proxy_servers is required", + }, + { + desc: "specifying proxy_servers with the wrong config version", + config: &Config{ + Version: defaults.TeleportConfigVersionV2, + Auth: AuthConfig{ + Enabled: true, + }, + ProxyServers: []utils.NetAddr{ + *utils.MustParseAddr("0.0.0.0"), + }, + }, + err: "config: proxy_servers is supported from config version v3 onwards", + }, + } + + for _, test := range tests { + t.Run(test.desc, func(t *testing.T) { + if err := validateConfig(test.config); err != nil { + require.Equal(t, test.err, err) + } + }) + } +} diff --git a/tool/teleport/common/teleport.go b/tool/teleport/common/teleport.go index 1d97313e2a76a..299f778ec5ea3 100644 --- a/tool/teleport/common/teleport.go +++ b/tool/teleport/common/teleport.go @@ -28,6 +28,7 @@ import ( "strings" "github.com/gravitational/teleport" + apiutils "github.com/gravitational/teleport/api/utils" "github.com/gravitational/teleport/lib/config" dbconfigurators "github.com/gravitational/teleport/lib/configurators/databases" "github.com/gravitational/teleport/lib/defaults" @@ -112,6 +113,9 @@ func Run(options Options) (app *kingpin.Application, executedCommand string, con start.Flag("auth-server", fmt.Sprintf("Address of the auth server [%s]", defaults.AuthConnectAddr().Addr)). StringsVar(&ccf.AuthServerAddr) + start.Flag("proxy-server", + "Address of the proxy server [none]"). + StringsVar(&ccf.ProxyServerAddr) start.Flag("token", "Invitation token to register with an auth server [none]"). StringVar(&ccf.AuthToken) @@ -182,6 +186,7 @@ func Run(options Options) (app *kingpin.Application, executedCommand string, con appStartCmd.Flag("debug", "Enable verbose logging to stderr.").Short('d').BoolVar(&ccf.Debug) appStartCmd.Flag("pid-file", "Full path to the PID file. By default no PID file will be created.").StringVar(&ccf.PIDFile) appStartCmd.Flag("auth-server", fmt.Sprintf("Address of the auth server [%s].", defaults.AuthConnectAddr().Addr)).StringsVar(&ccf.AuthServerAddr) + appStartCmd.Flag("proxy-server", "Address of the proxy server [none].").StringsVar(&ccf.ProxyServerAddr) appStartCmd.Flag("token", "Invitation token to register with an auth server [none].").StringVar(&ccf.AuthToken) appStartCmd.Flag("ca-pin", "CA pin to validate the auth server (can be repeated for multiple pins).").StringsVar(&ccf.CAPins) appStartCmd.Flag("config", fmt.Sprintf("Path to a configuration file [%v].", defaults.ConfigFilePath)).Short('c').ExistingFileVar(&ccf.ConfigFile) @@ -202,6 +207,7 @@ func Run(options Options) (app *kingpin.Application, executedCommand string, con dbStartCmd.Flag("debug", "Enable verbose logging to stderr.").Short('d').BoolVar(&ccf.Debug) dbStartCmd.Flag("pid-file", "Full path to the PID file. By default no PID file will be created.").StringVar(&ccf.PIDFile) dbStartCmd.Flag("auth-server", fmt.Sprintf("Address of the auth server [%s].", defaults.AuthConnectAddr().Addr)).StringsVar(&ccf.AuthServerAddr) + dbStartCmd.Flag("proxy-server", "Address of the proxy server [none].").StringsVar(&ccf.ProxyServerAddr) dbStartCmd.Flag("token", "Invitation token to register with an auth server [none].").StringVar(&ccf.AuthToken) dbStartCmd.Flag("ca-pin", "CA pin to validate the auth server (can be repeated for multiple pins).").StringsVar(&ccf.CAPins) dbStartCmd.Flag("config", fmt.Sprintf("Path to a configuration file [%v].", defaults.ConfigFilePath)).Short('c').ExistingFileVar(&ccf.ConfigFile) @@ -320,7 +326,7 @@ func Run(options Options) (app *kingpin.Application, executedCommand string, con dump.Flag("acme-email", "Email to receive updates from Letsencrypt.org.").StringVar(&dumpFlags.ACMEEmail) dump.Flag("test", "Path to a configuration file to test.").ExistingFileVar(&dumpFlags.testConfigFile) - dump.Flag("version", "Teleport configuration version.").Default(defaults.TeleportConfigVersionV2).StringVar(&dumpFlags.Version) + dump.Flag("version", "Teleport configuration version.").Default(defaults.TeleportConfigVersionV3).StringVar(&dumpFlags.Version) dump.Flag("public-addr", "The hostport that the proxy advertises for the HTTP endpoint.").StringVar(&dumpFlags.PublicAddr) dump.Flag("cert-file", "Path to a TLS certificate file for the proxy.").ExistingFileVar(&dumpFlags.CertFile) dump.Flag("key-file", "Path to a TLS key file for the proxy.").ExistingFileVar(&dumpFlags.KeyFile) @@ -328,6 +334,7 @@ func Run(options Options) (app *kingpin.Application, executedCommand string, con dump.Flag("token", "Invitation token to register with an auth server.").StringVar(&dumpFlags.AuthToken) dump.Flag("roles", "Comma-separated list of roles to create config with.").StringVar(&dumpFlags.Roles) dump.Flag("auth-server", "Address of the auth server.").StringVar(&dumpFlags.AuthServer) + dump.Flag("proxy-server", "Address of the proxy server.").StringVar(&dumpFlags.ProxyServer) dump.Flag("app-name", "Name of the application to start when using app role.").StringVar(&dumpFlags.AppName) dump.Flag("app-uri", "Internal address of the application to proxy.").StringVar(&dumpFlags.AppURI) dump.Flag("node-labels", "Comma-separated list of labels to add to newly created nodes, for example env=staging,cloud=aws.").StringVar(&dumpFlags.NodeLabels) @@ -339,11 +346,12 @@ func Run(options Options) (app *kingpin.Application, executedCommand string, con dumpNodeConfigure.Flag("output", "Write to stdout with -o=stdout, default config file with -o=file or custom path with -o=file:///path").Short('o').Default( teleport.SchemeStdout).StringVar(&dumpFlags.output) - dumpNodeConfigure.Flag("version", "Teleport configuration version.").Default(defaults.TeleportConfigVersionV2).StringVar(&dumpFlags.Version) + dumpNodeConfigure.Flag("version", "Teleport configuration version.").Default(defaults.TeleportConfigVersionV3).StringVar(&dumpFlags.Version) dumpNodeConfigure.Flag("public-addr", "The hostport that the node advertises for the SSH endpoint.").StringVar(&dumpFlags.PublicAddr) dumpNodeConfigure.Flag("data-dir", "Path to a directory where Teleport keep its data.").Default(defaults.DataDir).StringVar(&dumpFlags.DataDir) dumpNodeConfigure.Flag("token", "Invitation token to register with an auth server.").StringVar(&dumpFlags.AuthToken) dumpNodeConfigure.Flag("auth-server", "Address of the auth server.").StringVar(&dumpFlags.AuthServer) + dumpNodeConfigure.Flag("proxy-server", "Address of the proxy server.").StringVar(&dumpFlags.ProxyServer) dumpNodeConfigure.Flag("labels", "Comma-separated list of labels to add to newly created nodes ex) env=staging,cloud=aws.").StringVar(&dumpFlags.NodeLabels) dumpNodeConfigure.Flag("ca-pin", "Comma-separated list of SKPI hashes for the CA used to verify the auth server.").StringVar(&dumpFlags.CAPin) dumpNodeConfigure.Flag("join-method", "Method to use to join the cluster (token, iam, ec2)").Default("token").EnumVar(&dumpFlags.JoinMethod, "token", "iam", "ec2") @@ -468,8 +476,7 @@ func (flags *dumpFlags) CheckAndSetDefaults() error { return trace.BadParameter("only --output or --test can be set, not both") } - err := checkConfigurationFileVersion(flags.Version) - if err != nil { + if err := checkConfigurationFileVersion(flags.Version); err != nil { return trace.Wrap(err) } @@ -489,13 +496,14 @@ func normalizeOutput(output string) string { } func checkConfigurationFileVersion(version string) error { - supportedVersions := []string{defaults.TeleportConfigVersionV1, defaults.TeleportConfigVersionV2} - switch version { - case defaults.TeleportConfigVersionV1, defaults.TeleportConfigVersionV2, "": - default: - return trace.BadParameter( - "unsupported Teleport configuration version %q, supported are: %s", - version, strings.Join(supportedVersions, ",")) + // allow an empty version as we default to v3 + if version == "" { + return nil + } + + hasVersion := apiutils.SliceContainsStr(defaults.TeleportVersions, version) + if !hasVersion { + return trace.BadParameter("config: version must be one of %s", strings.Join(defaults.TeleportVersions, ", ")) } return nil diff --git a/tool/tsh/proxy_test.go b/tool/tsh/proxy_test.go index 9133da1252d13..9109d7371ca19 100644 --- a/tool/tsh/proxy_test.go +++ b/tool/tsh/proxy_test.go @@ -52,6 +52,10 @@ func TestSSH(t *testing.T) { defer lib.SetInsecureDevMode(false) s := newTestSuite(t, + withRootConfigFunc(func(cfg *service.Config) { + cfg.Version = defaults.TeleportConfigVersionV3 + cfg.Auth.NetworkingConfig.SetProxyListenerMode(types.ProxyListenerMode_Multiplex) + }), withRootConfigFunc(func(cfg *service.Config) { cfg.Version = defaults.TeleportConfigVersionV2 cfg.Auth.NetworkingConfig.SetProxyListenerMode(types.ProxyListenerMode_Multiplex)