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

  1. Connect the client machine to the NetBird network (netbird up) and confirm the peer shows Connected before retrying the URL.
  2. 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.
  3. Check proxy logs for the preceding tunnel-validation failure to see whether the peer identity or source range was rejected.
  4. 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

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


AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16). Data as JSON: /api/errors/6cd9503d9246d7f6. Report an issue: GitHub.