ipc, worker: dial and bind through netaddr (V-484)

Five hardcoded transports, three in internal/ipc and two in
internal/worker, all now go through the seam address. The unix perms
logic moved into netaddr, so the two copies of parentDir and the umask
dance are gone.

peerCaller already returned ok=false for a non-unix conn, so the
SO_PEERCRED path degrades correctly on tcp with no change.
This commit is contained in:
2026-08-02 16:41:56 +04:00
committed by kami
parent 3e534340bf
commit c0de473382
4 changed files with 80 additions and 89 deletions
+20 -40
View File
@@ -8,11 +8,11 @@ import (
"fmt"
"log"
"net"
"os"
"sync"
"sync/atomic"
"time"
"github.com/kami/maven/internal/netaddr"
"github.com/kami/maven/internal/store"
"golang.org/x/sys/unix"
)
@@ -446,6 +446,7 @@ func mapErr(err error) error {
type Server struct {
api atomic.Value // stores CoreAPI
path string
addr netaddr.Addr
ln net.Listener
wg sync.WaitGroup
@@ -610,31 +611,29 @@ type CheckFunc func(ctx context.Context, m Method, params json.RawMessage) error
// MethodAssertStepUp dispatch calls this instead of going through CoreAPI.
type StepUpFunc func(ctx context.Context) error
// Listen creates a Server bound to path. path's parent dir must exist and be
// 0700 (we chmod it if we own it); the socket file itself is created 0600 so
// only the same unix user can connect — the current "auth floor", same radius
// as wg at the network boundary. Removing a stale socket at path first lets
// the daemon restart cleanly.
// Listen creates a Server bound to path.
//
// A bare path is a unix socket, unchanged: its parent dir is 0700 and the
// socket file itself is 0600, so only the same unix user can connect — the
// current "auth floor", same radius as wg at the network boundary. A stale
// socket is removed first so the daemon restarts cleanly.
//
// A "tcp://host:port?token=..." address binds a network listener instead, for
// a module that lives on another host. There is no filesystem there to be the
// auth floor, so netaddr checks the shared token before this package sees the
// connection and a token is mandatory. See internal/netaddr.
func Listen(path string, api CoreAPI) (*Server, error) {
_ = os.Remove(path) // stale socket from a crashed daemon; ignore missing
if err := os.MkdirAll(parentDir(path), 0o700); err != nil {
return nil, fmt.Errorf("ipc: mkdir socket dir: %w", err)
}
// umask could widen the perms on socket creation; tighten then chmod to
// be explicit. 0600 ⇒ read+write by owner only.
oldMask := unix.Umask(0o077)
ln, err := net.Listen("unix", path)
unix.Umask(oldMask)
addr, err := netaddr.Parse(path)
if err != nil {
return nil, fmt.Errorf("ipc: listen %s: %w", path, err)
return nil, err
}
if err := os.Chmod(path, 0o600); err != nil {
_ = ln.Close()
_ = os.Remove(path)
return nil, fmt.Errorf("ipc: chmod socket: %w", err)
ln, err := netaddr.Listen(addr)
if err != nil {
return nil, err
}
s := &Server{
path: path,
addr: addr,
ln: ln,
done: make(chan struct{}),
}
@@ -1268,7 +1267,7 @@ func (s *Server) Close() error {
// missing the seal costs every write since the last clean shutdown.
log.Printf("ipc: %d connection(s) still busy after %s, closing anyway", s.liveConns(), closeGrace)
}
_ = os.Remove(s.path)
netaddr.Cleanup(s.addr)
return err
}
@@ -1338,25 +1337,6 @@ func (s *Server) Path() string { return s.path }
// while the server is serving (dispatch loads api once per request via atomic).
func (s *Server) SetAPI(api CoreAPI) { s.api.Store(api) }
func parentDir(p string) string {
if i := lastIndexByte(p, '/'); i >= 0 {
if i == 0 {
return "/"
}
return p[:i]
}
return "."
}
func lastIndexByte(s string, b byte) int {
for i := len(s) - 1; i >= 0; i-- {
if s[i] == b {
return i
}
}
return -1
}
// peerCaller — read SO_PEERCRED off a unix conn to identify the connecting
// process. Returns ok=false on a non-unix conn or a platform without
// SO_PEERCRED; the caller then proceeds without a Caller (the socket perms