Dont allow cloud tenants to update certain cluster networking config fields (#28634)

* Dont allow cloud tenants to update certain cluster networking fields

* resolve comments

* remove fmt.Sprintf
This commit is contained in:
Alex McGrath 2023-07-11 17:11:51 +01:00 committed by GitHub
parent 732ad92d5f
commit 3fff0bdbbf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 179 additions and 0 deletions

View file

@ -4111,7 +4111,47 @@ func (a *ServerWithRoles) SetClusterNetworkingConfig(ctx context.Context, newNet
return trace.AccessDenied("proxy peering is an enterprise-only feature")
}
oldNetConf, err := a.authServer.GetClusterNetworkingConfig(ctx)
if err != nil {
return trace.Wrap(err)
}
if err := a.validateCloudNetworkConfigUpdate(newNetConfig, oldNetConf); err != nil {
return trace.Wrap(err)
}
return a.authServer.SetClusterNetworkingConfig(ctx, newNetConfig)
}
func (a *ServerWithRoles) validateCloudNetworkConfigUpdate(newConfig, oldConfig types.ClusterNetworkingConfig) error {
if a.hasBuiltinRole(types.RoleAdmin) {
return nil
}
if !modules.GetModules().Features().Cloud {
return nil
}
const cloudUpdateFailureMsg = "cloud tenants cannot update %q"
if newConfig.GetProxyListenerMode() != oldConfig.GetProxyListenerMode() {
return trace.BadParameter(cloudUpdateFailureMsg, "proxy_listener_mode")
}
newtst, _ := newConfig.GetTunnelStrategyType()
oldtst, _ := oldConfig.GetTunnelStrategyType()
if newtst != oldtst {
return trace.BadParameter(cloudUpdateFailureMsg, "tunnel_strategy")
}
if newConfig.GetKeepAliveInterval() != oldConfig.GetKeepAliveInterval() {
return trace.BadParameter(cloudUpdateFailureMsg, "keep_alive_interval")
}
if newConfig.GetKeepAliveCountMax() != oldConfig.GetKeepAliveCountMax() {
return trace.BadParameter(cloudUpdateFailureMsg, "keep_alive_count_max")
}
return nil
}
// ResetClusterNetworkingConfig resets cluster networking configuration to defaults.
@ -4129,6 +4169,14 @@ func (a *ServerWithRoles) ResetClusterNetworkingConfig(ctx context.Context) erro
return trace.Wrap(err)
}
}
oldNetConf, err := a.authServer.GetClusterNetworkingConfig(ctx)
if err != nil {
return trace.Wrap(err)
}
if err := a.validateCloudNetworkConfigUpdate(types.DefaultClusterNetworkingConfig(), oldNetConf); err != nil {
return trace.Wrap(err)
}
return a.authServer.SetClusterNetworkingConfig(ctx, types.DefaultClusterNetworkingConfig())
}

View file

