Skip to content

Commit

Permalink
Disallow some networking config fields to be changed in Cloud
Browse files Browse the repository at this point in the history
  • Loading branch information
Vitor Enes committed Dec 19, 2022
1 parent 93f5310 commit 3c65de8
Show file tree
Hide file tree
Showing 6 changed files with 420 additions and 93 deletions.
45 changes: 39 additions & 6 deletions lib/auth/auth_with_roles.go
Original file line number Diff line number Diff line change
Expand Up @@ -3436,7 +3436,7 @@ func (a *ServerWithRoles) SetAuthPreference(ctx context.Context, newAuthPref typ
}
}

return a.authServer.SetAuthPreference(ctx, newAuthPref)
return a.setAuthPreference(ctx, storedAuthPref, newAuthPref)
}

// ResetAuthPreference resets cluster auth preference to defaults.
Expand All @@ -3453,7 +3453,18 @@ func (a *ServerWithRoles) ResetAuthPreference(ctx context.Context) error {
return trace.Wrap(err)
}

return a.authServer.SetAuthPreference(ctx, types.DefaultAuthPreference())
return a.setAuthPreference(ctx, storedAuthPref, types.DefaultAuthPreference())
}

// setAuthPreference sets cluster auth preference, validating if the update is allowed in cloud.
func (a *ServerWithRoles) setAuthPreference(ctx context.Context, storedAuthPref, newAuthPref types.AuthPreference) error {
// Perform the modules-provided checks.
isAdmin := a.hasBuiltinRole(types.RoleAdmin)
if err := modules.ValidateAuthPreference(isAdmin, storedAuthPref, newAuthPref); err != nil {
return trace.Wrap(err)
}

return a.authServer.SetAuthPreference(ctx, newAuthPref)
}

// DeleteAuthPreference not implemented: can only be called locally.
Expand Down Expand Up @@ -3513,7 +3524,7 @@ func (a *ServerWithRoles) SetClusterNetworkingConfig(ctx context.Context, newNet
return trace.AccessDenied("proxy peering is an enterprise-only feature")
}

return a.authServer.SetClusterNetworkingConfig(ctx, newNetConfig)
return a.setClusterNetworkingConfig(ctx, storedNetConfig, newNetConfig)
}

// ResetClusterNetworkingConfig resets cluster networking configuration to defaults.
Expand All @@ -3532,7 +3543,18 @@ func (a *ServerWithRoles) ResetClusterNetworkingConfig(ctx context.Context) erro
}
}

return a.authServer.SetClusterNetworkingConfig(ctx, types.DefaultClusterNetworkingConfig())
return a.setClusterNetworkingConfig(ctx, storedNetConfig, types.DefaultClusterNetworkingConfig())
}

// setClusterNetworkingConfig sets cluster networking configuration, validating if the update is allowed in cloud.
func (a *ServerWithRoles) setClusterNetworkingConfig(ctx context.Context, storedNetConfig, newNetConfig types.ClusterNetworkingConfig) error {
// Perform the modules-provided checks.
isAdmin := a.hasBuiltinRole(types.RoleAdmin)
if err := modules.ValidateClusterNetworkingConfig(isAdmin, storedNetConfig, newNetConfig); err != nil {
return trace.Wrap(err)
}

return a.authServer.SetClusterNetworkingConfig(ctx, newNetConfig)
}

// DeleteClusterNetworkingConfig not implemented: can only be called locally.
Expand Down Expand Up @@ -3563,7 +3585,7 @@ func (a *ServerWithRoles) SetSessionRecordingConfig(ctx context.Context, newRecC
}
}

return a.authServer.SetSessionRecordingConfig(ctx, newRecConfig)
return a.setSessionRecordingConfig(ctx, storedRecConfig, newRecConfig)
}

// ResetSessionRecordingConfig resets session recording configuration to defaults.
Expand All @@ -3582,7 +3604,18 @@ func (a *ServerWithRoles) ResetSessionRecordingConfig(ctx context.Context) error
}
}

return a.authServer.SetSessionRecordingConfig(ctx, types.DefaultSessionRecordingConfig())
return a.setSessionRecordingConfig(ctx, storedRecConfig, types.DefaultSessionRecordingConfig())
}

// setSessionRecordingConfig sets session recording configuration, validating if the update is allowed in cloud.
func (a *ServerWithRoles) setSessionRecordingConfig(ctx context.Context, storedRecConfig, newRecConfig types.SessionRecordingConfig) error {
// Perform the modules-provided checks.
isAdmin := a.hasBuiltinRole(types.RoleAdmin)
if err := modules.ValidateSessionRecordingConfig(isAdmin, storedRecConfig, newRecConfig); err != nil {
return trace.Wrap(err)
}

return a.authServer.SetSessionRecordingConfig(ctx, newRecConfig)
}

