Skip to content

Commit

Permalink
Allow cluster_networking_config to have defaults origin (#19106)
Browse files Browse the repository at this point in the history
This commit ensures that `cluster_networking_config` has the
`teleport.dev/origin: defaults` label when no related field is set in
the configuration file.
Before this commit, `cluster_networking_config` would always have the
`teleport.dev/origin: config-file`, even if no related field was set.
  • Loading branch information
Vitor Enes authored Dec 13, 2022
1 parent f8e81e6 commit 0050eb7
Show file tree
Hide file tree
Showing 4 changed files with 203 additions and 17 deletions.
35 changes: 19 additions & 16 deletions lib/config/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -661,26 +661,29 @@ func applyAuthConfig(fc *FileConfig, cfg *service.Config) error {
return trace.Wrap(err)
}

// Set cluster networking configuration from file configuration.
cfg.Auth.NetworkingConfig, err = types.NewClusterNetworkingConfigFromConfigFile(types.ClusterNetworkingConfigSpecV2{
ClientIdleTimeout: fc.Auth.ClientIdleTimeout,
ClientIdleTimeoutMessage: fc.Auth.ClientIdleTimeoutMessage,
WebIdleTimeout: fc.Auth.WebIdleTimeout,
KeepAliveInterval: fc.Auth.KeepAliveInterval,
KeepAliveCountMax: fc.Auth.KeepAliveCountMax,
SessionControlTimeout: fc.Auth.SessionControlTimeout,
ProxyListenerMode: fc.Auth.ProxyListenerMode,
RoutingStrategy: fc.Auth.RoutingStrategy,
TunnelStrategy: fc.Auth.TunnelStrategy,
ProxyPingInterval: fc.Auth.ProxyPingInterval,
})
if err != nil {
return trace.Wrap(err)
// Only override networking configuration if some of its fields are
// specified in file configuration.
if fc.Auth.hasCustomNetworkingConfig() {
cfg.Auth.NetworkingConfig, err = types.NewClusterNetworkingConfigFromConfigFile(types.ClusterNetworkingConfigSpecV2{
ClientIdleTimeout: fc.Auth.ClientIdleTimeout,
ClientIdleTimeoutMessage: fc.Auth.ClientIdleTimeoutMessage,
WebIdleTimeout: fc.Auth.WebIdleTimeout,
KeepAliveInterval: fc.Auth.KeepAliveInterval,
KeepAliveCountMax: fc.Auth.KeepAliveCountMax,
SessionControlTimeout: fc.Auth.SessionControlTimeout,
ProxyListenerMode: fc.Auth.ProxyListenerMode,
RoutingStrategy: fc.Auth.RoutingStrategy,
TunnelStrategy: fc.Auth.TunnelStrategy,
ProxyPingInterval: fc.Auth.ProxyPingInterval,
})
if err != nil {
return trace.Wrap(err)
}
}

// Only override session recording configuration if either field is
// specified in file configuration.
if fc.Auth.SessionRecording != "" || fc.Auth.ProxyChecksHostKeys != nil {
if fc.Auth.hasCustomSessionRecording() {
cfg.Auth.SessionRecordingConfig, err = types.NewSessionRecordingConfigFromConfigFile(types.SessionRecordingConfigSpecV2{
Mode: fc.Auth.SessionRecording,
ProxyChecksHostKeys: fc.Auth.ProxyChecksHostKeys,
Expand Down
100 changes: 100 additions & 0 deletions lib/config/configuration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -778,6 +778,9 @@ func TestApplyConfig(t *testing.T) {
require.NoError(t, err)
require.Equal(t, types.AgentMesh, tunnelStrategyType)
require.Equal(t, types.DefaultAgentMeshTunnelStrategy(), cfg.Auth.NetworkingConfig.GetAgentMeshTunnelStrategy())
require.Equal(t, types.OriginConfigFile, cfg.Auth.Preference.Origin())
require.Equal(t, types.OriginDefaults, cfg.Auth.NetworkingConfig.Origin())
require.Equal(t, types.OriginDefaults, cfg.Auth.SessionRecordingConfig.Origin())

require.True(t, cfg.Proxy.Enabled)
require.Equal(t, "tcp://webhost:3080", cfg.Proxy.WebAddr.FullAddress())
Expand Down Expand Up @@ -900,6 +903,9 @@ func TestApplyConfigNoneEnabled(t *testing.T) {

require.False(t, cfg.Auth.Enabled)
require.Empty(t, cfg.Auth.PublicAddrs)
require.Equal(t, types.OriginDefaults, cfg.Auth.Preference.Origin())
require.Equal(t, types.OriginDefaults, cfg.Auth.NetworkingConfig.Origin())
require.Equal(t, types.OriginDefaults, cfg.Auth.SessionRecordingConfig.Origin())
require.False(t, cfg.Proxy.Enabled)
require.Empty(t, cfg.Proxy.PublicAddrs)
require.False(t, cfg.SSH.Enabled)
Expand All @@ -912,6 +918,100 @@ func TestApplyConfigNoneEnabled(t *testing.T) {
require.Empty(t, cfg.Proxy.MySQLPublicAddrs)
}

// TestApplyConfigNoneEnabled makes sure that if the auth file configuration
// does not have `cluster_auth_preference`, `cluster_networking_config` and
// `session_recording` fields, then they all have the origin label as defaults.
func TestApplyDefaultAuthResources(t *testing.T) {
conf, err := ReadConfig(bytes.NewBufferString(DefaultAuthResourcesConfigString))
require.NoError(t, err)

cfg := service.MakeDefaultConfig()
err = ApplyFileConfig(conf, cfg)
require.NoError(t, err)

require.True(t, cfg.Auth.Enabled)
require.Equal(t, "example.com", cfg.Auth.ClusterName.GetClusterName())
require.Equal(t, types.OriginDefaults, cfg.Auth.Preference.Origin())
require.Equal(t, types.OriginDefaults, cfg.Auth.NetworkingConfig.Origin())
require.Equal(t, types.OriginDefaults, cfg.Auth.SessionRecordingConfig.Origin())
}

// TestApplyCustomAuthPreference makes sure that if the auth file configuration
// has a `cluster_auth_preference` field, then it will have the origin label as
// config-file.
func TestApplyCustomAuthPreference(t *testing.T) {
conf, err := ReadConfig(bytes.NewBufferString(CustomAuthPreferenceConfigString))
require.NoError(t, err)

cfg := service.MakeDefaultConfig()
err = ApplyFileConfig(conf, cfg)
require.NoError(t, err)

require.True(t, cfg.Auth.Enabled)
require.Equal(t, "example.com", cfg.Auth.ClusterName.GetClusterName())
require.Equal(t, types.OriginConfigFile, cfg.Auth.Preference.Origin())
require.True(t, cfg.Auth.Preference.GetDisconnectExpiredCert())
require.Equal(t, types.OriginDefaults, cfg.Auth.NetworkingConfig.Origin())
require.Equal(t, types.OriginDefaults, cfg.Auth.SessionRecordingConfig.Origin())
}

// TestApplyCustomAuthPreferenceWithMOTD makes sure that if the auth file configuration
// has only the `message_of_the_day` `cluster_auth_preference` field, then it will have
// the origin label as defaults (instead of config-file).
func TestApplyCustomAuthPreferenceWithMOTD(t *testing.T) {
conf, err := ReadConfig(bytes.NewBufferString(AuthPreferenceConfigWithMOTDString))
require.NoError(t, err)

cfg := service.MakeDefaultConfig()
err = ApplyFileConfig(conf, cfg)
require.NoError(t, err)

require.True(t, cfg.Auth.Enabled)
require.Equal(t, "example.com", cfg.Auth.ClusterName.GetClusterName())
require.Equal(t, types.OriginDefaults, cfg.Auth.Preference.Origin())
require.Equal(t, "welcome!", cfg.Auth.Preference.GetMessageOfTheDay())
require.Equal(t, types.OriginDefaults, cfg.Auth.NetworkingConfig.Origin())
require.Equal(t, types.OriginDefaults, cfg.Auth.SessionRecordingConfig.Origin())
}

// TestApplyCustomNetworkingConfig makes sure that if the auth file configuration
// has a `cluster_networking_config` field, then it will have the origin label as
// config-file.
func TestApplyCustomNetworkingConfig(t *testing.T) {
conf, err := ReadConfig(bytes.NewBufferString(CustomNetworkingConfigString))
require.NoError(t, err)

cfg := service.MakeDefaultConfig()
err = ApplyFileConfig(conf, cfg)
require.NoError(t, err)

require.True(t, cfg.Auth.Enabled)
require.Equal(t, "example.com", cfg.Auth.ClusterName.GetClusterName())
require.Equal(t, types.OriginDefaults, cfg.Auth.Preference.Origin())
require.Equal(t, types.OriginConfigFile, cfg.Auth.NetworkingConfig.Origin())
require.Equal(t, 10*time.Second, cfg.Auth.NetworkingConfig.GetWebIdleTimeout())
require.Equal(t, types.OriginDefaults, cfg.Auth.SessionRecordingConfig.Origin())
}

// TestApplyCustomSessionRecordingConfig makes sure that if the auth file configuration
// has a `session_recording` field, then it will have the origin label as
// config-file.
func TestApplyCustomSessionRecordingConfig(t *testing.T) {
conf, err := ReadConfig(bytes.NewBufferString(CustomSessionRecordingConfigString))
require.NoError(t, err)

cfg := service.MakeDefaultConfig()
err = ApplyFileConfig(conf, cfg)
require.NoError(t, err)

require.True(t, cfg.Auth.Enabled)
require.Equal(t, cfg.Auth.ClusterName.GetClusterName(), "example.com")
require.Equal(t, types.OriginDefaults, cfg.Auth.Preference.Origin())
require.Equal(t, types.OriginDefaults, cfg.Auth.NetworkingConfig.Origin())
require.Equal(t, types.OriginConfigFile, cfg.Auth.SessionRecordingConfig.Origin())
require.True(t, cfg.Auth.SessionRecordingConfig.GetProxyChecksHostKeys())
}

// TestPostgresPublicAddr makes sure Postgres proxy public address default
// port logic works correctly.
func TestPostgresPublicAddr(t *testing.T) {
Expand Down
26 changes: 25 additions & 1 deletion lib/config/fileconf.go
Original file line number Diff line number Diff line change
Expand Up @@ -803,7 +803,7 @@ type Auth struct {
// environments where paranoid security is not needed
//
// Each token string has the following format: "role1,role2,..:token",
// for exmple: "auth,proxy,node:MTIzNGlvemRmOWE4MjNoaQo"
// for example: "auth,proxy,node:MTIzNGlvemRmOWE4MjNoaQo"
StaticTokens StaticTokens `yaml:"tokens,omitempty"`

// Authentication holds authentication configuration information like authentication
Expand Down Expand Up @@ -889,6 +889,30 @@ type Auth struct {
LoadAllCAs bool `yaml:"load_all_cas,omitempty"`
}

// hasCustomNetworkingConfig returns true if any of the networking
// configuration fields have values different from an empty Auth.
func (a *Auth) hasCustomNetworkingConfig() bool {
empty := Auth{}
return a.ClientIdleTimeout != empty.ClientIdleTimeout ||
a.ClientIdleTimeoutMessage != empty.ClientIdleTimeoutMessage ||
a.WebIdleTimeout != empty.WebIdleTimeout ||
a.KeepAliveInterval != empty.KeepAliveInterval ||
a.KeepAliveCountMax != empty.KeepAliveCountMax ||
a.SessionControlTimeout != empty.SessionControlTimeout ||
a.ProxyListenerMode != empty.ProxyListenerMode ||
a.RoutingStrategy != empty.RoutingStrategy ||
a.TunnelStrategy != empty.TunnelStrategy ||
a.ProxyPingInterval != empty.ProxyPingInterval
}

// hasCustomSessionRecording returns true if any of the session recording
// configuration fields have values different from an empty Auth.
func (a *Auth) hasCustomSessionRecording() bool {
empty := Auth{}
return a.SessionRecording != empty.SessionRecording ||
a.ProxyChecksHostKeys != empty.ProxyChecksHostKeys
}

// CAKeyParams configures how CA private keys will be created and stored.
type CAKeyParams struct {
// PKCS11 configures a PKCS#11 HSM to be used for all CA private key generation and
Expand Down
59 changes: 59 additions & 0 deletions lib/config/testdata_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,65 @@ app_service:
enabled: no
`

// DefaultAuthResourcesConfigString is a configuration file without
// `cluster_auth_preference`, `cluster_networking_config` and `session_recording` fields.
const DefaultAuthResourcesConfigString = `
teleport:
nodename: node.example.com
auth_service:
enabled: yes
cluster_name: "example.com"
`

// CustomAuthPreferenceConfigString is a configuration file with a single
// `cluster_auth_preference` field.
const CustomAuthPreferenceConfigString = `
teleport:
nodename: node.example.com
auth_service:
enabled: yes
cluster_name: "example.com"
disconnect_expired_cert: true
`

// AuthPreferenceConfigWithMOTDString is a configuration file with the
// `message_of_the_day` `cluster_auth_preference` field.
const AuthPreferenceConfigWithMOTDString = `
teleport:
nodename: node.example.com
auth_service:
enabled: yes
cluster_name: "example.com"
message_of_the_day: "welcome!"
`

// CustomNetworkingConfigString is a configuration file with a single
// `cluster_networking_config` field.
const CustomNetworkingConfigString = `
teleport:
nodename: node.example.com
auth_service:
enabled: yes
cluster_name: "example.com"
web_idle_timeout: 10s
`

// CustomSessionRecordingConfigString is a configuration file with a single
// `session_recording` field.
const CustomSessionRecordingConfigString = `
teleport:
nodename: node.example.com
auth_service:
enabled: yes
cluster_name: "example.com"
proxy_checks_host_keys: true
`

// configWithFIPSKex is a configuration file with a FIPS compliant KEX
// algorithm.
const configWithFIPSKex = `
Expand Down

0 comments on commit 0050eb7

Please sign in to comment.