{"record":{"id":"6cd9503d9246d7f6","repo":"netbirdio/netbird","slug":"host-argument-required","errorCode":null,"errorMessage":"host argument required","messagePattern":"host argument required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"client/cmd/ssh.go","lineNumber":436,"sourceCode":"\tfs.BoolVar(&flags.StrictHostKeyChecking, \"strict-host-key-checking\", true, \"Enable strict host key checking\")\n\tfs.StringVar(&flags.KnownHostsFile, \"o\", \"\", \"Path to known_hosts file\")\n\tfs.StringVar(&flags.KnownHostsFile, \"known-hosts\", \"\", \"Path to known_hosts file\")\n\tfs.StringVar(&flags.IdentityFile, \"i\", \"\", \"Path to SSH private key file\")\n\tfs.StringVar(&flags.IdentityFile, \"identity\", \"\", \"Path to SSH private key file\")\n\tfs.BoolVar(&flags.SkipCachedToken, \"no-cache\", false, \"Skip cached JWT token and force fresh authentication\")\n\tfs.BoolVar(&flags.NoBrowser, \"no-browser\", defaultNoBrowser, noBrowserDesc)\n\n\tfs.StringVar(&flags.ConfigPath, \"c\", defaultConfigPath, \"Netbird config file location\")\n\tfs.StringVar(&flags.ConfigPath, \"config\", defaultConfigPath, \"Netbird config file location\")\n\tfs.StringVar(&flags.LogLevel, \"l\", defaultLogLevel, \"sets Netbird log level\")\n\tfs.StringVar(&flags.LogLevel, \"log-level\", defaultLogLevel, \"sets Netbird log level\")\n\n\treturn fs, flags\n}\n\nfunc validateSSHArgsWithoutFlagParsing(_ *cobra.Command, args []string) error {\n\tif len(args) < 1 {\n\t\treturn errors.New(hostArgumentRequired)\n\t}\n\n\tresetSSHGlobals()\n\n\tif len(os.Args) > 2 {\n\t\textractGlobalFlags(os.Args[1:])\n\t}\n\n\tfilteredArgs, localForwardFlags, remoteForwardFlags := parseCustomSSHFlags(args)\n\n\tfs, flags := createSSHFlagSet()\n\n\tif err := fs.Parse(filteredArgs); err != nil {\n\t\tif errors.Is(err, flag.ErrHelp) {\n\t\t\treturn nil\n\t\t}\n\t\treturn err\n\t}","sourceCodeStart":418,"sourceCodeEnd":454,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/client/cmd/ssh.go#L418-L454","documentation":"Emitted by the proxy auth middleware (proxy/internal/auth/middleware.go:131) for a domain registered with private=true. Private services skip operator auth schemes entirely: the request is handed to forwardWithTunnelPeer, which only succeeds when the connection arrives over the NetBird overlay and the peer passes tunnel validation (ValidateTunnelPeer, cached). If that helper returns false, the request is denied with a bare 403.","triggerScenarios":"Any request to a private service's hostname that did not come through the NetBird tunnel: a client on the public internet or LAN hitting the domain directly, a connected peer whose tunnel identity validation fails, or an overlay connection whose source address is not recognized (validation cache expired and re-validation failed).","commonSituations":"Operator tests a private service URL from a browser outside the NetBird network; the peer's NetBird agent is disconnected (netbird down) while DNS still resolves the domain; the service was flipped to private but users were never told to connect to the overlay first; network route/exit-node misconfiguration strips the overlay source address.","solutions":["Connect the client machine to the NetBird network (netbird up) and confirm the peer shows Connected before retrying the URL.","Verify the private flag is intended: if the service should be reachable by unauthenticated or scheme-authenticated public users, re-register the domain with private=false.","Check proxy logs for the preceding tunnel-validation failure to see whether the peer identity or source range was rejected.","If testing locally without the overlay, use a peer that is enrolled and connected, or temporarily test through a connected jump host."],"exampleFix":"// before: domain registered private, public clients get 403\nmw.AddDomain(\"svc.example.com\", nil, \"\", time.Hour, accountID, serviceID, nil, true)\n\n// after: only operator-scheme-authenticated public access intended, drop private\nmw.AddDomain(\"svc.example.com\", schemes, pubKeyB64, time.Hour, accountID, serviceID, nil, false)","handlingStrategy":"validation","validationCode":"// Client-side guard: before calling a private service, require an active overlay\n// route to it (the NetBird interface must be up).\nfunc canReachPrivateService(tunnelIP string) bool {\n    iface, err := net.InterfaceByName(\"wt0\") // NetBird interface name on this host\n    if err != nil {\n        return false\n    }\n    addrs, _ := iface.Addrs()\n    for _, a := range addrs {\n        if net.ParseIP(strings.Split(a.String(), \"/\")[0]) != nil {\n            return true // overlay address present, tunnel likely up\n        }\n    }\n    _ = tunnelIP\n    return false\n}","typeGuard":null,"tryCatchPattern":"resp, err := client.Get(url)\nif err == nil && resp.StatusCode == http.StatusForbidden {\n    // For private services, 403 almost always means 'not on the overlay':\n    // reconnect netbird, verify peer status, then retry once.\n    log.Print(\"403 from private service: connect to the NetBird network first\")\n}","preventionTips":["Run 'netbird status' and require Connected before accessing private service URLs.","Document clearly which service hostnames are private and overlay-only.","Do not save private-service URLs in public DNS tooling that pre-fetches them (probes will get 403).","When registering a domain, set private=true only when every consumer is an overlay peer."],"tags":["proxy","authorization","tunnel","overlay","http"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}