mirror of
https://github.com/juanfont/headscale.git
synced 2026-07-07 16:40:21 +09:00
policy/v2: add ssh-edges captures from Tailscale SaaS
30 new captures recorded against SaaS covering multi-rule SSH policies, group nesting, tag-owner cycles, empty references, unicode, hosts table aliases, multi-src/dst, whitespace in src/dst, null and missing fields, and sshTests edges. 20 captures already match headscale behaviour. The remaining 10 land in sshSkipReasons and sshRejectSkipReasons tracking 5 engine divergences (whitespace trim in src/dst, tag-owner cycle tolerance, hosts-alias rejection on SSH dst, non-ASCII tag-name rejection, group-nesting error wording); follow-up commits close each gap and drain the corresponding skip entries.
This commit is contained in:
@@ -87,6 +87,31 @@ var sshSkipReasons = map[string]string{
|
||||
// equivalent for the user:*@passkey wildcard pattern.
|
||||
"ssh-b5": "user:*@passkey wildcard not supported in headscale",
|
||||
"ssh-d10": "user:*@passkey wildcard not supported in headscale",
|
||||
|
||||
// WHITESPACE_TRIM_IN_SRC_DST (2 tests)
|
||||
//
|
||||
// SaaS strips surrounding whitespace from SSH rule src/dst entries
|
||||
// before lookup ("tag:server " resolves to "tag:server",
|
||||
// " user@host" resolves to "user@host"). headscale treats the
|
||||
// untrimmed string as a literal identifier and either fails the
|
||||
// tag/user lookup or produces no rule for the affected node. The
|
||||
// fix lives in policy/v2 alias resolution — strings.TrimSpace at
|
||||
// the same point where SaaS does, before dispatching to the alias
|
||||
// type registry.
|
||||
"ssh-dst-trailing-whitespace": "src/dst whitespace trim mismatch: SaaS trims, headscale doesn't",
|
||||
"ssh-src-leading-whitespace": "src/dst whitespace trim mismatch: SaaS trims, headscale doesn't",
|
||||
|
||||
// TAG_OWNER_CYCLE_TOLERATED (2 tests)
|
||||
//
|
||||
// SaaS accepts tagOwners with circular references (tag:a -> tag:b,
|
||||
// tag:b -> tag:a or tag:a -> tag:a) and resolves them as the empty
|
||||
// owner set. headscale's TagOwnerSet resolver eagerly detects
|
||||
// circular tag references and rejects the policy at parse time.
|
||||
// Pick a side and adjust: either match SaaS by treating cycles as
|
||||
// "no owners" instead of an error, or document that headscale is
|
||||
// intentionally stricter and remove these scenarios.
|
||||
"ssh-tag-owner-cycle": "headscale rejects tagOwner cycles that SaaS accepts as empty owner set",
|
||||
"ssh-tag-owner-self-reference": "headscale rejects self-referencing tagOwner that SaaS accepts as empty owner set",
|
||||
}
|
||||
|
||||
// sshRejectSkipReasons documents APIResponseCode != 200 captures where
|
||||
@@ -106,6 +131,41 @@ var sshRejectSkipReasons = map[string]string{
|
||||
"ssh-e1": "domain validation: headscale has no 'associated tailnet domains' concept",
|
||||
"ssh-e2": "domain validation: headscale has no 'associated tailnet domains' concept",
|
||||
"ssh-malformed-user-localpart-multi-glob": "domain validation: headscale has no 'associated tailnet domains' concept (same gap as ssh-b4/d1/e1/e2)",
|
||||
|
||||
// HOSTS_ALIAS_AS_SSH_DST (2 tests)
|
||||
//
|
||||
// SaaS rejects every hosts-alias dst on an SSH rule with
|
||||
// `invalid dst "alias"`, regardless of whether the alias resolves
|
||||
// to a single IP or a CIDR. headscale's compileSSHPolicy resolves
|
||||
// host aliases through the same path as ACL dsts and accepts
|
||||
// them. Either reject hosts-aliases at SSH dst parse time to
|
||||
// match, or document that headscale is intentionally more
|
||||
// permissive than SaaS for SSH dsts.
|
||||
"ssh-hosts-as-dst-single-ip": "SaaS rejects host alias as SSH dst, headscale accepts",
|
||||
"ssh-hosts-as-dst-multi-host-prefix": "SaaS rejects host alias as SSH dst, headscale accepts",
|
||||
|
||||
// NON_ASCII_TAG_NAME (1 test)
|
||||
//
|
||||
// SaaS rejects non-ASCII characters in tag names with
|
||||
// `tag names must start with a letter, after 'tag:'`. headscale's
|
||||
// tag parser accepts arbitrary UTF-8. Tighten the tag-name
|
||||
// validator to match SaaS's letter-only rule.
|
||||
"ssh-unicode-cyrillic-tag": "SaaS rejects non-ASCII tag names, headscale accepts",
|
||||
|
||||
// GROUP_NESTING_ERROR_BODY (3 tests)
|
||||
//
|
||||
// SaaS rejects any group-in-group reference (cycle, chain,
|
||||
// self-cycle) with the structured message
|
||||
// `groups["X"]: "Y": group members cannot be recursive`.
|
||||
// headscale rejects too but the error surfaces as a generic
|
||||
// `parsing policy: parsing policy from bytes: json: unable to
|
||||
// unmarshal …` because the group resolver fails before the
|
||||
// validation phase that would emit a specific message. Wire the
|
||||
// resolver's "group references a group" detection to produce the
|
||||
// SaaS-style structured error so the body matches.
|
||||
"ssh-group-nested-cycle": "group nesting rejected with different error body (parse error vs structured 'group members cannot be recursive')",
|
||||
"ssh-group-nested-three-deep": "group nesting rejected with different error body (parse error vs structured 'group members cannot be recursive')",
|
||||
"ssh-group-nested-two-deep": "group nesting rejected with different error body (parse error vs structured 'group members cannot be recursive')",
|
||||
}
|
||||
|
||||
// TestSSHDataCompat is a data-driven test that loads all ssh-*.hujson test
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+22348
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+22113
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+22107
File diff suppressed because it is too large
Load Diff
+22107
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+21916
File diff suppressed because it is too large
Load Diff
+21916
File diff suppressed because it is too large
Load Diff
+22146
File diff suppressed because it is too large
Load Diff
Vendored
+22152
File diff suppressed because it is too large
Load Diff
+22146
File diff suppressed because it is too large
Load Diff
+22146
File diff suppressed because it is too large
Load Diff
+21944
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+22123
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+22101
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+21887
File diff suppressed because it is too large
Load Diff
+21903
File diff suppressed because it is too large
Load Diff
+21904
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user