Files
nebula/localapi/info_windows_test.go
T
Matt RichardsonandClaude Fable 5.1 3fb29d6444 Let a group share the info API too, and serve it on windows
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>
2026-10-21 12:26:57 -04:00

59 lines
1.9 KiB
Go

//go:build windows
package localapi
import (
"context"
"io"
"net"
"net/http"
"testing"
"time"
"github.com/Microsoft/go-winio"
"github.com/slackhq/nebula/commands"
"github.com/slackhq/nebula/test"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/sys/windows"
)
// TestInfoIsServedOnAPipe: the info API serves HTTP on a named pipe, whose security
// descriptor is its access control the way a socket's mode is on unix; a group named
// in the config is admitted by it.
func TestInfoIsServedOnAPipe(t *testing.T) {
users, err := windows.CreateWellKnownSid(windows.WinBuiltinUsersSid)
require.NoError(t, err)
group := &Group{Name: "Users", ID: users.String()}
s := NewInfoServer(test.NewLogger(), commands.New())
path := pipePrefix + "nebula-info-test"
require.NoError(t, s.Rebind(context.Background(), path, Access{Mode: groupSocketMode, Group: group}))
t.Cleanup(s.Stop)
assert.Equal(t, path, s.Path())
client := &http.Client{Transport: &http.Transport{
// What a program on the host has to do: ask for the data rights alone, since the
// group is granted no more; see pipeClientAccess.
DialContext: func(ctx context.Context, _, _ string) (net.Conn, error) {
ctx, cancel := context.WithTimeout(ctx, 2*time.Second)
defer cancel()
return winio.DialPipeAccess(ctx, path, pipeClientAccess)
},
}}
res, err := client.Get("http://nebula/v1")
require.NoError(t, err)
defer res.Body.Close()
body, err := io.ReadAll(res.Body)
require.NoError(t, err)
assert.Equal(t, http.StatusOK, res.StatusCode)
assert.Contains(t, string(body), `"help"`)
// A change of group means a new pipe under the same name, still serving.
require.NoError(t, s.Rebind(context.Background(), path, Access{Mode: groupSocketMode}))
res, err = client.Get("http://nebula/v1")
require.NoError(t, err)
res.Body.Close()
assert.Equal(t, http.StatusOK, res.StatusCode)
}