mirror of
https://github.com/juanfont/headscale.git
synced 2026-11-04 04:34:07 +09:00
policy/v2: add grant port list refusal capture
Grant and ACL refusals share one check-keyed refused/message check.
This commit is contained in:
1 parent
8d01364b00
commit
629eb158a3
4 files changed
+32482
-56
No files matched your search
@@ -271,7 +271,7 @@ func TestACLCompat(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
if tf.Error {
|
||||
testACLError(t, tf)
|
||||
testCompatRefusal(t, aclSkipReasons, tf)
|
||||
|
||||
return
|
||||
}
|
||||
@@ -288,8 +288,9 @@ func TestACLCompat(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// testACLError verifies that an invalid policy produces the expected error.
|
||||
func testACLError(t *testing.T, tf *testcapture.Capture) {
|
||||
// testCompatRefusal checks that headscale refuses a policy SaaS refused, with
|
||||
// SaaS's message in its error; checks are keyed <test_id>/<check> in skips.
|
||||
func testCompatRefusal(t *testing.T, skips map[string]string, tf *testcapture.Capture) {
|
||||
t.Helper()
|
||||
|
||||
policyJSON := convertPolicyUserEmails(tf.Input.FullPolicy)
|
||||
@@ -301,7 +302,7 @@ func testACLError(t *testing.T, tf *testcapture.Capture) {
|
||||
|
||||
key := tf.TestID + "/"
|
||||
|
||||
compatCheck(t, aclSkipReasons, key+"refused", func() string {
|
||||
compatCheck(t, skips, key+"refused", func() string {
|
||||
if err == nil {
|
||||
return "SaaS refused; the policy parsed and validated"
|
||||
}
|
||||
@@ -310,7 +311,7 @@ func testACLError(t *testing.T, tf *testcapture.Capture) {
|
||||
})
|
||||
|
||||
if err == nil {
|
||||
compatChecksNotRun(t, aclSkipReasons, key+"message")
|
||||
compatChecksNotRun(t, skips, key+"message")
|
||||
|
||||
return
|
||||
}
|
||||
@@ -322,7 +323,7 @@ func testACLError(t *testing.T, tf *testcapture.Capture) {
|
||||
want = tf.Input.APIResponseBody.Message
|
||||
}
|
||||
|
||||
compatCheck(t, aclSkipReasons, key+"message", func() string {
|
||||
compatCheck(t, skips, key+"message", func() string {
|
||||
if strings.Contains(err.Error(), want) {
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -175,6 +175,8 @@ var grantSkipReasons = map[string]string{
|
||||
"via-grant-v63/blastoise/filter": viaDstAddressForm,
|
||||
"via-grant-v63/squirtle/filter": viaDstAddressForm,
|
||||
|
||||
"grant-dxp-comma-ports/refused": grantPortList,
|
||||
|
||||
// Contiguous pinned addresses; TestViaGrantHACompat compares these
|
||||
// filters by address.
|
||||
"via-grant-v64/client-a/filter": srcAddressForm,
|
||||
@@ -191,6 +193,8 @@ var grantSkipReasons = map[string]string{
|
||||
"via-grant-v65/via-r/filter": srcAddressForm,
|
||||
}
|
||||
|
||||
const grantPortList = "GRANT_PORT_LIST: SaaS refuses a comma port list in a grant ip entry (only one port range allowed); headscale accepts it"
|
||||
|
||||
const viaDstAddressForm = "FILTER_ADDRESS_FORM: SaaS writes a single-address via destination as a bare address, headscale as a /32 prefix"
|
||||
|
||||
// TestGrantsCompat is a data-driven test that loads all GRANT-*.json
|
||||
@@ -240,7 +244,7 @@ func TestGrantsCompat(t *testing.T) {
|
||||
policyJSON := convertPolicyUserEmails(tf.Input.FullPolicy)
|
||||
|
||||
if tf.Input.APIResponseCode == 400 || tf.Error {
|
||||
testGrantError(t, policyJSON, tf)
|
||||
testCompatRefusal(t, grantSkipReasons, tf)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -249,55 +253,6 @@ func TestGrantsCompat(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// testGrantError verifies that an invalid policy produces the expected error.
|
||||
func testGrantError(t *testing.T, policyJSON []byte, tf *testcapture.Capture) {
|
||||
t.Helper()
|
||||
|
||||
wantMsg := ""
|
||||
if tf.Input.APIResponseBody != nil {
|
||||
wantMsg = tf.Input.APIResponseBody.Message
|
||||
}
|
||||
|
||||
pol, err := unmarshalPolicy(policyJSON)
|
||||
if err != nil {
|
||||
// Parse-time error
|
||||
if wantMsg != "" {
|
||||
assertGrantErrorContains(t, err, wantMsg, tf.TestID)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
err = pol.validate()
|
||||
if err != nil {
|
||||
// Validation error
|
||||
if wantMsg != "" {
|
||||
assertGrantErrorContains(t, err, wantMsg, tf.TestID)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
t.Errorf("%s: expected error (api_response_code=400) but policy parsed and validated successfully; want message: %q",
|
||||
tf.TestID, wantMsg)
|
||||
}
|
||||
|
||||
// assertGrantErrorContains requires that headscale's error contains
|
||||
// the Tailscale SaaS error message exactly. Divergence means an
|
||||
// emitter needs to be aligned, not papered over with a translation
|
||||
// table.
|
||||
func assertGrantErrorContains(t *testing.T, err error, wantMsg string, testID string) {
|
||||
t.Helper()
|
||||
|
||||
errStr := err.Error()
|
||||
if strings.Contains(errStr, wantMsg) {
|
||||
return
|
||||
}
|
||||
|
||||
t.Errorf("%s: error message mismatch\n tailscale wants: %q\n headscale got: %q",
|
||||
testID, wantMsg, errStr)
|
||||
}
|
||||
|
||||
// testGrantSuccess verifies that a valid policy produces the expected
|
||||
// packet filter rules for each node.
|
||||
func testGrantSuccess(
|
||||
|
||||
+32468
File diff suppressed because it is too large.
Load diff
@@ -322,6 +322,8 @@ var rejectedPolicySkipReasons = map[string]string{
|
||||
|
||||
"ssh-check-tag-src/refused": "SSH_CHECK_TAG_SRC: headscale accepts a check rule with a tag source",
|
||||
|
||||
"grant-dxp-comma-ports/refused": "GRANT_PORT_LIST: headscale accepts a comma port list in a grant ip entry",
|
||||
|
||||
"policytest-src-cidr-accept/refused": "TEST_SRC_SHAPE: headscale evaluates a CIDR tests source",
|
||||
"policytest-src-wildcard-deny/refused": "TEST_SRC_SHAPE: headscale evaluates \"*\" as a tests source",
|
||||
"policytest-dst-multinode-partial-accept/refused": "TEST_DST_EVERY_NODE: headscale accepts when one node of the dst alias is reached",
|
||||
|
||||
Reference in new issue
Block a user