Add a local control socket and nebula ctl (#1975)

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>
This commit is contained in:
Matt RichardsonandClaude Fable 5.1 authored and GitHub committed 2026-10-21 11:16:00 -04:00
1 parent 9a5bdb508e
commit 0f767ce529
54 files changed
+7581 -925

No files matched your search

+52
View File
@@ -0,0 +1,52 @@
//go:build !windows
package localapi
import (
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
type fakeConfig map[string]string
func (f fakeConfig) GetString(k, d string) string {
if v, ok := f[k]; ok {
return v
}
return d
}
// TestSocketPathFromConfigUsesTheDefault is what makes the endpoint work with no
// configuration: the daemon and the CLI both land on the one fixed path.
func TestSocketPathFromConfigUsesTheDefault(t *testing.T) {
p, err := SocketPathFromConfig(fakeConfig{})
require.NoError(t, err)
assert.Equal(t, "/var/run/nebula/ctl.sock", p)
assert.Equal(t, DefaultSocketPath, p)
}
func TestSocketPathFromConfigHonoursOverride(t *testing.T) {
p, err := SocketPathFromConfig(fakeConfig{CtlSocketKey: "/tmp/custom.sock"})
require.NoError(t, err)
assert.Equal(t, "/tmp/custom.sock", p, "the path is used as given")
}
// TestSocketPathFromConfigRefusesARelativeOverride: the daemon and the CLI are started
// from different directories, so a relative path would name two sockets from one config.
// It is refused rather than resolved against anything.
func TestSocketPathFromConfigRefusesARelativeOverride(t *testing.T) {
for _, rel := range []string{"run/ctl.sock", "./ctl.sock", "ctl.sock", "../ctl.sock"} {
_, err := SocketPathFromConfig(fakeConfig{CtlSocketKey: rel})
require.Error(t, err, rel)
require.ErrorContains(t, err, "must be an absolute path", rel)
assert.ErrorContains(t, err, CtlSocketKey, rel)
}
}
func TestSocketPathFromConfigRejectsOverlongOverride(t *testing.T) {
_, err := SocketPathFromConfig(fakeConfig{CtlSocketKey: "/" + strings.Repeat("x", sunPathMax)})
assert.ErrorContains(t, err, "too long for this platform")
}