mirror of
https://github.com/juanfont/headscale.git
synced 2026-11-04 04:34:07 +09:00
policy/v2, servertest: add group source split across rules captures
SaaS judges a tests or sshTests group source by one rule naming the group: per-member rules fail a true accept and pass a false deny. Headscale checks each member through its own rules.
This commit is contained in:
1 parent
06260d6fad
commit
5de5399cee
7 files changed
+766
No files matched your search
@@ -43,6 +43,8 @@ var knownPolicyTesterDivergences = map[string]string{ //nolint:gosec // strings
|
||||
"policytest-dst-unknown-group/breakdown": testUnknownAlias,
|
||||
"policytest-src-unknown-group/breakdown": testUnknownAlias,
|
||||
"policytest-src-unknown-user-email/breakdown": testUnknownAlias,
|
||||
|
||||
"policytest-src-split-across-rules/verdict": "TEST_SRC_ONE_RULE: SaaS needs one rule to cover a group source; headscale lets each member pass through its own rule",
|
||||
}
|
||||
|
||||
const (
|
||||
|
||||
@@ -16,6 +16,9 @@ import (
|
||||
// headscale and upstream disagree, keyed <test_id>/<check>.
|
||||
var knownSSHTesterDivergences = map[string]string{
|
||||
"sshtest-malformed-dst-bare-ipv6/verdict": "bare-IPv6 sshTests dst: upstream parse-accepts then engine-rejects; headscale accepts (IPv4 mirror passes both sides)",
|
||||
|
||||
"sshtest-group-split-actions-accept/verdict": "SSHTEST_SRC_ONE_RULE: SaaS needs one accept rule to cover a group source; headscale lets each member pass through its own accept or check rule",
|
||||
"sshtest-group-split-actions-deny/verdict": "SSHTEST_SRC_ONE_RULE: SaaS denies a group source no single rule covers; headscale fails the deny because each member is let in",
|
||||
}
|
||||
|
||||
func TestSSHTesterCompat(t *testing.T) {
|
||||
|
||||
+188
@@ -0,0 +1,188 @@
|
||||
// policytest-src-split-across-rules
|
||||
//
|
||||
// tests src split across rules
|
||||
//
|
||||
// Steps: 1
|
||||
// Captured at: 2026-10-17T15:35:13Z
|
||||
// tool version: tscap/e3511f1
|
||||
// schema version: 1
|
||||
{
|
||||
"schema_version": 1,
|
||||
"test_id": "policytest-src-split-across-rules",
|
||||
"description": "tests src split across rules",
|
||||
"category": "policytest",
|
||||
"captured_at": "2026-10-17T15:35:13.947754671Z",
|
||||
"tool_version": "tscap/e3511f1",
|
||||
"tailnet": "odin@example.com",
|
||||
"error": true,
|
||||
"input": {
|
||||
"api_response_code": 400,
|
||||
"api_response_body": {"message": "test(s) failed", "data": [{"user": "group:g", "errors": [
|
||||
"[acl test error]: address \"tag:server:22\" (protocol \"tcp\"): want: Accept, got: Drop"
|
||||
]}]},
|
||||
"tailnet": {
|
||||
"dns": {"magic_dns": false, "nameservers": [], "search_paths": [], "split_dns": {}},
|
||||
"settings": {}
|
||||
},
|
||||
"scenario_hujson": " \n \n \n{\n\t\"id\": \"policytest-src-split-across-rules\",\n\t\"category\": \"policytest\",\n\t\"description\": \"tests src split across rules\",\n\t\"topology\": \"../../_topologies/grant.hujson\",\n\t\"tailnet\": {\n\t\t\"dns\": {\"magic_dns\": false, \"nameservers\": [], \"search_paths\": [], \"split_dns\": {}},\n\t\t\"settings\": {}\n\t},\n\t\"options\": {\"capture\": \"none\"},\n\t\"policy\": {\n\t\t\"groups\": {\"group:g\": [\"thor@example.org\", \"odin@example.com\"]},\n\t\t\"tagOwners\": {\"tag:server\": [\"odin@example.com\"]},\n\t\t\"acls\": [{\n\t\t\t\"action\": \"accept\",\n\t\t\t\"src\": [\"thor@example.org\"],\n\t\t\t\"dst\": [\"tag:server:22\"]\n\t\t}, {\n\t\t\t\"action\": \"accept\",\n\t\t\t\"src\": [\"odin@example.com\"],\n\t\t\t\"dst\": [\"tag:server:22\"]\n\t\t}],\n\t\t\"tests\": [{\"src\": \"group:g\", \"accept\": [\"tag:server:22\"]}]\n\t}\n}\n",
|
||||
"scenario_path": "scenarios/grant-matrix/grant/policytest-src-split-across-rules.hujson",
|
||||
"full_policy": {
|
||||
"acls": [{
|
||||
"action": "accept",
|
||||
"dst": ["tag:server:22"],
|
||||
"src": ["thor@example.org"]
|
||||
}, {
|
||||
"action": "accept",
|
||||
"dst": ["tag:server:22"],
|
||||
"src": ["odin@example.com"]
|
||||
}],
|
||||
"groups": {"group:g": ["thor@example.org", "odin@example.com"]},
|
||||
"tagOwners": {"tag:server": ["odin@example.com"]},
|
||||
"tests": [{"accept": ["tag:server:22"], "src": "group:g"}]
|
||||
}
|
||||
},
|
||||
"topology": {"users": [
|
||||
{"id": 1, "name": "odin", "email": "odin@example.com"},
|
||||
{"id": 2, "name": "thor", "email": "thor@example.org"},
|
||||
{"id": 3, "name": "freya", "email": "freya@example.com"}
|
||||
], "nodes": {"beedrill": {
|
||||
"hostname": "beedrill",
|
||||
"tags": ["tag:server"],
|
||||
"ipv4": "100.64.0.16",
|
||||
"ipv6": "fd7a:115c:a1e0::10",
|
||||
"routable_ips": [],
|
||||
"approved_routes": []
|
||||
}, "blastoise": {
|
||||
"hostname": "blastoise",
|
||||
"tags": ["tag:exit", "tag:router"],
|
||||
"ipv4": "100.64.0.9",
|
||||
"ipv6": "fd7a:115c:a1e0::9",
|
||||
"routable_ips": ["10.33.0.0/16", "0.0.0.0/0", "::/0"],
|
||||
"approved_routes": []
|
||||
}, "bulbasaur": {
|
||||
"hostname": "bulbasaur",
|
||||
"tags": [],
|
||||
"ipv4": "100.64.0.19",
|
||||
"ipv6": "fd7a:115c:a1e0::13",
|
||||
"user": "odin",
|
||||
"routable_ips": [],
|
||||
"approved_routes": []
|
||||
}, "charmander": {
|
||||
"hostname": "charmander",
|
||||
"tags": ["tag:exit"],
|
||||
"ipv4": "100.64.0.4",
|
||||
"ipv6": "fd7a:115c:a1e0::4",
|
||||
"routable_ips": ["0.0.0.0/0", "::/0"],
|
||||
"approved_routes": []
|
||||
}, "fearow": {
|
||||
"hostname": "fearow",
|
||||
"tags": ["tag:fearow"],
|
||||
"ipv4": "100.64.0.12",
|
||||
"ipv6": "fd7a:115c:a1e0::c",
|
||||
"routable_ips": ["10.55.0.0/16"],
|
||||
"approved_routes": []
|
||||
}, "ivysaur": {
|
||||
"hostname": "ivysaur",
|
||||
"tags": [],
|
||||
"ipv4": "100.64.0.17",
|
||||
"ipv6": "fd7a:115c:a1e0::11",
|
||||
"user": "thor",
|
||||
"routable_ips": [],
|
||||
"approved_routes": []
|
||||
}, "kakuna": {
|
||||
"hostname": "kakuna",
|
||||
"tags": ["tag:prod"],
|
||||
"ipv4": "100.64.0.15",
|
||||
"ipv6": "fd7a:115c:a1e0::f",
|
||||
"routable_ips": [],
|
||||
"approved_routes": []
|
||||
}, "pidgeotto": {
|
||||
"hostname": "pidgeotto",
|
||||
"tags": ["tag:pidgeotto"],
|
||||
"ipv4": "100.64.0.3",
|
||||
"ipv6": "fd7a:115c:a1e0::3",
|
||||
"routable_ips": ["0.0.0.0/0", "::/0"],
|
||||
"approved_routes": []
|
||||
}, "pidgey": {
|
||||
"hostname": "pidgey",
|
||||
"tags": ["tag:pidgey"],
|
||||
"ipv4": "100.64.0.2",
|
||||
"ipv6": "fd7a:115c:a1e0::2",
|
||||
"routable_ips": ["0.0.0.0/0", "::/0"],
|
||||
"approved_routes": []
|
||||
}, "raticate": {
|
||||
"hostname": "raticate",
|
||||
"tags": ["tag:group-b"],
|
||||
"ipv4": "100.64.0.6",
|
||||
"ipv6": "fd7a:115c:a1e0::6",
|
||||
"routable_ips": [],
|
||||
"approved_routes": []
|
||||
}, "rattata": {
|
||||
"hostname": "rattata",
|
||||
"tags": ["tag:group-a"],
|
||||
"ipv4": "100.64.0.5",
|
||||
"ipv6": "fd7a:115c:a1e0::5",
|
||||
"routable_ips": [],
|
||||
"approved_routes": []
|
||||
}, "spearow": {
|
||||
"hostname": "spearow",
|
||||
"tags": ["tag:spearow"],
|
||||
"ipv4": "100.64.0.11",
|
||||
"ipv6": "fd7a:115c:a1e0::b",
|
||||
"routable_ips": ["10.44.0.0/16"],
|
||||
"approved_routes": []
|
||||
}, "squirtle": {
|
||||
"hostname": "squirtle",
|
||||
"tags": ["tag:router"],
|
||||
"ipv4": "100.64.0.13",
|
||||
"ipv6": "fd7a:115c:a1e0::d",
|
||||
"routable_ips": ["10.33.0.0/16"],
|
||||
"approved_routes": []
|
||||
}, "venusaur": {
|
||||
"hostname": "venusaur",
|
||||
"tags": [],
|
||||
"ipv4": "100.64.0.18",
|
||||
"ipv6": "fd7a:115c:a1e0::12",
|
||||
"user": "freya",
|
||||
"routable_ips": [],
|
||||
"approved_routes": []
|
||||
}, "weedle": {
|
||||
"hostname": "weedle",
|
||||
"tags": ["tag:client"],
|
||||
"ipv4": "100.64.0.14",
|
||||
"ipv6": "fd7a:115c:a1e0::e",
|
||||
"routable_ips": [],
|
||||
"approved_routes": []
|
||||
}}},
|
||||
"captures": {},
|
||||
"steps": [{
|
||||
"name": "validate",
|
||||
"at": "2026-10-17T15:35:19.779715243Z",
|
||||
"result": "observed",
|
||||
"actions": [{
|
||||
"kind": "api",
|
||||
"principal": "admin",
|
||||
"method": "POST",
|
||||
"path": "/tailnet/-/acl/validate",
|
||||
"header": {"Content-Type": "application/hujson"},
|
||||
"body": {
|
||||
"acls": [{
|
||||
"action": "accept",
|
||||
"dst": ["tag:server:22"],
|
||||
"src": ["thor@example.org"]
|
||||
}, {
|
||||
"action": "accept",
|
||||
"dst": ["tag:server:22"],
|
||||
"src": ["odin@example.com"]
|
||||
}],
|
||||
"groups": {"group:g": ["thor@example.org", "odin@example.com"]},
|
||||
"tagOwners": {"tag:server": ["odin@example.com"]},
|
||||
"tests": [{"accept": ["tag:server:22"], "src": "group:g"}]
|
||||
},
|
||||
"status": 200,
|
||||
"response": {"data": [{"errors": [
|
||||
"[acl test error]: address \"tag:server:22\" (protocol \"tcp\"): want: Accept, got: Drop"
|
||||
], "user": "group:g"}], "message": "test(s) failed"}
|
||||
}]
|
||||
}]
|
||||
}
|
||||
+192
@@ -0,0 +1,192 @@
|
||||
// sshtest-group-split-actions-accept
|
||||
//
|
||||
// sshTests group across accept and check rules, accept
|
||||
//
|
||||
// Steps: 1
|
||||
// Captured at: 2026-10-17T15:38:51Z
|
||||
// tool version: tscap/e3511f1
|
||||
// schema version: 1
|
||||
{
|
||||
"schema_version": 1,
|
||||
"test_id": "sshtest-group-split-actions-accept",
|
||||
"description": "sshTests group across accept and check rules, accept",
|
||||
"category": "sshtest",
|
||||
"captured_at": "2026-10-17T15:38:51.396983195Z",
|
||||
"tool_version": "tscap/e3511f1",
|
||||
"tailnet": "odin@example.com",
|
||||
"error": true,
|
||||
"input": {
|
||||
"api_response_code": 400,
|
||||
"api_response_body": {"message": "test(s) failed", "data": [{"user": "group:h", "errors": [
|
||||
"[ssh test error]: src \"group:h\" at \"100.64.0.0\", dst \"tag:server\" at \"100.64.0.1\", ssh user \"root\": want: \"accept\" got: \"deny\""
|
||||
]}]},
|
||||
"tailnet": {
|
||||
"dns": {"magic_dns": false, "nameservers": [], "search_paths": [], "split_dns": {}},
|
||||
"settings": {}
|
||||
},
|
||||
"scenario_hujson": " \n \n \n \n{\n\t\"id\": \"sshtest-group-split-actions-accept\",\n\t\"category\": \"sshtest\",\n\t\"description\": \"sshTests group across accept and check rules, accept\",\n\t\"topology\": \"../../_topologies/grant.hujson\",\n\t\"tailnet\": {\n\t\t\"dns\": {\"magic_dns\": false, \"nameservers\": [], \"search_paths\": [], \"split_dns\": {}},\n\t\t\"settings\": {}\n\t},\n\t\"options\": {\"capture\": \"none\"},\n\t\"policy\": {\n\t\t\"groups\": {\"group:h\": [\"thor@example.org\", \"freya@example.com\"]},\n\t\t\"tagOwners\": {\"tag:server\": [\"odin@example.com\"]},\n\t\t\"ssh\": [{\n\t\t\t\"action\": \"accept\",\n\t\t\t\"src\": [\"thor@example.org\"],\n\t\t\t\"dst\": [\"tag:server\"],\n\t\t\t\"users\": [\"root\"]\n\t\t}, {\n\t\t\t\"action\": \"check\",\n\t\t\t\"src\": [\"freya@example.com\"],\n\t\t\t\"dst\": [\"tag:server\"],\n\t\t\t\"users\": [\"root\"]\n\t\t}],\n\t\t\"sshTests\": [{\"src\": \"group:h\", \"dst\": [\"tag:server\"], \"accept\": [\"root\"]}]\n\t}\n}\n",
|
||||
"scenario_path": "scenarios/grant-matrix/grant/sshtest-group-split-actions-accept.hujson",
|
||||
"full_policy": {
|
||||
"groups": {"group:h": ["thor@example.org", "freya@example.com"]},
|
||||
"ssh": [{
|
||||
"action": "accept",
|
||||
"dst": ["tag:server"],
|
||||
"src": ["thor@example.org"],
|
||||
"users": ["root"]
|
||||
}, {
|
||||
"action": "check",
|
||||
"dst": ["tag:server"],
|
||||
"src": ["freya@example.com"],
|
||||
"users": ["root"]
|
||||
}],
|
||||
"sshTests": [{"accept": ["root"], "dst": ["tag:server"], "src": "group:h"}],
|
||||
"tagOwners": {"tag:server": ["odin@example.com"]}
|
||||
}
|
||||
},
|
||||
"topology": {"users": [
|
||||
{"id": 1, "name": "odin", "email": "odin@example.com"},
|
||||
{"id": 2, "name": "thor", "email": "thor@example.org"},
|
||||
{"id": 3, "name": "freya", "email": "freya@example.com"}
|
||||
], "nodes": {"beedrill": {
|
||||
"hostname": "beedrill",
|
||||
"tags": ["tag:server"],
|
||||
"ipv4": "100.64.0.16",
|
||||
"ipv6": "fd7a:115c:a1e0::10",
|
||||
"routable_ips": [],
|
||||
"approved_routes": []
|
||||
}, "blastoise": {
|
||||
"hostname": "blastoise",
|
||||
"tags": ["tag:exit", "tag:router"],
|
||||
"ipv4": "100.64.0.9",
|
||||
"ipv6": "fd7a:115c:a1e0::9",
|
||||
"routable_ips": ["10.33.0.0/16", "0.0.0.0/0", "::/0"],
|
||||
"approved_routes": []
|
||||
}, "bulbasaur": {
|
||||
"hostname": "bulbasaur",
|
||||
"tags": [],
|
||||
"ipv4": "100.64.0.19",
|
||||
"ipv6": "fd7a:115c:a1e0::13",
|
||||
"user": "odin",
|
||||
"routable_ips": [],
|
||||
"approved_routes": []
|
||||
}, "charmander": {
|
||||
"hostname": "charmander",
|
||||
"tags": ["tag:exit"],
|
||||
"ipv4": "100.64.0.4",
|
||||
"ipv6": "fd7a:115c:a1e0::4",
|
||||
"routable_ips": ["0.0.0.0/0", "::/0"],
|
||||
"approved_routes": []
|
||||
}, "fearow": {
|
||||
"hostname": "fearow",
|
||||
"tags": ["tag:fearow"],
|
||||
"ipv4": "100.64.0.12",
|
||||
"ipv6": "fd7a:115c:a1e0::c",
|
||||
"routable_ips": ["10.55.0.0/16"],
|
||||
"approved_routes": []
|
||||
}, "ivysaur": {
|
||||
"hostname": "ivysaur",
|
||||
"tags": [],
|
||||
"ipv4": "100.64.0.17",
|
||||
"ipv6": "fd7a:115c:a1e0::11",
|
||||
"user": "thor",
|
||||
"routable_ips": [],
|
||||
"approved_routes": []
|
||||
}, "kakuna": {
|
||||
"hostname": "kakuna",
|
||||
"tags": ["tag:prod"],
|
||||
"ipv4": "100.64.0.15",
|
||||
"ipv6": "fd7a:115c:a1e0::f",
|
||||
"routable_ips": [],
|
||||
"approved_routes": []
|
||||
}, "pidgeotto": {
|
||||
"hostname": "pidgeotto",
|
||||
"tags": ["tag:pidgeotto"],
|
||||
"ipv4": "100.64.0.3",
|
||||
"ipv6": "fd7a:115c:a1e0::3",
|
||||
"routable_ips": ["0.0.0.0/0", "::/0"],
|
||||
"approved_routes": []
|
||||
}, "pidgey": {
|
||||
"hostname": "pidgey",
|
||||
"tags": ["tag:pidgey"],
|
||||
"ipv4": "100.64.0.2",
|
||||
"ipv6": "fd7a:115c:a1e0::2",
|
||||
"routable_ips": ["0.0.0.0/0", "::/0"],
|
||||
"approved_routes": []
|
||||
}, "raticate": {
|
||||
"hostname": "raticate",
|
||||
"tags": ["tag:group-b"],
|
||||
"ipv4": "100.64.0.6",
|
||||
"ipv6": "fd7a:115c:a1e0::6",
|
||||
"routable_ips": [],
|
||||
"approved_routes": []
|
||||
}, "rattata": {
|
||||
"hostname": "rattata",
|
||||
"tags": ["tag:group-a"],
|
||||
"ipv4": "100.64.0.5",
|
||||
"ipv6": "fd7a:115c:a1e0::5",
|
||||
"routable_ips": [],
|
||||
"approved_routes": []
|
||||
}, "spearow": {
|
||||
"hostname": "spearow",
|
||||
"tags": ["tag:spearow"],
|
||||
"ipv4": "100.64.0.11",
|
||||
"ipv6": "fd7a:115c:a1e0::b",
|
||||
"routable_ips": ["10.44.0.0/16"],
|
||||
"approved_routes": []
|
||||
}, "squirtle": {
|
||||
"hostname": "squirtle",
|
||||
"tags": ["tag:router"],
|
||||
"ipv4": "100.64.0.13",
|
||||
"ipv6": "fd7a:115c:a1e0::d",
|
||||
"routable_ips": ["10.33.0.0/16"],
|
||||
"approved_routes": []
|
||||
}, "venusaur": {
|
||||
"hostname": "venusaur",
|
||||
"tags": [],
|
||||
"ipv4": "100.64.0.18",
|
||||
"ipv6": "fd7a:115c:a1e0::12",
|
||||
"user": "freya",
|
||||
"routable_ips": [],
|
||||
"approved_routes": []
|
||||
}, "weedle": {
|
||||
"hostname": "weedle",
|
||||
"tags": ["tag:client"],
|
||||
"ipv4": "100.64.0.14",
|
||||
"ipv6": "fd7a:115c:a1e0::e",
|
||||
"routable_ips": [],
|
||||
"approved_routes": []
|
||||
}}},
|
||||
"captures": {},
|
||||
"steps": [{
|
||||
"name": "validate",
|
||||
"at": "2026-10-17T15:38:54.859552188Z",
|
||||
"result": "observed",
|
||||
"actions": [{
|
||||
"kind": "api",
|
||||
"principal": "admin",
|
||||
"method": "POST",
|
||||
"path": "/tailnet/-/acl/validate",
|
||||
"header": {"Content-Type": "application/hujson"},
|
||||
"body": {
|
||||
"groups": {"group:h": ["thor@example.org", "freya@example.com"]},
|
||||
"ssh": [{
|
||||
"action": "accept",
|
||||
"dst": ["tag:server"],
|
||||
"src": ["thor@example.org"],
|
||||
"users": ["root"]
|
||||
}, {
|
||||
"action": "check",
|
||||
"dst": ["tag:server"],
|
||||
"src": ["freya@example.com"],
|
||||
"users": ["root"]
|
||||
}],
|
||||
"sshTests": [{"accept": ["root"], "dst": ["tag:server"], "src": "group:h"}],
|
||||
"tagOwners": {"tag:server": ["odin@example.com"]}
|
||||
},
|
||||
"status": 200,
|
||||
"response": {"data": [{"errors": [
|
||||
"[ssh test error]: src \"group:h\" at \"100.64.0.0\", dst \"tag:server\" at \"100.64.0.1\", ssh user \"root\": want: \"accept\" got: \"deny\""
|
||||
], "user": "group:h"}], "message": "test(s) failed"}
|
||||
}]
|
||||
}]
|
||||
}
|
||||
+192
@@ -0,0 +1,192 @@
|
||||
// sshtest-group-split-actions-check
|
||||
//
|
||||
// sshTests group across accept and check rules, check
|
||||
//
|
||||
// Steps: 1
|
||||
// Captured at: 2026-10-17T15:38:58Z
|
||||
// tool version: tscap/e3511f1
|
||||
// schema version: 1
|
||||
{
|
||||
"schema_version": 1,
|
||||
"test_id": "sshtest-group-split-actions-check",
|
||||
"description": "sshTests group across accept and check rules, check",
|
||||
"category": "sshtest",
|
||||
"captured_at": "2026-10-17T15:38:58.89973761Z",
|
||||
"tool_version": "tscap/e3511f1",
|
||||
"tailnet": "odin@example.com",
|
||||
"error": true,
|
||||
"input": {
|
||||
"api_response_code": 400,
|
||||
"api_response_body": {"message": "test(s) failed", "data": [{"user": "group:h", "errors": [
|
||||
"[ssh test error]: src \"group:h\" at \"100.64.0.0\", dst \"tag:server\" at \"100.64.0.1\", ssh user \"root\": want: \"check\" got: \"deny\""
|
||||
]}]},
|
||||
"tailnet": {
|
||||
"dns": {"magic_dns": false, "nameservers": [], "search_paths": [], "split_dns": {}},
|
||||
"settings": {}
|
||||
},
|
||||
"scenario_hujson": " \n \n \n \n{\n\t\"id\": \"sshtest-group-split-actions-check\",\n\t\"category\": \"sshtest\",\n\t\"description\": \"sshTests group across accept and check rules, check\",\n\t\"topology\": \"../../_topologies/grant.hujson\",\n\t\"tailnet\": {\n\t\t\"dns\": {\"magic_dns\": false, \"nameservers\": [], \"search_paths\": [], \"split_dns\": {}},\n\t\t\"settings\": {}\n\t},\n\t\"options\": {\"capture\": \"none\"},\n\t\"policy\": {\n\t\t\"groups\": {\"group:h\": [\"thor@example.org\", \"freya@example.com\"]},\n\t\t\"tagOwners\": {\"tag:server\": [\"odin@example.com\"]},\n\t\t\"ssh\": [{\n\t\t\t\"action\": \"accept\",\n\t\t\t\"src\": [\"thor@example.org\"],\n\t\t\t\"dst\": [\"tag:server\"],\n\t\t\t\"users\": [\"root\"]\n\t\t}, {\n\t\t\t\"action\": \"check\",\n\t\t\t\"src\": [\"freya@example.com\"],\n\t\t\t\"dst\": [\"tag:server\"],\n\t\t\t\"users\": [\"root\"]\n\t\t}],\n\t\t\"sshTests\": [{\"src\": \"group:h\", \"dst\": [\"tag:server\"], \"check\": [\"root\"]}]\n\t}\n}\n",
|
||||
"scenario_path": "scenarios/grant-matrix/grant/sshtest-group-split-actions-check.hujson",
|
||||
"full_policy": {
|
||||
"groups": {"group:h": ["thor@example.org", "freya@example.com"]},
|
||||
"ssh": [{
|
||||
"action": "accept",
|
||||
"dst": ["tag:server"],
|
||||
"src": ["thor@example.org"],
|
||||
"users": ["root"]
|
||||
}, {
|
||||
"action": "check",
|
||||
"dst": ["tag:server"],
|
||||
"src": ["freya@example.com"],
|
||||
"users": ["root"]
|
||||
}],
|
||||
"sshTests": [{"check": ["root"], "dst": ["tag:server"], "src": "group:h"}],
|
||||
"tagOwners": {"tag:server": ["odin@example.com"]}
|
||||
}
|
||||
},
|
||||
"topology": {"users": [
|
||||
{"id": 1, "name": "odin", "email": "odin@example.com"},
|
||||
{"id": 2, "name": "thor", "email": "thor@example.org"},
|
||||
{"id": 3, "name": "freya", "email": "freya@example.com"}
|
||||
], "nodes": {"beedrill": {
|
||||
"hostname": "beedrill",
|
||||
"tags": ["tag:server"],
|
||||
"ipv4": "100.64.0.16",
|
||||
"ipv6": "fd7a:115c:a1e0::10",
|
||||
"routable_ips": [],
|
||||
"approved_routes": []
|
||||
}, "blastoise": {
|
||||
"hostname": "blastoise",
|
||||
"tags": ["tag:exit", "tag:router"],
|
||||
"ipv4": "100.64.0.9",
|
||||
"ipv6": "fd7a:115c:a1e0::9",
|
||||
"routable_ips": ["10.33.0.0/16", "0.0.0.0/0", "::/0"],
|
||||
"approved_routes": []
|
||||
}, "bulbasaur": {
|
||||
"hostname": "bulbasaur",
|
||||
"tags": [],
|
||||
"ipv4": "100.64.0.19",
|
||||
"ipv6": "fd7a:115c:a1e0::13",
|
||||
"user": "odin",
|
||||
"routable_ips": [],
|
||||
"approved_routes": []
|
||||
}, "charmander": {
|
||||
"hostname": "charmander",
|
||||
"tags": ["tag:exit"],
|
||||
"ipv4": "100.64.0.4",
|
||||
"ipv6": "fd7a:115c:a1e0::4",
|
||||
"routable_ips": ["0.0.0.0/0", "::/0"],
|
||||
"approved_routes": []
|
||||
}, "fearow": {
|
||||
"hostname": "fearow",
|
||||
"tags": ["tag:fearow"],
|
||||
"ipv4": "100.64.0.12",
|
||||
"ipv6": "fd7a:115c:a1e0::c",
|
||||
"routable_ips": ["10.55.0.0/16"],
|
||||
"approved_routes": []
|
||||
}, "ivysaur": {
|
||||
"hostname": "ivysaur",
|
||||
"tags": [],
|
||||
"ipv4": "100.64.0.17",
|
||||
"ipv6": "fd7a:115c:a1e0::11",
|
||||
"user": "thor",
|
||||
"routable_ips": [],
|
||||
"approved_routes": []
|
||||
}, "kakuna": {
|
||||
"hostname": "kakuna",
|
||||
"tags": ["tag:prod"],
|
||||
"ipv4": "100.64.0.15",
|
||||
"ipv6": "fd7a:115c:a1e0::f",
|
||||
"routable_ips": [],
|
||||
"approved_routes": []
|
||||
}, "pidgeotto": {
|
||||
"hostname": "pidgeotto",
|
||||
"tags": ["tag:pidgeotto"],
|
||||
"ipv4": "100.64.0.3",
|
||||
"ipv6": "fd7a:115c:a1e0::3",
|
||||
"routable_ips": ["0.0.0.0/0", "::/0"],
|
||||
"approved_routes": []
|
||||
}, "pidgey": {
|
||||
"hostname": "pidgey",
|
||||
"tags": ["tag:pidgey"],
|
||||
"ipv4": "100.64.0.2",
|
||||
"ipv6": "fd7a:115c:a1e0::2",
|
||||
"routable_ips": ["0.0.0.0/0", "::/0"],
|
||||
"approved_routes": []
|
||||
}, "raticate": {
|
||||
"hostname": "raticate",
|
||||
"tags": ["tag:group-b"],
|
||||
"ipv4": "100.64.0.6",
|
||||
"ipv6": "fd7a:115c:a1e0::6",
|
||||
"routable_ips": [],
|
||||
"approved_routes": []
|
||||
}, "rattata": {
|
||||
"hostname": "rattata",
|
||||
"tags": ["tag:group-a"],
|
||||
"ipv4": "100.64.0.5",
|
||||
"ipv6": "fd7a:115c:a1e0::5",
|
||||
"routable_ips": [],
|
||||
"approved_routes": []
|
||||
}, "spearow": {
|
||||
"hostname": "spearow",
|
||||
"tags": ["tag:spearow"],
|
||||
"ipv4": "100.64.0.11",
|
||||
"ipv6": "fd7a:115c:a1e0::b",
|
||||
"routable_ips": ["10.44.0.0/16"],
|
||||
"approved_routes": []
|
||||
}, "squirtle": {
|
||||
"hostname": "squirtle",
|
||||
"tags": ["tag:router"],
|
||||
"ipv4": "100.64.0.13",
|
||||
"ipv6": "fd7a:115c:a1e0::d",
|
||||
"routable_ips": ["10.33.0.0/16"],
|
||||
"approved_routes": []
|
||||
}, "venusaur": {
|
||||
"hostname": "venusaur",
|
||||
"tags": [],
|
||||
"ipv4": "100.64.0.18",
|
||||
"ipv6": "fd7a:115c:a1e0::12",
|
||||
"user": "freya",
|
||||
"routable_ips": [],
|
||||
"approved_routes": []
|
||||
}, "weedle": {
|
||||
"hostname": "weedle",
|
||||
"tags": ["tag:client"],
|
||||
"ipv4": "100.64.0.14",
|
||||
"ipv6": "fd7a:115c:a1e0::e",
|
||||
"routable_ips": [],
|
||||
"approved_routes": []
|
||||
}}},
|
||||
"captures": {},
|
||||
"steps": [{
|
||||
"name": "validate",
|
||||
"at": "2026-10-17T15:39:02.819824553Z",
|
||||
"result": "observed",
|
||||
"actions": [{
|
||||
"kind": "api",
|
||||
"principal": "admin",
|
||||
"method": "POST",
|
||||
"path": "/tailnet/-/acl/validate",
|
||||
"header": {"Content-Type": "application/hujson"},
|
||||
"body": {
|
||||
"groups": {"group:h": ["thor@example.org", "freya@example.com"]},
|
||||
"ssh": [{
|
||||
"action": "accept",
|
||||
"dst": ["tag:server"],
|
||||
"src": ["thor@example.org"],
|
||||
"users": ["root"]
|
||||
}, {
|
||||
"action": "check",
|
||||
"dst": ["tag:server"],
|
||||
"src": ["freya@example.com"],
|
||||
"users": ["root"]
|
||||
}],
|
||||
"sshTests": [{"check": ["root"], "dst": ["tag:server"], "src": "group:h"}],
|
||||
"tagOwners": {"tag:server": ["odin@example.com"]}
|
||||
},
|
||||
"status": 200,
|
||||
"response": {"data": [{"errors": [
|
||||
"[ssh test error]: src \"group:h\" at \"100.64.0.0\", dst \"tag:server\" at \"100.64.0.1\", ssh user \"root\": want: \"check\" got: \"deny\""
|
||||
], "user": "group:h"}], "message": "test(s) failed"}
|
||||
}]
|
||||
}]
|
||||
}
|
||||
+186
@@ -0,0 +1,186 @@
|
||||
// sshtest-group-split-actions-deny
|
||||
//
|
||||
// sshTests group across accept and check rules, deny
|
||||
//
|
||||
// Steps: 1
|
||||
// Captured at: 2026-10-17T15:39:06Z
|
||||
// tool version: tscap/e3511f1
|
||||
// schema version: 1
|
||||
{
|
||||
"schema_version": 1,
|
||||
"test_id": "sshtest-group-split-actions-deny",
|
||||
"description": "sshTests group across accept and check rules, deny",
|
||||
"category": "sshtest",
|
||||
"captured_at": "2026-10-17T15:39:06.85864835Z",
|
||||
"tool_version": "tscap/e3511f1",
|
||||
"tailnet": "odin@example.com",
|
||||
"input": {
|
||||
"api_response_code": 200,
|
||||
"tailnet": {
|
||||
"dns": {"magic_dns": false, "nameservers": [], "search_paths": [], "split_dns": {}},
|
||||
"settings": {}
|
||||
},
|
||||
"scenario_hujson": " \n \n \n \n{\n\t\"id\": \"sshtest-group-split-actions-deny\",\n\t\"category\": \"sshtest\",\n\t\"description\": \"sshTests group across accept and check rules, deny\",\n\t\"topology\": \"../../_topologies/grant.hujson\",\n\t\"tailnet\": {\n\t\t\"dns\": {\"magic_dns\": false, \"nameservers\": [], \"search_paths\": [], \"split_dns\": {}},\n\t\t\"settings\": {}\n\t},\n\t\"options\": {\"capture\": \"none\"},\n\t\"policy\": {\n\t\t\"groups\": {\"group:h\": [\"thor@example.org\", \"freya@example.com\"]},\n\t\t\"tagOwners\": {\"tag:server\": [\"odin@example.com\"]},\n\t\t\"ssh\": [{\n\t\t\t\"action\": \"accept\",\n\t\t\t\"src\": [\"thor@example.org\"],\n\t\t\t\"dst\": [\"tag:server\"],\n\t\t\t\"users\": [\"root\"]\n\t\t}, {\n\t\t\t\"action\": \"check\",\n\t\t\t\"src\": [\"freya@example.com\"],\n\t\t\t\"dst\": [\"tag:server\"],\n\t\t\t\"users\": [\"root\"]\n\t\t}],\n\t\t\"sshTests\": [{\"src\": \"group:h\", \"dst\": [\"tag:server\"], \"deny\": [\"root\"]}]\n\t}\n}\n",
|
||||
"scenario_path": "scenarios/grant-matrix/grant/sshtest-group-split-actions-deny.hujson",
|
||||
"full_policy": {
|
||||
"groups": {"group:h": ["thor@example.org", "freya@example.com"]},
|
||||
"ssh": [{
|
||||
"action": "accept",
|
||||
"dst": ["tag:server"],
|
||||
"src": ["thor@example.org"],
|
||||
"users": ["root"]
|
||||
}, {
|
||||
"action": "check",
|
||||
"dst": ["tag:server"],
|
||||
"src": ["freya@example.com"],
|
||||
"users": ["root"]
|
||||
}],
|
||||
"sshTests": [{"deny": ["root"], "dst": ["tag:server"], "src": "group:h"}],
|
||||
"tagOwners": {"tag:server": ["odin@example.com"]}
|
||||
}
|
||||
},
|
||||
"topology": {"users": [
|
||||
{"id": 1, "name": "odin", "email": "odin@example.com"},
|
||||
{"id": 2, "name": "thor", "email": "thor@example.org"},
|
||||
{"id": 3, "name": "freya", "email": "freya@example.com"}
|
||||
], "nodes": {"beedrill": {
|
||||
"hostname": "beedrill",
|
||||
"tags": ["tag:server"],
|
||||
"ipv4": "100.64.0.16",
|
||||
"ipv6": "fd7a:115c:a1e0::10",
|
||||
"routable_ips": [],
|
||||
"approved_routes": []
|
||||
}, "blastoise": {
|
||||
"hostname": "blastoise",
|
||||
"tags": ["tag:exit", "tag:router"],
|
||||
"ipv4": "100.64.0.9",
|
||||
"ipv6": "fd7a:115c:a1e0::9",
|
||||
"routable_ips": ["10.33.0.0/16", "0.0.0.0/0", "::/0"],
|
||||
"approved_routes": []
|
||||
}, "bulbasaur": {
|
||||
"hostname": "bulbasaur",
|
||||
"tags": [],
|
||||
"ipv4": "100.64.0.19",
|
||||
"ipv6": "fd7a:115c:a1e0::13",
|
||||
"user": "odin",
|
||||
"routable_ips": [],
|
||||
"approved_routes": []
|
||||
}, "charmander": {
|
||||
"hostname": "charmander",
|
||||
"tags": ["tag:exit"],
|
||||
"ipv4": "100.64.0.4",
|
||||
"ipv6": "fd7a:115c:a1e0::4",
|
||||
"routable_ips": ["0.0.0.0/0", "::/0"],
|
||||
"approved_routes": []
|
||||
}, "fearow": {
|
||||
"hostname": "fearow",
|
||||
"tags": ["tag:fearow"],
|
||||
"ipv4": "100.64.0.12",
|
||||
"ipv6": "fd7a:115c:a1e0::c",
|
||||
"routable_ips": ["10.55.0.0/16"],
|
||||
"approved_routes": []
|
||||
}, "ivysaur": {
|
||||
"hostname": "ivysaur",
|
||||
"tags": [],
|
||||
"ipv4": "100.64.0.17",
|
||||
"ipv6": "fd7a:115c:a1e0::11",
|
||||
"user": "thor",
|
||||
"routable_ips": [],
|
||||
"approved_routes": []
|
||||
}, "kakuna": {
|
||||
"hostname": "kakuna",
|
||||
"tags": ["tag:prod"],
|
||||
"ipv4": "100.64.0.15",
|
||||
"ipv6": "fd7a:115c:a1e0::f",
|
||||
"routable_ips": [],
|
||||
"approved_routes": []
|
||||
}, "pidgeotto": {
|
||||
"hostname": "pidgeotto",
|
||||
"tags": ["tag:pidgeotto"],
|
||||
"ipv4": "100.64.0.3",
|
||||
"ipv6": "fd7a:115c:a1e0::3",
|
||||
"routable_ips": ["0.0.0.0/0", "::/0"],
|
||||
"approved_routes": []
|
||||
}, "pidgey": {
|
||||
"hostname": "pidgey",
|
||||
"tags": ["tag:pidgey"],
|
||||
"ipv4": "100.64.0.2",
|
||||
"ipv6": "fd7a:115c:a1e0::2",
|
||||
"routable_ips": ["0.0.0.0/0", "::/0"],
|
||||
"approved_routes": []
|
||||
}, "raticate": {
|
||||
"hostname": "raticate",
|
||||
"tags": ["tag:group-b"],
|
||||
"ipv4": "100.64.0.6",
|
||||
"ipv6": "fd7a:115c:a1e0::6",
|
||||
"routable_ips": [],
|
||||
"approved_routes": []
|
||||
}, "rattata": {
|
||||
"hostname": "rattata",
|
||||
"tags": ["tag:group-a"],
|
||||
"ipv4": "100.64.0.5",
|
||||
"ipv6": "fd7a:115c:a1e0::5",
|
||||
"routable_ips": [],
|
||||
"approved_routes": []
|
||||
}, "spearow": {
|
||||
"hostname": "spearow",
|
||||
"tags": ["tag:spearow"],
|
||||
"ipv4": "100.64.0.11",
|
||||
"ipv6": "fd7a:115c:a1e0::b",
|
||||
"routable_ips": ["10.44.0.0/16"],
|
||||
"approved_routes": []
|
||||
}, "squirtle": {
|
||||
"hostname": "squirtle",
|
||||
"tags": ["tag:router"],
|
||||
"ipv4": "100.64.0.13",
|
||||
"ipv6": "fd7a:115c:a1e0::d",
|
||||
"routable_ips": ["10.33.0.0/16"],
|
||||
"approved_routes": []
|
||||
}, "venusaur": {
|
||||
"hostname": "venusaur",
|
||||
"tags": [],
|
||||
"ipv4": "100.64.0.18",
|
||||
"ipv6": "fd7a:115c:a1e0::12",
|
||||
"user": "freya",
|
||||
"routable_ips": [],
|
||||
"approved_routes": []
|
||||
}, "weedle": {
|
||||
"hostname": "weedle",
|
||||
"tags": ["tag:client"],
|
||||
"ipv4": "100.64.0.14",
|
||||
"ipv6": "fd7a:115c:a1e0::e",
|
||||
"routable_ips": [],
|
||||
"approved_routes": []
|
||||
}}},
|
||||
"captures": {},
|
||||
"steps": [{
|
||||
"name": "validate",
|
||||
"at": "2026-10-17T15:39:12.817151014Z",
|
||||
"result": "observed",
|
||||
"actions": [{
|
||||
"kind": "api",
|
||||
"principal": "admin",
|
||||
"method": "POST",
|
||||
"path": "/tailnet/-/acl/validate",
|
||||
"header": {"Content-Type": "application/hujson"},
|
||||
"body": {
|
||||
"groups": {"group:h": ["thor@example.org", "freya@example.com"]},
|
||||
"ssh": [{
|
||||
"action": "accept",
|
||||
"dst": ["tag:server"],
|
||||
"src": ["thor@example.org"],
|
||||
"users": ["root"]
|
||||
}, {
|
||||
"action": "check",
|
||||
"dst": ["tag:server"],
|
||||
"src": ["freya@example.com"],
|
||||
"users": ["root"]
|
||||
}],
|
||||
"sshTests": [{"deny": ["root"], "dst": ["tag:server"], "src": "group:h"}],
|
||||
"tagOwners": {"tag:server": ["odin@example.com"]}
|
||||
},
|
||||
"status": 200,
|
||||
"response": {}
|
||||
}]
|
||||
}]
|
||||
}
|
||||
@@ -317,6 +317,9 @@ var rejectedPolicySkipReasons = map[string]string{
|
||||
"ssh-e2/refused": noTailnetDomains,
|
||||
"ssh-malformed-user-localpart-multi-glob/refused": noTailnetDomains,
|
||||
"sshtest-malformed-dst-bare-ipv6/refused": "SSHTEST_BARE_IPV6_DST: headscale accepts a bare IPv6 sshTests destination",
|
||||
|
||||
"policytest-src-split-across-rules/refused": "TEST_SRC_ONE_RULE: headscale lets each group member pass through its own rule",
|
||||
"sshtest-group-split-actions-accept/refused": "SSHTEST_SRC_ONE_RULE: headscale lets each group member pass through its own accept or check rule",
|
||||
}
|
||||
|
||||
const noTailnetDomains = "NO_TAILNET_DOMAINS: headscale does not check user:*@domain or localpart:*@domain against the tailnet's domains"
|
||||
|
||||
Reference in new issue
Block a user