{"record":{"id":"4c303a8d932e141e","repo":"juanfont/headscale","slug":"autogroup-internet-not-valid-as-a-test-destination","errorCode":null,"errorMessage":"autogroup:internet not valid as a test destination","messagePattern":"autogroup:internet not valid as a test destination","errorType":"validation","errorClass":"ErrAutogroupInternetTestDst","httpStatus":null,"severity":"error","filePath":"hscontrol/policy/v2/types.go","lineNumber":151,"sourceCode":"\tErrProtocolOutOfRange          = errors.New(\"protocol number out of range (0-255)\")\n\tErrAutogroupNotSupported       = errors.New(\"autogroup not supported in headscale\")\n\tErrAutogroupInternetSrc        = errors.New(\"autogroup:internet can only be used in ACL destinations\")\n\tErrAutogroupSelfSrc            = errors.New(\"\\\"autogroup:self\\\" not valid on the src side of a rule\")\n\tErrAutogroupNotSupportedACLSrc = errors.New(\"autogroup not supported for ACL sources\")\n\tErrAutogroupNotSupportedACLDst = errors.New(\"autogroup not supported for ACL destinations\")\n\tErrAutogroupDangerAllDst       = errors.New(\"cannot use autogroup:danger-all as a dst\")\n\tErrAutogroupNotSupportedSSHSrc = errors.New(\"autogroup not supported for SSH sources\")\n\tErrAutogroupNotSupportedSSHDst = errors.New(\"autogroup not supported for SSH destinations\")\n\tErrHostNotDefined              = errors.New(\"host not defined in policy\")\n\tErrSSHSourceAliasNotSupported  = errors.New(\"alias not supported for SSH source\")\n\tErrSSHDestAliasNotSupported    = errors.New(\"alias not supported for SSH destination\")\n\tErrUnknownField                = errors.New(\"unknown field\")\n\tErrProtocolNoSpecificPorts     = errors.New(\"protocol does not support specific ports\")\n\tErrTestEmptyAssertions         = errors.New(\"test entry must have at least one of \\\"accept\\\" or \\\"deny\\\"\")\n\tErrTestProtocolNotAllowed      = errors.New(\"test protocol must be tcp, udp, sctp, or empty\")\n\tErrTestDestinationMultiPort    = errors.New(\"test destination port must be a single port\")\n\tErrTestDestinationCIDR         = errors.New(\"test destination must be a single host, not a CIDR range\")\n\tErrAutogroupInternetTestDst    = errors.New(\"autogroup:internet not valid as a test destination\")\n\tErrSSHTestEmptySrc             = errors.New(\"SSH tests entry must have a non-empty src\")\n\tErrSSHTestEmptyDst             = errors.New(\"SSH tests entry must have at least one dst\")\n\tErrSSHTestDstUnknownTag        = errors.New(\"SSH tests dst contains unknown tag\")\n\tErrSSHTestDstDisallowedElement = errors.New(\"SSH tests dst contains disallowed element\")\n)\n\ntype resolved struct {\n\tips netipx.IPSet\n}\n\nfunc newResolved(ipb *netipx.IPSetBuilder) (resolved, error) {\n\tips, err := ipb.IPSet()\n\tif err != nil {\n\t\treturn resolved{}, err\n\t}\n\n\treturn resolved{ips: *ips}, nil\n}","sourceCodeStart":133,"sourceCodeEnd":169,"githubUrl":"https://github.com/juanfont/headscale/blob/565fd254d06c4c7f9a8cad1714a43445c79ba420/hscontrol/policy/v2/types.go#L133-L169","documentation":"ErrAutogroupInternetTestDst is returned by validateTestDestination (hscontrol/policy/v2/types.go:3251) when a tests-block destination is autogroup:internet. Internet access is enforced via exit-node routing (AllowedIPs on tailcfg.Node), not the packet filter, so the tests engine cannot give a single allow/deny answer for it. autogroup:internet remains valid as an ACL/grant destination only.","triggerScenarios":"A tests entry like {\"src\": \"user1\", \"accept\": [\"autogroup:internet:443\"]}. Raised when the parsed destination alias is *AutoGroup == AutoGroupInternet.","commonSituations":"Adding a regression test for 'can this user reach the internet via an exit node'; copying an existing grant that uses autogroup:internet as dst into the tests block; misunderstanding that tests evaluate only the packet filter.","solutions":["Remove the autogroup:internet entry from the tests block","Verify internet/exit-node access with a real client or integration test instead","If you want a packet-filter-level assertion, test against a concrete exit-node host address"],"exampleFix":"// before\n\"tests\": [{\"src\": \"user1\", \"accept\": [\"autogroup:internet:443\"]}]\n// after\n\"tests\": [{\"src\": \"user1\", \"accept\": [\"100.100.100.1:443\"]}]  // or drop the test","handlingStrategy":"validation","validationCode":"func isAutogroupInternet(dst string) bool { return dst == \"autogroup:internet\" || strings.HasPrefix(dst, \"autogroup:internet:\") }\n// reject such dst strings when building tests entries","typeGuard":null,"tryCatchPattern":"if errors.Is(err, policyv2.ErrAutogroupInternetTestDst) {\n    // remove the autogroup:internet test; verify internet access outside the tests block\n}","preventionTips":["Treat autogroup:internet as grant-dst-only","Test exit-node internet access with real clients/integration tests","When copying a grant into a test, strip autogroup destinations first"],"tags":["policy","tests","autogroup","exit-node"],"backgroundTag":null,"analyzedSha":"565fd254d06c4c7f9a8cad1714a43445c79ba420","analyzedAt":"2026-08-15T13:12:30.133Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}