netbirdio/netbird · warning
host argument required
Error message
host argument required
What it means
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.
Source
Thrown at client/cmd/ssh.go:436
fs.BoolVar(&flags.StrictHostKeyChecking, "strict-host-key-checking", true, "Enable strict host key checking")
fs.StringVar(&flags.KnownHostsFile, "o", "", "Path to known_hosts file")
fs.StringVar(&flags.KnownHostsFile, "known-hosts", "", "Path to known_hosts file")
fs.StringVar(&flags.IdentityFile, "i", "", "Path to SSH private key file")
fs.StringVar(&flags.IdentityFile, "identity", "", "Path to SSH private key file")
fs.BoolVar(&flags.SkipCachedToken, "no-cache", false, "Skip cached JWT token and force fresh authentication")
fs.BoolVar(&flags.NoBrowser, "no-browser", defaultNoBrowser, noBrowserDesc)
fs.StringVar(&flags.ConfigPath, "c", defaultConfigPath, "Netbird config file location")
fs.StringVar(&flags.ConfigPath, "config", defaultConfigPath, "Netbird config file location")
fs.StringVar(&flags.LogLevel, "l", defaultLogLevel, "sets Netbird log level")
fs.StringVar(&flags.LogLevel, "log-level", defaultLogLevel, "sets Netbird log level")
return fs, flags
}
func validateSSHArgsWithoutFlagParsing(_ *cobra.Command, args []string) error {
if len(args) < 1 {
return errors.New(hostArgumentRequired)
}
resetSSHGlobals()
if len(os.Args) > 2 {
extractGlobalFlags(os.Args[1:])
}
filteredArgs, localForwardFlags, remoteForwardFlags := parseCustomSSHFlags(args)
fs, flags := createSSHFlagSet()
if err := fs.Parse(filteredArgs); err != nil {
if errors.Is(err, flag.ErrHelp) {
return nil
}
return err
}View on GitHub (pinned to 93e97f4bf1)
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.
Example fix
// before: domain registered private, public clients get 403
mw.AddDomain("svc.example.com", nil, "", time.Hour, accountID, serviceID, nil, true)
// after: only operator-scheme-authenticated public access intended, drop private
mw.AddDomain("svc.example.com", schemes, pubKeyB64, time.Hour, accountID, serviceID, nil, false) Defensive patterns
Strategy: validation
Validate before calling
// Client-side guard: before calling a private service, require an active overlay
// route to it (the NetBird interface must be up).
func canReachPrivateService(tunnelIP string) bool {
iface, err := net.InterfaceByName("wt0") // NetBird interface name on this host
if err != nil {
return false
}
addrs, _ := iface.Addrs()
for _, a := range addrs {
if net.ParseIP(strings.Split(a.String(), "/")[0]) != nil {
return true // overlay address present, tunnel likely up
}
}
_ = tunnelIP
return false
} Try / catch
resp, err := client.Get(url)
if err == nil && resp.StatusCode == http.StatusForbidden {
// For private services, 403 almost always means 'not on the overlay':
// reconnect netbird, verify peer status, then retry once.
log.Print("403 from private service: connect to the NetBird network first")
} Prevention
- 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.
When it happens
Trigger: 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).
Common situations: 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.
Related errors
- invalid user@host format
- engine not started
- tun module not available
- read of the policy table timed out
- wireguard interface not initialized
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/6cd9503d9246d7f6.
Report an issue: GitHub.