mirror of
https://github.com/juanfont/headscale.git
synced 2026-01-23 02:24:10 +00:00
policy: update tests for SSH validation rules
Update unit tests to use valid SSH patterns that conform to Tailscale's security model: - Change group->user destinations to group->tag - Change tag->user destinations to tag->tag - Update expected error messages for new validation format - Add proper tagged/untagged node setup in filter tests Updates #3009 Updates #3010
This commit is contained in:
parent
9a1ae17996
commit
3f49ef9f1a
3 changed files with 407 additions and 77 deletions
|
|
@ -1092,6 +1092,15 @@ func TestSSHPolicyRules(t *testing.T) {
|
|||
Tags: []string{"tag:client"},
|
||||
}
|
||||
|
||||
// Create a tagged server node for valid SSH patterns
|
||||
nodeTaggedServer := types.Node{
|
||||
Hostname: "tagged-server",
|
||||
IPv4: ap("100.64.0.5"),
|
||||
UserID: ptr.To(uint(1)),
|
||||
User: ptr.To(users[0]),
|
||||
Tags: []string{"tag:server"},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
targetNode types.Node
|
||||
|
|
@ -1102,10 +1111,13 @@ func TestSSHPolicyRules(t *testing.T) {
|
|||
errorMessage string
|
||||
}{
|
||||
{
|
||||
name: "group-to-user",
|
||||
targetNode: nodeUser1,
|
||||
name: "group-to-tag",
|
||||
targetNode: nodeTaggedServer,
|
||||
peers: types.Nodes{&nodeUser2},
|
||||
policy: `{
|
||||
"tagOwners": {
|
||||
"tag:server": ["user1@"]
|
||||
},
|
||||
"groups": {
|
||||
"group:admins": ["user2@"]
|
||||
},
|
||||
|
|
@ -1113,7 +1125,7 @@ func TestSSHPolicyRules(t *testing.T) {
|
|||
{
|
||||
"action": "accept",
|
||||
"src": ["group:admins"],
|
||||
"dst": ["user1@"],
|
||||
"dst": ["tag:server"],
|
||||
"users": ["autogroup:nonroot"]
|
||||
}
|
||||
]
|
||||
|
|
@ -1138,18 +1150,21 @@ func TestSSHPolicyRules(t *testing.T) {
|
|||
},
|
||||
{
|
||||
name: "check-period-specified",
|
||||
targetNode: nodeUser1,
|
||||
peers: types.Nodes{&taggedClient},
|
||||
targetNode: taggedClient,
|
||||
peers: types.Nodes{&nodeUser2},
|
||||
policy: `{
|
||||
"tagOwners": {
|
||||
"tag:client": ["user1@"],
|
||||
"tag:client": ["user1@"]
|
||||
},
|
||||
"groups": {
|
||||
"group:admins": ["user2@"]
|
||||
},
|
||||
"ssh": [
|
||||
{
|
||||
"action": "check",
|
||||
"checkPeriod": "24h",
|
||||
"src": ["tag:client"],
|
||||
"dst": ["user1@"],
|
||||
"src": ["group:admins"],
|
||||
"dst": ["tag:client"],
|
||||
"users": ["autogroup:nonroot"]
|
||||
}
|
||||
]
|
||||
|
|
@ -1157,7 +1172,7 @@ func TestSSHPolicyRules(t *testing.T) {
|
|||
wantSSH: &tailcfg.SSHPolicy{Rules: []*tailcfg.SSHRule{
|
||||
{
|
||||
Principals: []*tailcfg.SSHPrincipal{
|
||||
{NodeIP: "100.64.0.4"},
|
||||
{NodeIP: "100.64.0.2"},
|
||||
},
|
||||
SSHUsers: map[string]string{
|
||||
"*": "=",
|
||||
|
|
@ -1176,16 +1191,19 @@ func TestSSHPolicyRules(t *testing.T) {
|
|||
{
|
||||
name: "no-matching-rules",
|
||||
targetNode: nodeUser2,
|
||||
peers: types.Nodes{&nodeUser1},
|
||||
peers: types.Nodes{&nodeUser1, &nodeTaggedServer},
|
||||
policy: `{
|
||||
"tagOwners": {
|
||||
"tag:client": ["user1@"],
|
||||
"tag:server": ["user1@"]
|
||||
},
|
||||
"groups": {
|
||||
"group:admins": ["user1@"]
|
||||
},
|
||||
"ssh": [
|
||||
{
|
||||
"action": "accept",
|
||||
"src": ["tag:client"],
|
||||
"dst": ["user1@"],
|
||||
"src": ["group:admins"],
|
||||
"dst": ["tag:server"],
|
||||
"users": ["autogroup:nonroot"]
|
||||
}
|
||||
]
|
||||
|
|
@ -1194,14 +1212,20 @@ func TestSSHPolicyRules(t *testing.T) {
|
|||
},
|
||||
{
|
||||
name: "invalid-action",
|
||||
targetNode: nodeUser1,
|
||||
targetNode: nodeTaggedServer,
|
||||
peers: types.Nodes{&nodeUser2},
|
||||
policy: `{
|
||||
"tagOwners": {
|
||||
"tag:server": ["user1@"]
|
||||
},
|
||||
"groups": {
|
||||
"group:admins": ["user2@"]
|
||||
},
|
||||
"ssh": [
|
||||
{
|
||||
"action": "invalid",
|
||||
"src": ["group:admins"],
|
||||
"dst": ["user1@"],
|
||||
"dst": ["tag:server"],
|
||||
"users": ["autogroup:nonroot"]
|
||||
}
|
||||
]
|
||||
|
|
@ -1211,15 +1235,21 @@ func TestSSHPolicyRules(t *testing.T) {
|
|||
},
|
||||
{
|
||||
name: "invalid-check-period",
|
||||
targetNode: nodeUser1,
|
||||
targetNode: nodeTaggedServer,
|
||||
peers: types.Nodes{&nodeUser2},
|
||||
policy: `{
|
||||
"tagOwners": {
|
||||
"tag:server": ["user1@"]
|
||||
},
|
||||
"groups": {
|
||||
"group:admins": ["user2@"]
|
||||
},
|
||||
"ssh": [
|
||||
{
|
||||
"action": "check",
|
||||
"checkPeriod": "invalid",
|
||||
"src": ["group:admins"],
|
||||
"dst": ["user1@"],
|
||||
"dst": ["tag:server"],
|
||||
"users": ["autogroup:nonroot"]
|
||||
}
|
||||
]
|
||||
|
|
@ -1229,26 +1259,12 @@ func TestSSHPolicyRules(t *testing.T) {
|
|||
},
|
||||
{
|
||||
name: "unsupported-autogroup",
|
||||
targetNode: nodeUser1,
|
||||
peers: types.Nodes{&taggedClient},
|
||||
policy: `{
|
||||
"ssh": [
|
||||
{
|
||||
"action": "accept",
|
||||
"src": ["tag:client"],
|
||||
"dst": ["user1@"],
|
||||
"users": ["autogroup:invalid"]
|
||||
}
|
||||
]
|
||||
}`,
|
||||
expectErr: true,
|
||||
errorMessage: "autogroup \"autogroup:invalid\" is not supported",
|
||||
},
|
||||
{
|
||||
name: "autogroup-nonroot-should-use-wildcard-with-root-excluded",
|
||||
targetNode: nodeUser1,
|
||||
targetNode: taggedClient,
|
||||
peers: types.Nodes{&nodeUser2},
|
||||
policy: `{
|
||||
"tagOwners": {
|
||||
"tag:client": ["user1@"]
|
||||
},
|
||||
"groups": {
|
||||
"group:admins": ["user2@"]
|
||||
},
|
||||
|
|
@ -1256,7 +1272,30 @@ func TestSSHPolicyRules(t *testing.T) {
|
|||
{
|
||||
"action": "accept",
|
||||
"src": ["group:admins"],
|
||||
"dst": ["user1@"],
|
||||
"dst": ["tag:client"],
|
||||
"users": ["autogroup:invalid"]
|
||||
}
|
||||
]
|
||||
}`,
|
||||
expectErr: true,
|
||||
errorMessage: "autogroup \"autogroup:invalid\" is not supported",
|
||||
},
|
||||
{
|
||||
name: "autogroup-nonroot-should-use-wildcard-with-root-excluded",
|
||||
targetNode: nodeTaggedServer,
|
||||
peers: types.Nodes{&nodeUser2},
|
||||
policy: `{
|
||||
"tagOwners": {
|
||||
"tag:server": ["user1@"]
|
||||
},
|
||||
"groups": {
|
||||
"group:admins": ["user2@"]
|
||||
},
|
||||
"ssh": [
|
||||
{
|
||||
"action": "accept",
|
||||
"src": ["group:admins"],
|
||||
"dst": ["tag:server"],
|
||||
"users": ["autogroup:nonroot"]
|
||||
}
|
||||
]
|
||||
|
|
@ -1282,9 +1321,12 @@ func TestSSHPolicyRules(t *testing.T) {
|
|||
},
|
||||
{
|
||||
name: "autogroup-nonroot-plus-root-should-use-wildcard-with-root-mapped",
|
||||
targetNode: nodeUser1,
|
||||
targetNode: nodeTaggedServer,
|
||||
peers: types.Nodes{&nodeUser2},
|
||||
policy: `{
|
||||
"tagOwners": {
|
||||
"tag:server": ["user1@"]
|
||||
},
|
||||
"groups": {
|
||||
"group:admins": ["user2@"]
|
||||
},
|
||||
|
|
@ -1292,7 +1334,7 @@ func TestSSHPolicyRules(t *testing.T) {
|
|||
{
|
||||
"action": "accept",
|
||||
"src": ["group:admins"],
|
||||
"dst": ["user1@"],
|
||||
"dst": ["tag:server"],
|
||||
"users": ["autogroup:nonroot", "root"]
|
||||
}
|
||||
]
|
||||
|
|
@ -1318,9 +1360,12 @@ func TestSSHPolicyRules(t *testing.T) {
|
|||
},
|
||||
{
|
||||
name: "specific-users-should-map-to-themselves-not-equals",
|
||||
targetNode: nodeUser1,
|
||||
targetNode: nodeTaggedServer,
|
||||
peers: types.Nodes{&nodeUser2},
|
||||
policy: `{
|
||||
"tagOwners": {
|
||||
"tag:server": ["user1@"]
|
||||
},
|
||||
"groups": {
|
||||
"group:admins": ["user2@"]
|
||||
},
|
||||
|
|
@ -1328,7 +1373,7 @@ func TestSSHPolicyRules(t *testing.T) {
|
|||
{
|
||||
"action": "accept",
|
||||
"src": ["group:admins"],
|
||||
"dst": ["user1@"],
|
||||
"dst": ["tag:server"],
|
||||
"users": ["ubuntu", "root"]
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -406,21 +406,33 @@ func TestCompileSSHPolicy_UserMapping(t *testing.T) {
|
|||
{Name: "user2", Model: gorm.Model{ID: 2}},
|
||||
}
|
||||
|
||||
// Create test nodes
|
||||
nodeUser1 := types.Node{
|
||||
Hostname: "user1-device",
|
||||
// Create test nodes - use tagged nodes as SSH destinations
|
||||
// and untagged nodes as SSH sources (since group->username destinations
|
||||
// are not allowed per Tailscale security model, but groups can SSH to tags)
|
||||
nodeTaggedServer := types.Node{
|
||||
Hostname: "tagged-server",
|
||||
IPv4: createAddr("100.64.0.1"),
|
||||
UserID: ptr.To(users[0].ID),
|
||||
User: ptr.To(users[0]),
|
||||
Tags: []string{"tag:server"},
|
||||
}
|
||||
nodeUser2 := types.Node{
|
||||
Hostname: "user2-device",
|
||||
nodeTaggedDB := types.Node{
|
||||
Hostname: "tagged-db",
|
||||
IPv4: createAddr("100.64.0.2"),
|
||||
UserID: ptr.To(users[1].ID),
|
||||
User: ptr.To(users[1]),
|
||||
Tags: []string{"tag:database"},
|
||||
}
|
||||
// Add untagged node for user2 - this will be the SSH source
|
||||
// (group:admins contains user2, so user2's untagged node provides the source IPs)
|
||||
nodeUser2Untagged := types.Node{
|
||||
Hostname: "user2-device",
|
||||
IPv4: createAddr("100.64.0.3"),
|
||||
UserID: ptr.To(users[1].ID),
|
||||
User: ptr.To(users[1]),
|
||||
}
|
||||
|
||||
nodes := types.Nodes{&nodeUser1, &nodeUser2}
|
||||
nodes := types.Nodes{&nodeTaggedServer, &nodeTaggedDB, &nodeUser2Untagged}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
|
|
@ -431,8 +443,11 @@ func TestCompileSSHPolicy_UserMapping(t *testing.T) {
|
|||
}{
|
||||
{
|
||||
name: "specific user mapping",
|
||||
targetNode: nodeUser1,
|
||||
targetNode: nodeTaggedServer,
|
||||
policy: &Policy{
|
||||
TagOwners: TagOwners{
|
||||
Tag("tag:server"): Owners{up("user1@")},
|
||||
},
|
||||
Groups: Groups{
|
||||
Group("group:admins"): []Username{Username("user2@")},
|
||||
},
|
||||
|
|
@ -440,7 +455,7 @@ func TestCompileSSHPolicy_UserMapping(t *testing.T) {
|
|||
{
|
||||
Action: "accept",
|
||||
Sources: SSHSrcAliases{gp("group:admins")},
|
||||
Destinations: SSHDstAliases{up("user1@")},
|
||||
Destinations: SSHDstAliases{tp("tag:server")},
|
||||
Users: []SSHUser{"ssh-it-user"},
|
||||
},
|
||||
},
|
||||
|
|
@ -451,8 +466,11 @@ func TestCompileSSHPolicy_UserMapping(t *testing.T) {
|
|||
},
|
||||
{
|
||||
name: "multiple specific users",
|
||||
targetNode: nodeUser1,
|
||||
targetNode: nodeTaggedServer,
|
||||
policy: &Policy{
|
||||
TagOwners: TagOwners{
|
||||
Tag("tag:server"): Owners{up("user1@")},
|
||||
},
|
||||
Groups: Groups{
|
||||
Group("group:admins"): []Username{Username("user2@")},
|
||||
},
|
||||
|
|
@ -460,7 +478,7 @@ func TestCompileSSHPolicy_UserMapping(t *testing.T) {
|
|||
{
|
||||
Action: "accept",
|
||||
Sources: SSHSrcAliases{gp("group:admins")},
|
||||
Destinations: SSHDstAliases{up("user1@")},
|
||||
Destinations: SSHDstAliases{tp("tag:server")},
|
||||
Users: []SSHUser{"ubuntu", "admin", "deploy"},
|
||||
},
|
||||
},
|
||||
|
|
@ -473,8 +491,11 @@ func TestCompileSSHPolicy_UserMapping(t *testing.T) {
|
|||
},
|
||||
{
|
||||
name: "autogroup:nonroot only",
|
||||
targetNode: nodeUser1,
|
||||
targetNode: nodeTaggedServer,
|
||||
policy: &Policy{
|
||||
TagOwners: TagOwners{
|
||||
Tag("tag:server"): Owners{up("user1@")},
|
||||
},
|
||||
Groups: Groups{
|
||||
Group("group:admins"): []Username{Username("user2@")},
|
||||
},
|
||||
|
|
@ -482,7 +503,7 @@ func TestCompileSSHPolicy_UserMapping(t *testing.T) {
|
|||
{
|
||||
Action: "accept",
|
||||
Sources: SSHSrcAliases{gp("group:admins")},
|
||||
Destinations: SSHDstAliases{up("user1@")},
|
||||
Destinations: SSHDstAliases{tp("tag:server")},
|
||||
Users: []SSHUser{SSHUser(AutoGroupNonRoot)},
|
||||
},
|
||||
},
|
||||
|
|
@ -494,8 +515,11 @@ func TestCompileSSHPolicy_UserMapping(t *testing.T) {
|
|||
},
|
||||
{
|
||||
name: "root only",
|
||||
targetNode: nodeUser1,
|
||||
targetNode: nodeTaggedServer,
|
||||
policy: &Policy{
|
||||
TagOwners: TagOwners{
|
||||
Tag("tag:server"): Owners{up("user1@")},
|
||||
},
|
||||
Groups: Groups{
|
||||
Group("group:admins"): []Username{Username("user2@")},
|
||||
},
|
||||
|
|
@ -503,7 +527,7 @@ func TestCompileSSHPolicy_UserMapping(t *testing.T) {
|
|||
{
|
||||
Action: "accept",
|
||||
Sources: SSHSrcAliases{gp("group:admins")},
|
||||
Destinations: SSHDstAliases{up("user1@")},
|
||||
Destinations: SSHDstAliases{tp("tag:server")},
|
||||
Users: []SSHUser{"root"},
|
||||
},
|
||||
},
|
||||
|
|
@ -514,8 +538,11 @@ func TestCompileSSHPolicy_UserMapping(t *testing.T) {
|
|||
},
|
||||
{
|
||||
name: "autogroup:nonroot plus root",
|
||||
targetNode: nodeUser1,
|
||||
targetNode: nodeTaggedServer,
|
||||
policy: &Policy{
|
||||
TagOwners: TagOwners{
|
||||
Tag("tag:server"): Owners{up("user1@")},
|
||||
},
|
||||
Groups: Groups{
|
||||
Group("group:admins"): []Username{Username("user2@")},
|
||||
},
|
||||
|
|
@ -523,7 +550,7 @@ func TestCompileSSHPolicy_UserMapping(t *testing.T) {
|
|||
{
|
||||
Action: "accept",
|
||||
Sources: SSHSrcAliases{gp("group:admins")},
|
||||
Destinations: SSHDstAliases{up("user1@")},
|
||||
Destinations: SSHDstAliases{tp("tag:server")},
|
||||
Users: []SSHUser{SSHUser(AutoGroupNonRoot), "root"},
|
||||
},
|
||||
},
|
||||
|
|
@ -535,8 +562,11 @@ func TestCompileSSHPolicy_UserMapping(t *testing.T) {
|
|||
},
|
||||
{
|
||||
name: "mixed specific users and autogroups",
|
||||
targetNode: nodeUser1,
|
||||
targetNode: nodeTaggedServer,
|
||||
policy: &Policy{
|
||||
TagOwners: TagOwners{
|
||||
Tag("tag:server"): Owners{up("user1@")},
|
||||
},
|
||||
Groups: Groups{
|
||||
Group("group:admins"): []Username{Username("user2@")},
|
||||
},
|
||||
|
|
@ -544,7 +574,7 @@ func TestCompileSSHPolicy_UserMapping(t *testing.T) {
|
|||
{
|
||||
Action: "accept",
|
||||
Sources: SSHSrcAliases{gp("group:admins")},
|
||||
Destinations: SSHDstAliases{up("user1@")},
|
||||
Destinations: SSHDstAliases{tp("tag:server")},
|
||||
Users: []SSHUser{SSHUser(AutoGroupNonRoot), "root", "ubuntu", "admin"},
|
||||
},
|
||||
},
|
||||
|
|
@ -558,8 +588,12 @@ func TestCompileSSHPolicy_UserMapping(t *testing.T) {
|
|||
},
|
||||
{
|
||||
name: "no matching destination",
|
||||
targetNode: nodeUser2, // Target node2, but policy only allows user1
|
||||
targetNode: nodeTaggedDB, // Target tag:database, but policy only allows tag:server
|
||||
policy: &Policy{
|
||||
TagOwners: TagOwners{
|
||||
Tag("tag:server"): Owners{up("user1@")},
|
||||
Tag("tag:database"): Owners{up("user1@")},
|
||||
},
|
||||
Groups: Groups{
|
||||
Group("group:admins"): []Username{Username("user2@")},
|
||||
},
|
||||
|
|
@ -567,7 +601,7 @@ func TestCompileSSHPolicy_UserMapping(t *testing.T) {
|
|||
{
|
||||
Action: "accept",
|
||||
Sources: SSHSrcAliases{gp("group:admins")},
|
||||
Destinations: SSHDstAliases{up("user1@")}, // Only user1, not user2
|
||||
Destinations: SSHDstAliases{tp("tag:server")}, // Only tag:server, not tag:database
|
||||
Users: []SSHUser{"ssh-it-user"},
|
||||
},
|
||||
},
|
||||
|
|
@ -600,9 +634,9 @@ func TestCompileSSHPolicy_UserMapping(t *testing.T) {
|
|||
rule := sshPolicy.Rules[0]
|
||||
assert.Equal(t, tt.wantSSHUsers, rule.SSHUsers, "SSH users mapping should match expected")
|
||||
|
||||
// Verify principals are set correctly (should contain user2's IP since that's the source)
|
||||
// Verify principals are set correctly (should contain user2's untagged device IP since that's the source)
|
||||
require.Len(t, rule.Principals, 1)
|
||||
assert.Equal(t, "100.64.0.2", rule.Principals[0].NodeIP)
|
||||
assert.Equal(t, "100.64.0.3", rule.Principals[0].NodeIP)
|
||||
|
||||
// Verify action is set correctly
|
||||
assert.True(t, rule.Action.Accept)
|
||||
|
|
@ -619,11 +653,13 @@ func TestCompileSSHPolicy_CheckAction(t *testing.T) {
|
|||
{Name: "user2", Model: gorm.Model{ID: 2}},
|
||||
}
|
||||
|
||||
nodeUser1 := types.Node{
|
||||
Hostname: "user1-device",
|
||||
// Use tagged nodes for SSH user mapping tests
|
||||
nodeTaggedServer := types.Node{
|
||||
Hostname: "tagged-server",
|
||||
IPv4: createAddr("100.64.0.1"),
|
||||
UserID: ptr.To(users[0].ID),
|
||||
User: ptr.To(users[0]),
|
||||
Tags: []string{"tag:server"},
|
||||
}
|
||||
nodeUser2 := types.Node{
|
||||
Hostname: "user2-device",
|
||||
|
|
@ -632,9 +668,12 @@ func TestCompileSSHPolicy_CheckAction(t *testing.T) {
|
|||
User: ptr.To(users[1]),
|
||||
}
|
||||
|
||||
nodes := types.Nodes{&nodeUser1, &nodeUser2}
|
||||
nodes := types.Nodes{&nodeTaggedServer, &nodeUser2}
|
||||
|
||||
policy := &Policy{
|
||||
TagOwners: TagOwners{
|
||||
Tag("tag:server"): Owners{up("user1@")},
|
||||
},
|
||||
Groups: Groups{
|
||||
Group("group:admins"): []Username{Username("user2@")},
|
||||
},
|
||||
|
|
@ -643,7 +682,7 @@ func TestCompileSSHPolicy_CheckAction(t *testing.T) {
|
|||
Action: "check",
|
||||
CheckPeriod: model.Duration(24 * time.Hour),
|
||||
Sources: SSHSrcAliases{gp("group:admins")},
|
||||
Destinations: SSHDstAliases{up("user1@")},
|
||||
Destinations: SSHDstAliases{tp("tag:server")},
|
||||
Users: []SSHUser{"ssh-it-user"},
|
||||
},
|
||||
},
|
||||
|
|
@ -652,7 +691,7 @@ func TestCompileSSHPolicy_CheckAction(t *testing.T) {
|
|||
err := policy.validate()
|
||||
require.NoError(t, err)
|
||||
|
||||
sshPolicy, err := policy.compileSSHPolicy(users, nodeUser1.View(), nodes.ViewSlice())
|
||||
sshPolicy, err := policy.compileSSHPolicy(users, nodeTaggedServer.View(), nodes.ViewSlice())
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, sshPolicy)
|
||||
require.Len(t, sshPolicy.Rules, 1)
|
||||
|
|
@ -697,16 +736,17 @@ func TestSSHIntegrationReproduction(t *testing.T) {
|
|||
nodes := types.Nodes{node1, node2}
|
||||
|
||||
// Create a simple policy that reproduces the issue
|
||||
// Updated to use autogroup:self instead of username destination (per Tailscale security model)
|
||||
policy := &Policy{
|
||||
Groups: Groups{
|
||||
Group("group:integration-test"): []Username{Username("user1@")},
|
||||
Group("group:integration-test"): []Username{Username("user1@"), Username("user2@")},
|
||||
},
|
||||
SSHs: []SSH{
|
||||
{
|
||||
Action: "accept",
|
||||
Sources: SSHSrcAliases{gp("group:integration-test")},
|
||||
Destinations: SSHDstAliases{up("user2@")}, // Target user2
|
||||
Users: []SSHUser{SSHUser("ssh-it-user")}, // This is the key - specific user
|
||||
Destinations: SSHDstAliases{agp("autogroup:self")}, // Users can SSH to their own devices
|
||||
Users: []SSHUser{SSHUser("ssh-it-user")}, // This is the key - specific user
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
@ -715,7 +755,7 @@ func TestSSHIntegrationReproduction(t *testing.T) {
|
|||
err := policy.validate()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Test SSH policy compilation for node2 (target)
|
||||
// Test SSH policy compilation for node2 (owned by user2, who is in the group)
|
||||
sshPolicy, err := policy.compileSSHPolicy(users, node2.View(), nodes.ViewSlice())
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, sshPolicy)
|
||||
|
|
|
|||
|
|
@ -664,7 +664,8 @@ func TestUnmarshalPolicy(t *testing.T) {
|
|||
input: `
|
||||
{
|
||||
"tagOwners": {
|
||||
"tag:web": ["admin@example.com"]
|
||||
"tag:web": ["admin@example.com"],
|
||||
"tag:server": ["admin@example.com"]
|
||||
},
|
||||
"ssh": [
|
||||
{
|
||||
|
|
@ -673,7 +674,7 @@ func TestUnmarshalPolicy(t *testing.T) {
|
|||
"tag:web"
|
||||
],
|
||||
"dst": [
|
||||
"admin@example.com"
|
||||
"tag:server"
|
||||
],
|
||||
"users": ["*"]
|
||||
}
|
||||
|
|
@ -682,7 +683,8 @@ func TestUnmarshalPolicy(t *testing.T) {
|
|||
`,
|
||||
want: &Policy{
|
||||
TagOwners: TagOwners{
|
||||
Tag("tag:web"): Owners{ptr.To(Username("admin@example.com"))},
|
||||
Tag("tag:web"): Owners{ptr.To(Username("admin@example.com"))},
|
||||
Tag("tag:server"): Owners{ptr.To(Username("admin@example.com"))},
|
||||
},
|
||||
SSHs: []SSH{
|
||||
{
|
||||
|
|
@ -691,7 +693,7 @@ func TestUnmarshalPolicy(t *testing.T) {
|
|||
tp("tag:web"),
|
||||
},
|
||||
Destinations: SSHDstAliases{
|
||||
ptr.To(Username("admin@example.com")),
|
||||
tp("tag:server"),
|
||||
},
|
||||
Users: []SSHUser{
|
||||
SSHUser("*"),
|
||||
|
|
@ -714,7 +716,7 @@ func TestUnmarshalPolicy(t *testing.T) {
|
|||
"group:admins"
|
||||
],
|
||||
"dst": [
|
||||
"admin@example.com"
|
||||
"autogroup:self"
|
||||
],
|
||||
"users": ["root"],
|
||||
"checkPeriod": "24h"
|
||||
|
|
@ -733,7 +735,7 @@ func TestUnmarshalPolicy(t *testing.T) {
|
|||
gp("group:admins"),
|
||||
},
|
||||
Destinations: SSHDstAliases{
|
||||
ptr.To(Username("admin@example.com")),
|
||||
agp("autogroup:self"),
|
||||
},
|
||||
Users: []SSHUser{
|
||||
SSHUser("root"),
|
||||
|
|
@ -1521,6 +1523,249 @@ func TestUnmarshalPolicy(t *testing.T) {
|
|||
`,
|
||||
wantErr: `tag "tag:child" references undefined tag "tag:nonexistent"`,
|
||||
},
|
||||
// SSH source/destination validation tests (#3009, #3010)
|
||||
{
|
||||
name: "ssh-tag-to-user-rejected",
|
||||
input: `
|
||||
{
|
||||
"tagOwners": {"tag:server": ["admin@"]},
|
||||
"ssh": [{
|
||||
"action": "accept",
|
||||
"src": ["tag:server"],
|
||||
"dst": ["admin@"],
|
||||
"users": ["autogroup:nonroot"]
|
||||
}]
|
||||
}
|
||||
`,
|
||||
wantErr: "tags in SSH source cannot access user-owned devices",
|
||||
},
|
||||
{
|
||||
name: "ssh-autogroup-tagged-to-user-rejected",
|
||||
input: `
|
||||
{
|
||||
"ssh": [{
|
||||
"action": "accept",
|
||||
"src": ["autogroup:tagged"],
|
||||
"dst": ["admin@"],
|
||||
"users": ["autogroup:nonroot"]
|
||||
}]
|
||||
}
|
||||
`,
|
||||
wantErr: "tags in SSH source cannot access user-owned devices",
|
||||
},
|
||||
{
|
||||
name: "ssh-tag-to-autogroup-self-rejected",
|
||||
input: `
|
||||
{
|
||||
"tagOwners": {"tag:server": ["admin@"]},
|
||||
"ssh": [{
|
||||
"action": "accept",
|
||||
"src": ["tag:server"],
|
||||
"dst": ["autogroup:self"],
|
||||
"users": ["autogroup:nonroot"]
|
||||
}]
|
||||
}
|
||||
`,
|
||||
wantErr: "autogroup:self destination requires source to contain only users or groups",
|
||||
},
|
||||
{
|
||||
name: "ssh-group-to-user-rejected",
|
||||
input: `
|
||||
{
|
||||
"groups": {"group:admins": ["admin@", "user1@"]},
|
||||
"ssh": [{
|
||||
"action": "accept",
|
||||
"src": ["group:admins"],
|
||||
"dst": ["admin@"],
|
||||
"users": ["autogroup:nonroot"]
|
||||
}]
|
||||
}
|
||||
`,
|
||||
wantErr: `user destination requires source to contain only that same user "admin@"`,
|
||||
},
|
||||
{
|
||||
name: "ssh-same-user-to-user-allowed",
|
||||
input: `
|
||||
{
|
||||
"ssh": [{
|
||||
"action": "accept",
|
||||
"src": ["admin@"],
|
||||
"dst": ["admin@"],
|
||||
"users": ["autogroup:nonroot"]
|
||||
}]
|
||||
}
|
||||
`,
|
||||
want: &Policy{
|
||||
SSHs: []SSH{
|
||||
{
|
||||
Action: "accept",
|
||||
Sources: SSHSrcAliases{up("admin@")},
|
||||
Destinations: SSHDstAliases{up("admin@")},
|
||||
Users: []SSHUser{SSHUser(AutoGroupNonRoot)},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ssh-group-to-autogroup-self-allowed",
|
||||
input: `
|
||||
{
|
||||
"groups": {"group:admins": ["admin@", "user1@"]},
|
||||
"ssh": [{
|
||||
"action": "accept",
|
||||
"src": ["group:admins"],
|
||||
"dst": ["autogroup:self"],
|
||||
"users": ["autogroup:nonroot"]
|
||||
}]
|
||||
}
|
||||
`,
|
||||
want: &Policy{
|
||||
Groups: Groups{
|
||||
Group("group:admins"): []Username{Username("admin@"), Username("user1@")},
|
||||
},
|
||||
SSHs: []SSH{
|
||||
{
|
||||
Action: "accept",
|
||||
Sources: SSHSrcAliases{gp("group:admins")},
|
||||
Destinations: SSHDstAliases{agp("autogroup:self")},
|
||||
Users: []SSHUser{SSHUser(AutoGroupNonRoot)},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ssh-autogroup-tagged-to-autogroup-member-rejected",
|
||||
input: `
|
||||
{
|
||||
"ssh": [{
|
||||
"action": "accept",
|
||||
"src": ["autogroup:tagged"],
|
||||
"dst": ["autogroup:member"],
|
||||
"users": ["autogroup:nonroot"]
|
||||
}]
|
||||
}
|
||||
`,
|
||||
wantErr: "tags in SSH source cannot access autogroup:member",
|
||||
},
|
||||
{
|
||||
name: "ssh-autogroup-tagged-to-autogroup-tagged-allowed",
|
||||
input: `
|
||||
{
|
||||
"ssh": [{
|
||||
"action": "accept",
|
||||
"src": ["autogroup:tagged"],
|
||||
"dst": ["autogroup:tagged"],
|
||||
"users": ["autogroup:nonroot"]
|
||||
}]
|
||||
}
|
||||
`,
|
||||
want: &Policy{
|
||||
SSHs: []SSH{
|
||||
{
|
||||
Action: "accept",
|
||||
Sources: SSHSrcAliases{agp("autogroup:tagged")},
|
||||
Destinations: SSHDstAliases{agp("autogroup:tagged")},
|
||||
Users: []SSHUser{SSHUser(AutoGroupNonRoot)},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ssh-wildcard-destination-rejected",
|
||||
input: `
|
||||
{
|
||||
"groups": {"group:admins": ["admin@"]},
|
||||
"ssh": [{
|
||||
"action": "accept",
|
||||
"src": ["group:admins"],
|
||||
"dst": ["*"],
|
||||
"users": ["autogroup:nonroot"]
|
||||
}]
|
||||
}
|
||||
`,
|
||||
wantErr: "wildcard (*) is not supported as SSH destination",
|
||||
},
|
||||
{
|
||||
name: "ssh-group-to-tag-allowed",
|
||||
input: `
|
||||
{
|
||||
"tagOwners": {"tag:server": ["admin@"]},
|
||||
"groups": {"group:admins": ["admin@"]},
|
||||
"ssh": [{
|
||||
"action": "accept",
|
||||
"src": ["group:admins"],
|
||||
"dst": ["tag:server"],
|
||||
"users": ["autogroup:nonroot"]
|
||||
}]
|
||||
}
|
||||
`,
|
||||
want: &Policy{
|
||||
TagOwners: TagOwners{
|
||||
Tag("tag:server"): Owners{up("admin@")},
|
||||
},
|
||||
Groups: Groups{
|
||||
Group("group:admins"): []Username{Username("admin@")},
|
||||
},
|
||||
SSHs: []SSH{
|
||||
{
|
||||
Action: "accept",
|
||||
Sources: SSHSrcAliases{gp("group:admins")},
|
||||
Destinations: SSHDstAliases{tp("tag:server")},
|
||||
Users: []SSHUser{SSHUser(AutoGroupNonRoot)},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ssh-user-to-tag-allowed",
|
||||
input: `
|
||||
{
|
||||
"tagOwners": {"tag:server": ["admin@"]},
|
||||
"ssh": [{
|
||||
"action": "accept",
|
||||
"src": ["admin@"],
|
||||
"dst": ["tag:server"],
|
||||
"users": ["autogroup:nonroot"]
|
||||
}]
|
||||
}
|
||||
`,
|
||||
want: &Policy{
|
||||
TagOwners: TagOwners{
|
||||
Tag("tag:server"): Owners{up("admin@")},
|
||||
},
|
||||
SSHs: []SSH{
|
||||
{
|
||||
Action: "accept",
|
||||
Sources: SSHSrcAliases{up("admin@")},
|
||||
Destinations: SSHDstAliases{tp("tag:server")},
|
||||
Users: []SSHUser{SSHUser(AutoGroupNonRoot)},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ssh-autogroup-member-to-autogroup-tagged-allowed",
|
||||
input: `
|
||||
{
|
||||
"ssh": [{
|
||||
"action": "accept",
|
||||
"src": ["autogroup:member"],
|
||||
"dst": ["autogroup:tagged"],
|
||||
"users": ["autogroup:nonroot"]
|
||||
}]
|
||||
}
|
||||
`,
|
||||
want: &Policy{
|
||||
SSHs: []SSH{
|
||||
{
|
||||
Action: "accept",
|
||||
Sources: SSHSrcAliases{agp("autogroup:member")},
|
||||
Destinations: SSHDstAliases{agp("autogroup:tagged")},
|
||||
Users: []SSHUser{SSHUser(AutoGroupNonRoot)},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
cmps := append(util.Comparers,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue