mirror of
https://github.com/gravitational/teleport.git
synced 2026-10-31 20:39:37 +08:00
* Fix Moderated session on leave pause action. * Unpause when moderator rejoins session with onLeave=pause This commite fixes a case when the moderator rejoins a paused session but Teleport didn't resume the operations and the session was still locked to everyone. * Fix panic when re-joining SSH session. * Update lib/kube/proxy/sess.go Co-authored-by: Noah Stride <noah.stride@goteleport.com> * Update lib/kube/proxy/sess.go Co-authored-by: Noah Stride <noah.stride@goteleport.com> * Fix typo Co-authored-by: Tiago Silva <tiago.silva@goteleport.com> --------- Co-authored-by: Tiago Silva <tiago.silva@goteleport.com> Co-authored-by: Noah Stride <noah.stride@goteleport.com>
514 lines
15 KiB
Go
514 lines
15 KiB
Go
/*
|
|
Copyright 2021 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 auth
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/gravitational/teleport/api/types"
|
|
)
|
|
|
|
type startTestCase struct {
|
|
name string
|
|
host []types.Role
|
|
sessionKinds []types.SessionKind
|
|
participants []SessionAccessContext
|
|
owner string
|
|
expected []bool
|
|
terminate types.OnSessionLeaveAction
|
|
}
|
|
|
|
func successStartTestCase(t *testing.T) startTestCase {
|
|
hostRole, err := types.NewRole("host", types.RoleSpecV6{})
|
|
require.NoError(t, err)
|
|
participantRole, err := types.NewRole("participant", types.RoleSpecV6{})
|
|
require.NoError(t, err)
|
|
|
|
hostRole.SetSessionRequirePolicies([]*types.SessionRequirePolicy{{
|
|
Filter: "contains(user.roles, \"participant\")",
|
|
Kinds: []string{string(types.SSHSessionKind), string(types.KubernetesSessionKind)},
|
|
Count: 2,
|
|
OnLeave: string(types.OnSessionLeaveTerminate),
|
|
Modes: []string{"peer"},
|
|
}})
|
|
|
|
participantRole.SetSessionJoinPolicies([]*types.SessionJoinPolicy{{
|
|
Roles: []string{hostRole.GetName()},
|
|
Kinds: []string{string(types.SSHSessionKind), string(types.KubernetesSessionKind)},
|
|
Modes: []string{"*"},
|
|
}})
|
|
|
|
return startTestCase{
|
|
name: "success",
|
|
host: []types.Role{hostRole},
|
|
sessionKinds: []types.SessionKind{types.SSHSessionKind, types.KubernetesSessionKind},
|
|
participants: []SessionAccessContext{
|
|
{
|
|
Username: "participant",
|
|
Roles: []types.Role{participantRole},
|
|
Mode: "peer",
|
|
},
|
|
{
|
|
Username: "participant2",
|
|
Roles: []types.Role{participantRole},
|
|
Mode: "peer",
|
|
},
|
|
},
|
|
expected: []bool{true, true},
|
|
terminate: types.OnSessionLeaveTerminate,
|
|
}
|
|
}
|
|
|
|
func successStartTestCasePause(t *testing.T) startTestCase {
|
|
hostRole, err := types.NewRole("host", types.RoleSpecV6{})
|
|
require.NoError(t, err)
|
|
participantRole, err := types.NewRole("participant", types.RoleSpecV6{})
|
|
require.NoError(t, err)
|
|
|
|
hostRole.SetSessionRequirePolicies([]*types.SessionRequirePolicy{{
|
|
Filter: "contains(user.roles, \"participant\")",
|
|
Kinds: []string{string(types.SSHSessionKind), string(types.KubernetesSessionKind)},
|
|
Count: 2,
|
|
OnLeave: string(types.OnSessionLeavePause),
|
|
Modes: []string{"peer"},
|
|
}})
|
|
|
|
participantRole.SetSessionJoinPolicies([]*types.SessionJoinPolicy{{
|
|
Roles: []string{hostRole.GetName()},
|
|
Kinds: []string{string(types.SSHSessionKind), string(types.KubernetesSessionKind)},
|
|
Modes: []string{"*"},
|
|
}})
|
|
|
|
return startTestCase{
|
|
name: "successStartTestCasePause",
|
|
host: []types.Role{hostRole},
|
|
sessionKinds: []types.SessionKind{types.SSHSessionKind, types.KubernetesSessionKind},
|
|
participants: []SessionAccessContext{
|
|
{
|
|
Username: "participant",
|
|
Roles: []types.Role{participantRole},
|
|
Mode: "peer",
|
|
},
|
|
{
|
|
Username: "participant2",
|
|
Roles: []types.Role{participantRole},
|
|
Mode: "peer",
|
|
},
|
|
},
|
|
expected: []bool{true, true},
|
|
terminate: types.OnSessionLeavePause,
|
|
}
|
|
}
|
|
|
|
func pauseCanBeOverwritten(t *testing.T) startTestCase {
|
|
hostRole, err := types.NewRole("host", types.RoleSpecV6{})
|
|
require.NoError(t, err)
|
|
participantRole, err := types.NewRole("participant", types.RoleSpecV6{})
|
|
require.NoError(t, err)
|
|
|
|
hostRole.SetSessionRequirePolicies([]*types.SessionRequirePolicy{
|
|
{
|
|
Filter: "contains(user.roles, \"participant\")",
|
|
Kinds: []string{string(types.SSHSessionKind), string(types.KubernetesSessionKind)},
|
|
Count: 2,
|
|
OnLeave: string(types.OnSessionLeavePause),
|
|
Modes: []string{"peer"},
|
|
},
|
|
{
|
|
Filter: "contains(user.roles, \"participant\")",
|
|
Kinds: []string{string(types.SSHSessionKind), string(types.KubernetesSessionKind)},
|
|
Count: 2,
|
|
OnLeave: string(types.OnSessionLeaveTerminate),
|
|
Modes: []string{"peer"},
|
|
},
|
|
})
|
|
|
|
participantRole.SetSessionJoinPolicies([]*types.SessionJoinPolicy{{
|
|
Roles: []string{hostRole.GetName()},
|
|
Kinds: []string{string(types.SSHSessionKind), string(types.KubernetesSessionKind)},
|
|
Modes: []string{"*"},
|
|
}})
|
|
|
|
return startTestCase{
|
|
name: "pauseCanBeOverwritten",
|
|
host: []types.Role{hostRole},
|
|
sessionKinds: []types.SessionKind{types.SSHSessionKind, types.KubernetesSessionKind},
|
|
participants: []SessionAccessContext{
|
|
{
|
|
Username: "participant",
|
|
Roles: []types.Role{participantRole},
|
|
Mode: "peer",
|
|
},
|
|
{
|
|
Username: "participant2",
|
|
Roles: []types.Role{participantRole},
|
|
Mode: "peer",
|
|
},
|
|
},
|
|
expected: []bool{true, true},
|
|
terminate: types.OnSessionLeaveTerminate,
|
|
}
|
|
}
|
|
|
|
func successStartTestCaseSpec(t *testing.T) startTestCase {
|
|
hostRole, err := types.NewRole("host", types.RoleSpecV6{})
|
|
require.NoError(t, err)
|
|
participantRole, err := types.NewRole("participant", types.RoleSpecV6{})
|
|
require.NoError(t, err)
|
|
|
|
hostRole.SetSessionRequirePolicies([]*types.SessionRequirePolicy{{
|
|
Filter: "contains(user.spec.roles, \"participant\")",
|
|
Kinds: []string{string(types.SSHSessionKind), string(types.KubernetesSessionKind)},
|
|
Count: 2,
|
|
OnLeave: string(types.OnSessionLeaveTerminate),
|
|
Modes: []string{"peer"},
|
|
}})
|
|
|
|
participantRole.SetSessionJoinPolicies([]*types.SessionJoinPolicy{{
|
|
Roles: []string{hostRole.GetName()},
|
|
Kinds: []string{string(types.SSHSessionKind), string(types.KubernetesSessionKind)},
|
|
Modes: []string{"*"},
|
|
}})
|
|
|
|
return startTestCase{
|
|
name: "success with spec",
|
|
host: []types.Role{hostRole},
|
|
sessionKinds: []types.SessionKind{types.SSHSessionKind, types.KubernetesSessionKind},
|
|
participants: []SessionAccessContext{
|
|
{
|
|
Username: "participant",
|
|
Roles: []types.Role{participantRole},
|
|
Mode: "peer",
|
|
},
|
|
{
|
|
Username: "participant2",
|
|
Roles: []types.Role{participantRole},
|
|
Mode: "peer",
|
|
},
|
|
},
|
|
expected: []bool{true, true},
|
|
terminate: types.OnSessionLeaveTerminate,
|
|
}
|
|
}
|
|
|
|
func failCountStartTestCase(t *testing.T) startTestCase {
|
|
hostRole, err := types.NewRole("host", types.RoleSpecV6{})
|
|
require.NoError(t, err)
|
|
participantRole, err := types.NewRole("participant", types.RoleSpecV6{})
|
|
require.NoError(t, err)
|
|
|
|
hostRole.SetSessionRequirePolicies([]*types.SessionRequirePolicy{{
|
|
Filter: "contains(user.roles, \"participant\")",
|
|
Kinds: []string{string(types.SSHSessionKind), string(types.KubernetesSessionKind)},
|
|
Count: 3,
|
|
Modes: []string{"peer"},
|
|
}})
|
|
|
|
participantRole.SetSessionJoinPolicies([]*types.SessionJoinPolicy{{
|
|
Roles: []string{hostRole.GetName()},
|
|
Kinds: []string{string(types.SSHSessionKind), string(types.KubernetesSessionKind)},
|
|
Modes: []string{"*"},
|
|
}})
|
|
|
|
return startTestCase{
|
|
name: "failCount",
|
|
host: []types.Role{hostRole},
|
|
sessionKinds: []types.SessionKind{types.SSHSessionKind, types.KubernetesSessionKind},
|
|
participants: []SessionAccessContext{
|
|
{
|
|
Username: "participant",
|
|
Roles: []types.Role{participantRole},
|
|
Mode: "peer",
|
|
},
|
|
{
|
|
Username: "participant2",
|
|
Roles: []types.Role{participantRole},
|
|
Mode: "peer",
|
|
},
|
|
},
|
|
expected: []bool{false, false},
|
|
terminate: types.OnSessionLeaveTerminate,
|
|
}
|
|
}
|
|
|
|
func succeedDiscardPolicySetStartTestCase(t *testing.T) startTestCase {
|
|
hostRole, err := types.NewRole("host", types.RoleSpecV6{})
|
|
require.NoError(t, err)
|
|
|
|
hostRole.SetSessionRequirePolicies([]*types.SessionRequirePolicy{{
|
|
Filter: "contains(user.roles, \"host\")",
|
|
Kinds: []string{string(types.KubernetesSessionKind)},
|
|
Count: 2,
|
|
Modes: []string{"peer"},
|
|
}})
|
|
|
|
return startTestCase{
|
|
name: "succeedDiscardPolicySet",
|
|
host: []types.Role{hostRole},
|
|
sessionKinds: []types.SessionKind{types.SSHSessionKind},
|
|
expected: []bool{true},
|
|
}
|
|
}
|
|
|
|
func failFilterStartTestCase(t *testing.T) startTestCase {
|
|
hostRole, err := types.NewRole("host", types.RoleSpecV6{})
|
|
require.NoError(t, err)
|
|
participantRole, err := types.NewRole("participant", types.RoleSpecV6{})
|
|
require.NoError(t, err)
|
|
|
|
hostRole.SetSessionRequirePolicies([]*types.SessionRequirePolicy{{
|
|
Filter: "contains(user.roles, \"host\")",
|
|
Kinds: []string{string(types.SSHSessionKind)},
|
|
Count: 2,
|
|
Modes: []string{"peer"},
|
|
}})
|
|
|
|
participantRole.SetSessionJoinPolicies([]*types.SessionJoinPolicy{{
|
|
Roles: []string{hostRole.GetName()},
|
|
Kinds: []string{string(types.SSHSessionKind)},
|
|
Modes: []string{"*"},
|
|
}})
|
|
|
|
return startTestCase{
|
|
name: "failFilter",
|
|
host: []types.Role{hostRole},
|
|
sessionKinds: []types.SessionKind{types.SSHSessionKind},
|
|
participants: []SessionAccessContext{
|
|
{
|
|
Username: "participant",
|
|
Roles: []types.Role{participantRole},
|
|
Mode: "peer",
|
|
},
|
|
{
|
|
Username: "participant2",
|
|
Roles: []types.Role{participantRole},
|
|
Mode: "peer",
|
|
},
|
|
},
|
|
expected: []bool{false},
|
|
terminate: types.OnSessionLeaveTerminate,
|
|
}
|
|
}
|
|
|
|
func TestSessionAccessStart(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testCases := []startTestCase{
|
|
successStartTestCase(t),
|
|
successStartTestCasePause(t),
|
|
successStartTestCaseSpec(t),
|
|
failCountStartTestCase(t),
|
|
failFilterStartTestCase(t),
|
|
succeedDiscardPolicySetStartTestCase(t),
|
|
pauseCanBeOverwritten(t),
|
|
}
|
|
|
|
for _, testCase := range testCases {
|
|
testCase := testCase
|
|
t.Run(testCase.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
var policies []*types.SessionTrackerPolicySet
|
|
for _, role := range testCase.host {
|
|
policySet := role.GetSessionPolicySet()
|
|
policies = append(policies, &policySet)
|
|
}
|
|
|
|
for i, kind := range testCase.sessionKinds {
|
|
evaluator := NewSessionAccessEvaluator(policies, kind, testCase.owner)
|
|
result, policyOptions, err := evaluator.FulfilledFor(testCase.participants)
|
|
require.NoError(t, err)
|
|
require.Equal(t, testCase.expected[i], result)
|
|
require.Equal(t, testCase.terminate, policyOptions.OnLeaveAction)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
type joinTestCase struct {
|
|
name string
|
|
host types.Role
|
|
sessionKinds []types.SessionKind
|
|
participant SessionAccessContext
|
|
owner string
|
|
expected []bool
|
|
}
|
|
|
|
func successJoinTestCase(t *testing.T) joinTestCase {
|
|
hostRole, err := types.NewRole("host", types.RoleSpecV6{})
|
|
require.NoError(t, err)
|
|
participantRole, err := types.NewRole("participant", types.RoleSpecV6{})
|
|
require.NoError(t, err)
|
|
|
|
participantRole.SetSessionJoinPolicies([]*types.SessionJoinPolicy{{
|
|
Roles: []string{hostRole.GetName()},
|
|
Kinds: []string{string(types.SSHSessionKind), string(types.KubernetesSessionKind)},
|
|
Modes: []string{types.Wildcard},
|
|
}})
|
|
|
|
return joinTestCase{
|
|
name: "success",
|
|
host: hostRole,
|
|
sessionKinds: []types.SessionKind{types.SSHSessionKind, types.KubernetesSessionKind},
|
|
participant: SessionAccessContext{
|
|
Username: "participant",
|
|
Roles: []types.Role{participantRole},
|
|
},
|
|
expected: []bool{true, true},
|
|
}
|
|
}
|
|
|
|
func successGlobJoinTestCase(t *testing.T) joinTestCase {
|
|
hostRole, err := types.NewRole("host", types.RoleSpecV6{})
|
|
require.NoError(t, err)
|
|
participantRole, err := types.NewRole("participant", types.RoleSpecV6{})
|
|
require.NoError(t, err)
|
|
|
|
participantRole.SetSessionJoinPolicies([]*types.SessionJoinPolicy{{
|
|
Roles: []string{types.Wildcard},
|
|
Kinds: []string{string(types.SSHSessionKind), string(types.KubernetesSessionKind)},
|
|
Modes: []string{types.Wildcard},
|
|
}})
|
|
|
|
return joinTestCase{
|
|
name: "successGlobJoin",
|
|
host: hostRole,
|
|
sessionKinds: []types.SessionKind{types.SSHSessionKind, types.KubernetesSessionKind},
|
|
participant: SessionAccessContext{
|
|
Username: "participant",
|
|
Roles: []types.Role{participantRole},
|
|
},
|
|
expected: []bool{true, true},
|
|
}
|
|
}
|
|
|
|
func successSameUserJoinTestCase(t *testing.T) joinTestCase {
|
|
hostRole, err := types.NewRole("host", types.RoleSpecV6{})
|
|
require.NoError(t, err)
|
|
participantRole, err := types.NewRole("participant", types.RoleSpecV6{})
|
|
require.NoError(t, err)
|
|
|
|
return joinTestCase{
|
|
name: "successSameUser",
|
|
host: hostRole,
|
|
sessionKinds: []types.SessionKind{types.SSHSessionKind},
|
|
participant: SessionAccessContext{
|
|
Username: "john",
|
|
Roles: []types.Role{participantRole},
|
|
},
|
|
owner: "john",
|
|
expected: []bool{true},
|
|
}
|
|
}
|
|
|
|
func failRoleJoinTestCase(t *testing.T) joinTestCase {
|
|
hostRole, err := types.NewRole("host", types.RoleSpecV6{})
|
|
require.NoError(t, err)
|
|
participantRole, err := types.NewRole("participant", types.RoleSpecV6{})
|
|
require.NoError(t, err)
|
|
|
|
return joinTestCase{
|
|
name: "failRole",
|
|
host: hostRole,
|
|
sessionKinds: []types.SessionKind{types.SSHSessionKind, types.KubernetesSessionKind},
|
|
participant: SessionAccessContext{
|
|
Username: "participant",
|
|
Roles: []types.Role{participantRole},
|
|
},
|
|
expected: []bool{false, false},
|
|
}
|
|
}
|
|
|
|
func failKindJoinTestCase(t *testing.T) joinTestCase {
|
|
hostRole, err := types.NewRole("host", types.RoleSpecV6{})
|
|
require.NoError(t, err)
|
|
participantRole, err := types.NewRole("participant", types.RoleSpecV6{})
|
|
require.NoError(t, err)
|
|
|
|
participantRole.SetSessionJoinPolicies([]*types.SessionJoinPolicy{{
|
|
Roles: []string{hostRole.GetName()},
|
|
Kinds: []string{string(types.KubernetesSessionKind)},
|
|
Modes: []string{types.Wildcard},
|
|
}})
|
|
|
|
return joinTestCase{
|
|
name: "failKind",
|
|
host: hostRole,
|
|
sessionKinds: []types.SessionKind{types.SSHSessionKind},
|
|
participant: SessionAccessContext{
|
|
Username: "participant",
|
|
Roles: []types.Role{participantRole},
|
|
},
|
|
expected: []bool{false},
|
|
}
|
|
}
|
|
|
|
func versionDefaultJoinTestCase(t *testing.T) joinTestCase {
|
|
hostRole, err := types.NewRole("host", types.RoleSpecV6{})
|
|
require.NoError(t, err)
|
|
|
|
// create a v3 role to check that access controls
|
|
// prior to Moderated Sessions are honored
|
|
participantRole := &types.RoleV6{
|
|
Version: types.V3,
|
|
Metadata: types.Metadata{
|
|
Name: "participant",
|
|
},
|
|
Spec: types.RoleSpecV6{},
|
|
}
|
|
require.NoError(t, participantRole.CheckAndSetDefaults())
|
|
|
|
return joinTestCase{
|
|
name: "failVersion",
|
|
host: hostRole,
|
|
sessionKinds: []types.SessionKind{types.SSHSessionKind, types.KubernetesSessionKind},
|
|
participant: SessionAccessContext{
|
|
Username: "participant",
|
|
Roles: []types.Role{participantRole},
|
|
},
|
|
expected: []bool{true, false},
|
|
}
|
|
}
|
|
|
|
func TestSessionAccessJoin(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testCases := []joinTestCase{
|
|
successJoinTestCase(t),
|
|
successGlobJoinTestCase(t),
|
|
successSameUserJoinTestCase(t),
|
|
failRoleJoinTestCase(t),
|
|
failKindJoinTestCase(t),
|
|
versionDefaultJoinTestCase(t),
|
|
}
|
|
|
|
for _, testCase := range testCases {
|
|
t.Run(testCase.name, func(t *testing.T) {
|
|
for i, kind := range testCase.sessionKinds {
|
|
t.Run(string(kind), func(t *testing.T) {
|
|
policy := testCase.host.GetSessionPolicySet()
|
|
evaluator := NewSessionAccessEvaluator([]*types.SessionTrackerPolicySet{&policy}, kind, testCase.owner)
|
|
result := evaluator.CanJoin(testCase.participant)
|
|
require.Equal(t, testCase.expected[i], len(result) > 0)
|
|
})
|
|
}
|
|
})
|
|
}
|
|
}
|