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:
Kristoffer Dalby 2026-01-21 12:14:56 +00:00
parent 5688c201e9
commit d40203e153
3 changed files with 407 additions and 77 deletions

View file

@ -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"]
}
]

View file

@ -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)

View file

@ -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,