@ -53,6 +53,7 @@ import (
libdefaults "github.com/gravitational/teleport/lib/defaults"
"github.com/gravitational/teleport/lib/events"
"github.com/gravitational/teleport/lib/events/eventstest"
"github.com/gravitational/teleport/lib/modules"
"github.com/gravitational/teleport/lib/services"
"github.com/gravitational/teleport/lib/session"
"github.com/gravitational/teleport/lib/tlsca"
@ -1137,6 +1138,136 @@ func TestAuthPreferenceRBAC(t *testing.T) {
})
}
func TestClusterNetworkingCloudUpdates(t *testing.T) {
srv := newTestTLSServer(t)
ctx := context.Background()
err := srv.Auth().SetClusterNetworkingConfig(ctx, types.DefaultClusterNetworkingConfig())
require.NoError(t, err)
user, _, err := CreateUserAndRole(srv.Auth(), "username", []string{}, []types.Rule{
{
Resources: []string{
types.KindClusterNetworkingConfig,
},
Verbs: services.RW(),
},
})
require.NoError(t, err)
for _, tc := range []struct {
cloud bool
identity TestIdentity
expectSetErr string
clusterNetworkingConfig types.ClusterNetworkingConfig
name string
}{
{
name: "non admin user can set existing values to the same value",
cloud: true,
identity: TestUser(user.GetName()),
clusterNetworkingConfig: types.DefaultClusterNetworkingConfig(),
},
{
name: "non admin user cannot set keep_alive_interval",
cloud: true,
identity: TestUser(user.GetName()),
expectSetErr: "keep_alive_interval",
clusterNetworkingConfig: newClusterNetworkingConf(t, types.ClusterNetworkingConfigSpecV2{
KeepAliveInterval: types.Duration(time.Second * 20),
}),
},
{
name: "non admin user cannot set tunnel_strategy",
cloud: true,
identity: TestUser(user.GetName()),
expectSetErr: "tunnel_strategy",
clusterNetworkingConfig: newClusterNetworkingConf(t, types.ClusterNetworkingConfigSpecV2{
TunnelStrategy: &types.TunnelStrategyV1{
Strategy: &types.TunnelStrategyV1_ProxyPeering{
ProxyPeering: types.DefaultProxyPeeringTunnelStrategy(),
},
},
}),
},
{
name: "non admin user cannot set proxy_listener_mode",
cloud: true,
identity: TestUser(user.GetName()),
expectSetErr: "proxy_listener_mode",
clusterNetworkingConfig: newClusterNetworkingConf(t, types.ClusterNetworkingConfigSpecV2{
ProxyListenerMode: types.ProxyListenerMode_Multiplex,
}),
},
{
name: "non admin user cannot set keep_alive_count_max",
cloud: true,
identity: TestUser(user.GetName()),
expectSetErr: "keep_alive_count_max",
clusterNetworkingConfig: newClusterNetworkingConf(t, types.ClusterNetworkingConfigSpecV2{
KeepAliveCountMax: 55,
}),
},
{
name: "non admin user can set client_idle_timeout",
cloud: true,
identity: TestUser(user.GetName()),
clusterNetworkingConfig: newClusterNetworkingConf(t, types.ClusterNetworkingConfigSpecV2{
ClientIdleTimeout: types.Duration(time.Second * 67),
}),
},
{
name: "admin user can set keep_alive_interval",
cloud: true,
identity: TestAdmin(),
clusterNetworkingConfig: newClusterNetworkingConf(t, types.ClusterNetworkingConfigSpecV2{
KeepAliveInterval: types.Duration(time.Second * 67),
}),
},
{
name: "non admin user can set keep_alive_interval on non cloud cluster",
cloud: false,
identity: TestUser(user.GetName()),
clusterNetworkingConfig: newClusterNetworkingConf(t, types.ClusterNetworkingConfigSpecV2{
KeepAliveInterval: types.Duration(time.Second * 67),
}),
},
} {
t.Run(tc.name, func(t *testing.T) {
modules.SetTestModules(t, &modules.TestModules{
TestBuildType: modules.BuildEnterprise,
TestFeatures: modules.Features{
Cloud: tc.cloud,
},
})
client, err := srv.NewClient(tc.identity)
require.NoError(t, err)
err = client.SetClusterNetworkingConfig(ctx, tc.clusterNetworkingConfig)
if err != nil {
require.NotEmpty(t, tc.expectSetErr)
require.ErrorContains(t, err, tc.expectSetErr)
} else {
require.Empty(t, tc.expectSetErr)
}
})
}
}
func newClusterNetworkingConf(t *testing.T, spec types.ClusterNetworkingConfigSpecV2) types.ClusterNetworkingConfig {
c := &types.ClusterNetworkingConfigV2{
Metadata: types.Metadata{
Labels: map[string]string{
types.OriginLabel: types.OriginDynamic,
},
},
Spec: spec,
}
err := c.CheckAndSetDefaults()
require.NoError(t, err)
return c
}
func TestClusterNetworkingConfigRBAC(t *testing.T) {
t.Parallel()
ctx := context.Background()