// DeleteSessionRecordingConfig not implemented: can only be called locally.
Expand Down
299 changes: 299 additions & 0 deletions lib/auth/auth_with_roles_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import (
"github.com/gravitational/teleport/lib/auth/testauthority"
libdefaults "github.com/gravitational/teleport/lib/defaults"
"github.com/gravitational/teleport/lib/events"
"github.com/gravitational/teleport/lib/modules"
"github.com/gravitational/teleport/lib/services"
"github.com/gravitational/teleport/lib/session"
"github.com/gravitational/teleport/lib/tlsca"
Expand Down Expand Up @@ -1058,6 +1059,304 @@ func TestSessionRecordingConfigRBAC(t *testing.T) {
})
}

func TestValidateAuthPreferenceOnCloud(t *testing.T) {
ctx := context.Background()
srv := newTestTLSServer(t)

// Create non-admin user.
_, _, err := CreateUserAndRole(srv.Auth(), "remote", []string{"role"})
require.NoError(t, err)

// This test cannot run in parallel as set module changes the global state.
modules.SetTestModules(t, &modules.TestModules{
TestBuildType: modules.BuildEnterprise,
TestFeatures: modules.Features{
Cloud: true,
},
})

tests := []struct {
desc string
identity TestIdentity
resource types.AuthPreference
err string
}{
{
desc: "user can set default auth preference",
identity: TestUser("remote"),
resource: types.DefaultAuthPreference(),
},
{
desc: "user cannot disable 2fa",
identity: TestUser("remote"),
resource: func() types.AuthPreference {
r := types.DefaultAuthPreference()
r.SetSecondFactor(constants.SecondFactorOff)
return r
}(),
err: "cannot disable two-factor authentication on Cloud",
},
{
desc: "user cannot set 2fa as optional",
identity: TestUser("remote"),
resource: func() types.AuthPreference {
r := types.DefaultAuthPreference()
r.SetSecondFactor(constants.SecondFactorOptional)
r.SetWebauthn(&types.Webauthn{RPID: "localhost"})
return r
}(),
err: "cannot disable two-factor authentication on Cloud",
},
{
desc: "admin can set 2fa as optional",
identity: TestAdmin(),
resource: func() types.AuthPreference {
r := types.DefaultAuthPreference()
r.SetSecondFactor(constants.SecondFactorOptional)
r.SetWebauthn(&types.Webauthn{RPID: "localhost"})
return r
}(),
},
}

for _, test := range tests {
t.Run(test.desc, func(t *testing.T) {
client, err := srv.NewClient(test.identity)
require.NoError(t, err)

err = client.SetAuthPreference(ctx, test.resource)
if test.err != "" {
require.ErrorContains(t, err, test.err)
} else {
require.NoError(t, err)
}
})
}
}

