Identify tailnet callers once per connection; list processes via sysctl

This commit is contained in:
Ian Gulliver
2026-10-23 11:22:16 -07:00
parent 69848a2c6e
commit 0a094aaa72
6 changed files with 58 additions and 31 deletions
+2 -1
View File
@@ -19,6 +19,7 @@ import (
"syscall"
"time"
"g.fc.run/ai/byre/internal/peer"
"g.fc.run/ai/byre/internal/server"
"tailscale.com/tsnet"
)
@@ -117,7 +118,7 @@ func serveCmd(args []string) {
base := func(net.Listener) context.Context { return ctx }
loopback := &http.Server{Addr: *listenAddr, Handler: srv.LoopbackHandler(), BaseContext: base}
tailnet := &http.Server{Handler: srv.Handler(), BaseContext: base}
tailnet := &http.Server{Handler: srv.Handler(), BaseContext: base, ConnContext: peer.ConnContext}
go func() {
<-ctx.Done()
+1 -1
View File
@@ -8,6 +8,7 @@ require (
github.com/getkin/kin-openapi v0.142.0
github.com/go-git/go-git/v5 v5.19.3
github.com/oapi-codegen/runtime v1.7.0
golang.org/x/sys v0.48.0
howett.net/plist v1.0.0
modernc.org/sqlite v1.60.1
tailscale.com v1.104.2
@@ -69,7 +70,6 @@ require (
golang.org/x/net v0.59.0 // indirect
golang.org/x/oauth2 v0.37.0 // indirect
golang.org/x/sync v0.23.0 // indirect
golang.org/x/sys v0.48.0 // indirect
golang.org/x/term v0.46.0 // indirect
golang.org/x/text v0.42.0 // indirect
golang.org/x/time v0.16.0 // indirect
+13 -26
View File
@@ -1,24 +1,21 @@
package agents
import (
"bufio"
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"log/slog"
"os"
"os/exec"
"path/filepath"
"slices"
"strconv"
"strings"
"sync"
"syscall"
"time"
"g.fc.run/ai/byre/internal/api"
"golang.org/x/sys/unix"
)
const (
@@ -112,7 +109,7 @@ func (m *Manager) Health(ctx context.Context) (Health, error) {
return Health{}, err
}
children, err := claudeChildren(ctx, os.Getpid())
children, err := claudeChildren(os.Getpid())
if err != nil {
return Health{}, err
}
@@ -226,39 +223,29 @@ func pidAlive(pid int) bool {
return err == nil || errors.Is(err, syscall.EPERM)
}
func claudeChildren(ctx context.Context, parent int) ([]int, error) {
cctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
out, err := exec.CommandContext(cctx, "ps", "-A", "-o", "pid=,ppid=,comm=").Output()
func claudeChildren(parent int) ([]int, error) {
procs, err := unix.SysctlKinfoProcSlice("kern.proc.all")
if err != nil {
return nil, fmt.Errorf("list processes: %w", err)
}
return parseChildren(out, parent), nil
}
func parseChildren(out []byte, parent int) []int {
pids := []int{}
sc := bufio.NewScanner(bytes.NewReader(out))
for sc.Scan() {
fields := strings.Fields(sc.Text())
if len(fields) < 3 {
for _, p := range procs {
if int(p.Eproc.Ppid) != parent {
continue
}
pid, err := strconv.Atoi(fields[0])
if err != nil {
pid := int(p.Proc.P_pid)
// p_comm holds the resolved binary's name (claude's is its version), so match the path it was started by.
args, err := unix.SysctlRaw("kern.procargs2", pid)
if err != nil || len(args) < 4 {
continue
}
ppid, err := strconv.Atoi(fields[1])
if err != nil || ppid != parent {
continue
}
if filepath.Base(strings.Join(fields[2:], " ")) != "claude" {
path, _, _ := strings.Cut(string(args[4:]), "\x00")
if filepath.Base(path) != "claude" {
continue
}
pids = append(pids, pid)
}
return pids
return pids, nil
}
func problems(h Health) []string {
+3 -2
View File
@@ -17,6 +17,7 @@ import (
"g.fc.run/ai/byre/internal/api"
"g.fc.run/ai/byre/internal/mcp"
"g.fc.run/ai/byre/internal/peer"
"g.fc.run/ai/byre/internal/store"
)
@@ -169,7 +170,7 @@ func (m *Manager) servePreview(port int) error {
http.Error(w, "forbidden preview host", http.StatusForbidden)
return
}
who, err := lc.WhoIs(r.Context(), r.RemoteAddr)
who, err := peer.WhoIs(r, lc)
if err != nil {
http.Error(w, err.Error(), http.StatusForbidden)
return
@@ -183,7 +184,7 @@ func (m *Manager) servePreview(port int) error {
proxy.ServeHTTP(w, r)
})
go func() {
err := (&http.Server{Handler: handler}).Serve(tracked)
err := (&http.Server{Handler: handler, ConnContext: peer.ConnContext}).Serve(tracked)
if !errors.Is(err, net.ErrClosed) {
slog.Error("serve preview", "port", port, "err", err)
}
+37
View File
@@ -0,0 +1,37 @@
package peer
import (
"context"
"net"
"net/http"
"sync"
"tailscale.com/client/local"
"tailscale.com/client/tailscale/apitype"
)
type key struct{}
type peer struct {
mu sync.Mutex
who *apitype.WhoIsResponse
}
func ConnContext(ctx context.Context, _ net.Conn) context.Context {
return context.WithValue(ctx, key{}, &peer{})
}
func WhoIs(r *http.Request, lc *local.Client) (*apitype.WhoIsResponse, error) {
p := r.Context().Value(key{}).(*peer)
p.mu.Lock()
defer p.mu.Unlock()
if p.who != nil {
return p.who, nil
}
who, err := lc.WhoIs(r.Context(), r.RemoteAddr)
if err != nil {
return nil, err
}
p.who = who
return who, nil
}
+2 -1
View File
@@ -19,6 +19,7 @@ import (
"g.fc.run/ai/byre/internal/clients"
"g.fc.run/ai/byre/internal/git"
"g.fc.run/ai/byre/internal/ids"
"g.fc.run/ai/byre/internal/peer"
"g.fc.run/ai/byre/internal/schedule"
"g.fc.run/ai/byre/internal/store"
"g.fc.run/ai/byre/internal/terminal"
@@ -172,7 +173,7 @@ func (s *Server) GetTailnet(w http.ResponseWriter, r *http.Request) {
func (s *Server) identify(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
identified := trace.Start(r.Context(), "whois")
who, err := s.tailnet.WhoIs(r.Context(), r.RemoteAddr)
who, err := peer.WhoIs(r, s.tailnet)
identified()
if err != nil {
writeError(w, http.StatusUnauthorized, fmt.Errorf("identify %s on the tailnet: %w", r.RemoteAddr, err))