The info API is for other programs on the host, and on unix the only
setting between "the user nebula runs as" and "everyone" was a manual
chgrp that the next bind undid; on windows it was not served at all,
because which accounts a pipe admits had not been decided. The control
socket's `group` decides it, and the info API takes the same key.
- `local_api.info.group`: the socket is handed to the group at 0660
unless `socket_mode` says otherwise, and a mode that gives a named
group nothing is refused with the mode that would
- windows: served on a named pipe, the group an ACE on it as on the
control pipe; the e2e test's helpers dial unix sockets, so it still
skips there
- a changed group or mode on a reload is applied to the socket in place
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Build slackhq/nebula#1821 ("who is this vpn address?" over a local
HTTP+JSON socket) on the command registry, so the lookup is one command
on every surface and the socket is one more local API.
identity
- `identity [-address <vpn addr>[:port]]`: no address reports this node;
with one, this node or the peer behind an established tunnel
- Certificate fields only: name, vpn addresses, networks, unsafe
networks, groups, fingerprint, issuer, validity, cert version; a peer's
fingerprint is the one nebula computed when it verified the cert
- Port accepted and stripped, 4in6 unmapped, so a RemoteAddr passes
straight through
- No tunnel is a commands.NotFound; an empty -address is refused
- Lighthouse TXT lookup shares the owner and peer lookups, keeps answering
with the initiating certificate, and no longer panics without a default cert
- Served on ssh and nebula ctl too
Info API
- Off until `local_api.info.enabled` with `socket` and optional quoted
octal `socket_mode` (default "0600"); nothing derived
- Serves commands declaring Command.Identity plus help; others 403 naming
their surfaces. TestInfoAPISurface pins the set to identity
- GET /v1 is the schema; GET /v1/<cmd>?<flag>=<value> runs one; body is
the -json rendering via commands.Render; commands.PresentationFormat
reads json/pretty from the query the way a line is read
- 400 commands.ParamError, 403 commands.WithheldError (what Resolve now
returns for a withheld name, so help and dispatch type-check it too),
404 NotFound or unknown, 500 Run error, refusal with no reason, or
recovered panic with no detail; no redirects; every miss is JSON
- listen takes a mode: bind under umask 0077 then chmod; "0660" plus a
searchable dir opens it to a group
- The chmod, at bind and on a mode-only reload, goes through chmodSocket,
which never follows a link: a user who can write the socket's directory
could otherwise swap the node for a link and have a root daemon put
0660 or 0666 on any file. Only a socket we own is chmoded. On linux the
node is held open O_PATH|O_NOFOLLOW, checked by fstat, and chmoded
through /proc/self/fd, so the check and the chmod land on one inode;
fchmodat2 is the fallback without /proc. On darwin and the BSDs it is
an Lstat check and fchmodat(AT_SYMLINK_NOFOLLOW). clearStale Lstats
too, so a dangling link is refused rather than followed by bind on
darwin
- One endpoint type owns bind under mutex, bind-before-release move,
context watch and stop/unlink; each server supplies what it serves
- Reloadable through the same reconcile shape as the control socket:
mode applied in place, unparsable section keeps what runs, control
socket path refused by name
- Wire returns Endpoints{Control, Info} with one Stop
- Windows unsupported for now (a pipe has no mode); warned once
Tests: identity lookups and shapes; API filter, status codes, panic
recovery, socket mode, rebind, cancellation; config parsing incl. bare
0660; Wire transitions; e2e of two nebulas with the info API resolving
the peer by address and port, through shared test.HTTPDo/HTTPGet helpers.
Goldens gain identity.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Add support for accessing the previously ssh-only debug commands via
`nebula ctl <command>.`
- unix socket (named pipe on windows) for communication
- sockets scoped to the user + an optional group
- in cmd/nebula and cmd/nebula-service, but not nebula.Main
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Report ssh command failures to the surface (#1959)
The ssh debug commands told the user when something went wrong but
never told the surface that ran them, so `ssh host cmd` always exited 0
and a few failures were not reported at all. A failed command now
returns its error, a failure the user has already heard about is a
commands.Failure, and the ssh exec path exits 1 for either. The command
bodies also lose their ssh prefixes and the duplication they carried
over from ssh.go; both goldens pin that nothing else changed.
- Dispatch returns the callback's error, or shlex's; the ssh session
logs it at debug, and not at all for a Failure the user already saw
- commands.Fail writes a message and returns it as a Failure in one step;
an unknown command, `help` for a command that is not there, a failed
reload and an unbalanced quote are failures
- ssh exec exit-status is 1 for a command, flag or failure error, 0
otherwise; the e2e golden records the exit status for every case
- Clone rebinds help to the copy, so an ssh session's help lists logout
and `help logout` and `logout -h` answer; sshd.LogoutCommand is the
session's logout, exported for tests
- "did not understand" names the first word only, the one looked up;
DispatchArgs takes just the tokens
- log-format writes its refusal to the user, as log-level does
- print-relays emits the relay error as a string and omits the key when
there is none, where it printed {} or "Error":null
- list-hostmap and list-lighthouse-addrmap -json return the encode
error; print-cert reports a certificate that will not marshal
- a flags type that is not the one registered is an internal error, not
a silent success
- command functions and flag types are <name>Command and <name>Flags;
parseVpnAddr and listHostMapFlagSet replace the repeated parse block
and flag set; close-tunnel sends through sendCloseTunnel
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Migrate commands from ssh into a transport neutral package
- add golden tests validating that output remains unchanged
- move commands to commands.go
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
* use stdlib maps.Values
This has been in stdlib since go1.23, no need to use golang.org/x/exp
anymore just for this:
- https://pkg.go.dev/maps#Values
* use slices.AppendSeq to match old behavior
handleOutsideRelayPacket filled ViaSender.remoteIdx with relay.RemoteIndex,
an index from the relay peer's index space, but the rescue in
sendHandshakeResponse looks that value up in relayForByIdx, which is keyed
by local index. The lookup could never hit, so a terminal relay entry left
Disestablished by a one-sided teardown stayed Disestablished even after a
valid handshake arrived over it. The responder's first transmit then failed
to find an Established relay, deleted its only relay entry, and every
subsequent send was silently dropped until dead-tunnel detection forced a
re-handshake.
* firewall can distinguish if the host connecting has an overlapping network, is a VPN peer without an overlapping network, or is a unsafe network
* Cross stack subnet stuff (#1512)
* experiment with not filtering out non-common addresses in hostinfo.networks
* allow handshakes without overlaps
* unsafe network test
* change HostInfo.buildNetworks argument to reference the cert
* try to make certificate addition/removal reloadable in some cases
* very spicy change to respond to handshakes with cert versions we cannot match with a cert that we can indeed match
* even spicier change to rehandshake if we detect our cert is lower-version than our peer, and we have a newer-version cert available
* make tryRehandshake easier to understand
* upgrade to yaml.v3
The main nice fix here is that maps unmarshal into `map[string]any`
instead of `map[any]any`, so it cleans things up a bit.
* add config.AsBool
Since yaml.v3 doesn't automatically convert yes to bool now, for
backwards compat
* use type aliases for m
* more cleanup
* more cleanup
* more cleanup
* go mod cleanup
* Fix for relay migration on rehandshaking issue. On rehandshake, the relay tunnel doesn't migrate to the new hostinfo object correctly, due to an incorrect Nebula IP sent in the CreateRelayRequest message.
* Add a test for this case
---------
Co-authored-by: Nate Brown <nbrown.us@gmail.com>
* Support NIST curve P256
This change adds support for NIST curve P256. When you use `nebula-cert ca`
or `nebula-cert keygen`, you can specify `-curve P256` to enable it. The
curve to use is based on the curve defined in your CA certificate.
Internally, we use ECDSA P256 to sign certificates, and ECDH P256 to do
Noise handshakes. P256 is not supported natively in Noise Protocol, so
we define `DHP256` in the `noiseutil` package to implement support for
it.
You cannot have a mixed network of Curve25519 and P256 certificates,
since the Noise protocol will only attempt to parse using the Curve
defined in the host's certificate.
* verify the curves match in VerifyPrivateKey
This would have failed anyways once we tried to actually use the bytes
in the private key, but its better to detect the issue up front with
a better error message.
* add cert.Curve argument to Sign method
* fix mismerge
* use crypto/ecdh
This is the preferred method for doing ECDH functions now, and also has
a boringcrypto specific codepath.
* remove other ecdh uses of crypto/elliptic
use crypto/ecdh instead