func TestValidateClusterNetworkingConfigCloud(t *testing.T) {
ctx := context.Background()
srv := newTestTLSServer(t)

// Create non-admin user.
_, _, err := CreateUserAndRole(srv.Auth(), "remote", []string{"role"})
require.NoError(t, err)

// This test cannot run in parallel as set module changes the global state.
modules.SetTestModules(t, &modules.TestModules{
TestBuildType: modules.BuildEnterprise,
TestFeatures: modules.Features{
Cloud: true,
},
})

tests := []struct {
desc string
identity TestIdentity
resource types.ClusterNetworkingConfig
err string
}{
{
desc: "user can set default networking config",
identity: TestUser("remote"),
resource: types.DefaultClusterNetworkingConfig(),
},
{
desc: "user cannot change keep alive interval",
identity: TestUser("remote"),
resource: func() types.ClusterNetworkingConfig {
r := types.DefaultClusterNetworkingConfig()
r.SetKeepAliveInterval(1 * time.Hour)
return r
}(),
err: "cannot change keep alive interval on Cloud",
},
{
desc: "user cannot change keep alive count max",
identity: TestUser("remote"),
resource: func() types.ClusterNetworkingConfig {
r := types.DefaultClusterNetworkingConfig()
r.SetKeepAliveCountMax(10)
return r
}(),
err: "cannot change keep alive count max on Cloud",
},
{
desc: "user cannot change proxy listener mode",
identity: TestUser("remote"),
resource: func() types.ClusterNetworkingConfig {
r := types.DefaultClusterNetworkingConfig()
r.SetProxyListenerMode(1)
return r
}(),
err: "cannot change proxy listener mode on Cloud",
},
{
desc: "user cannot change tunnel strategy type",
identity: TestUser("remote"),
resource: func() types.ClusterNetworkingConfig {
r := types.DefaultClusterNetworkingConfig()
r.SetTunnelStrategy(
&types.TunnelStrategyV1{
Strategy: &types.TunnelStrategyV1_ProxyPeering{
ProxyPeering: types.DefaultProxyPeeringTunnelStrategy(),
},
},
)
return r
}(),
err: "cannot change tunnel strategy type on Cloud",
},
{
desc: "admin can change tunnel strategy type",
identity: TestAdmin(),
resource: func() types.ClusterNetworkingConfig {
r := types.DefaultClusterNetworkingConfig()
r.SetTunnelStrategy(
&types.TunnelStrategyV1{
Strategy: &types.TunnelStrategyV1_ProxyPeering{
ProxyPeering: types.DefaultProxyPeeringTunnelStrategy(),
},
},
)
return r
}(),
},
{
desc: "user cannot change agent connection count",
identity: TestUser("remote"),
resource: func() types.ClusterNetworkingConfig {
r := types.DefaultClusterNetworkingConfig()
r.SetTunnelStrategy(
&types.TunnelStrategyV1{
Strategy: &types.TunnelStrategyV1_ProxyPeering{
ProxyPeering: &types.ProxyPeeringTunnelStrategy{
AgentConnectionCount: 10,
},
},
},
)
return r
}(),
err: "cannot change agent connection count on Cloud",
},
{
desc: "admin can change agent connection count",
identity: TestAdmin(),
resource: func() types.ClusterNetworkingConfig {
r := types.DefaultClusterNetworkingConfig()
r.SetTunnelStrategy(
&types.TunnelStrategyV1{
Strategy: &types.TunnelStrategyV1_ProxyPeering{
ProxyPeering: &types.ProxyPeeringTunnelStrategy{
AgentConnectionCount: 10,
},
},
},
)
return r
}(),
},
}

for _, test := range tests {
t.Run(test.desc, func(t *testing.T) {
client, err := srv.NewClient(test.identity)
require.NoError(t, err)

err = client.SetClusterNetworkingConfig(ctx, test.resource)
if test.err != "" {
require.ErrorContains(t, err, test.err)
} else {
require.NoError(t, err)
}
})
}
}

func TestValidateSessionRecordingConfigOnCloud(t *testing.T) {
ctx := context.Background()
srv := newTestTLSServer(t)

// Create non-admin user.
_, _, err := CreateUserAndRole(srv.Auth(), "remote", []string{"role"})
require.NoError(t, err)

// This test cannot run in parallel as set module changes the global state.
modules.SetTestModules(t, &modules.TestModules{
TestBuildType: modules.BuildEnterprise,
TestFeatures: modules.Features{
Cloud: true,
},
})

tests := []struct {
desc string
identity TestIdentity
resource types.SessionRecordingConfig
err string
}{
{
desc: "user can set default session recording config",
identity: TestUser("remote"),
resource: types.DefaultSessionRecordingConfig(),
},
{
desc: "user cannot set record a proxy",
identity: TestUser("remote"),
resource: func() types.SessionRecordingConfig {
r := types.DefaultSessionRecordingConfig()
r.SetMode(types.RecordAtProxy)
return r
}(),
err: "cannot set proxy recording mode on Cloud",
},
{
desc: "user cannot set record a proxy sync",
identity: TestUser("remote"),
resource: func() types.SessionRecordingConfig {
r := types.DefaultSessionRecordingConfig()
r.SetMode(types.RecordAtProxySync)
return r
}(),
err: "cannot set proxy recording mode on Cloud",
},
{
desc: "user cannot disable strict host key checking",
identity: TestUser("remote"),
resource: func() types.SessionRecordingConfig {
r := types.DefaultSessionRecordingConfig()
r.SetProxyChecksHostKeys(false)
return r
}(),
err: "cannot disable strict host key checking on Cloud",
},
{
desc: "admin can disable strict host key checking",
identity: TestAdmin(),
resource: func() types.SessionRecordingConfig {
r := types.DefaultSessionRecordingConfig()
r.SetProxyChecksHostKeys(false)
return r
}(),
},
}

for _, test := range tests {
t.Run(test.desc, func(t *testing.T) {
client, err := srv.NewClient(test.identity)
require.NoError(t, err)

err = client.SetSessionRecordingConfig(ctx, test.resource)
if test.err != "" {
require.ErrorContains(t, err, test.err)
} else {
require.NoError(t, err)
}
})
}
}

// TestGetAndList_Nodes users can retrieve nodes with various filters
// and with the appropriate permissions.
func TestGetAndList_Nodes(t *testing.T) {
Expand Down
Loading

0 comments on commit 3c65de8

Please sign in to comment.