Switch VNC daemon-to-agent IPC to Unix sockets and audit-log every connection

This commit is contained in:
Viktor Liu
2026-05-22 15:32:35 +02:00
parent 8ca5c2f4b4
commit 613b886c78
9 changed files with 273 additions and 342 deletions
+6 -66
View File
@@ -3,9 +3,6 @@
package server
import (
"bytes"
"errors"
"io"
"net"
log "github.com/sirupsen/logrus"
@@ -23,17 +20,15 @@ func (s *Server) platformSessionManager() virtualSessionManager {
return nil
}
// serviceAcceptLoop runs in a LaunchDaemon and proxies each VNC
// connection to a per-user agent. The agent is spawned lazily on the
// first connection (and respawned after a console-user change) via
// launchctl asuser, which is the only mechanism that lands a child
// inside the user's Aqua session, where WindowServer and TCC grants
// for screen capture work.
// serviceAcceptLoop runs as a LaunchDaemon and proxies each VNC connection
// to the per-user agent darwinAgentManager spawns via launchctl asuser
// (the only spawn mode that lands a child in the user's Aqua session with
// WindowServer + TCC access).
func (s *Server) serviceAcceptLoop() {
mgr := newDarwinAgentManager(s.ctx)
defer mgr.stop()
log.Infof("service mode, proxying connections to per-user agent on 127.0.0.1:%d", agentPort)
log.Info("service mode, proxying connections to per-user agent over Unix socket")
for {
conn, err := s.listener.Accept()
@@ -58,62 +53,7 @@ func (s *Server) serviceAcceptLoop() {
go func(c net.Conn) {
defer s.releaseConnSlot()
defer s.untrackConn(c)
s.handleServiceConnectionDarwin(c, mgr)
s.handleServiceConnection(c, mgr)
}(conn)
}
}
func (s *Server) handleServiceConnectionDarwin(conn net.Conn, mgr *darwinAgentManager) {
connLog := s.log.WithField("remote", conn.RemoteAddr().String())
if !s.isAllowedSource(conn.RemoteAddr()) {
conn.Close()
return
}
var headerBuf bytes.Buffer
tee := io.TeeReader(conn, &headerBuf)
teeConn := &darwinPrefixConn{Reader: tee, Conn: conn}
header, err := s.readConnectionHeader(teeConn)
if err != nil {
connLog.Debugf("read connection header: %v", err)
conn.Close()
return
}
if !s.disableAuth {
if _, err := s.authenticateSession(header); err != nil {
rejectConnection(conn, codeMessage(RejectCodeAuthForbidden, err.Error()))
connLog.Warnf("auth rejected: %v", err)
return
}
}
s.registerConnAuth(conn, header)
token, err := mgr.ensure(s.ctx)
if err != nil {
code := RejectCodeCapturerError
if errors.Is(err, errNoConsoleUser) {
code = RejectCodeNoConsoleUser
}
rejectConnection(conn, codeMessage(code, err.Error()))
connLog.Warnf("spawn per-user agent: %v", err)
return
}
replayConn := &darwinPrefixConn{
Reader: io.MultiReader(&headerBuf, conn),
Conn: conn,
}
proxyToAgent(s.ctx, replayConn, agentPort, token)
}
// darwinPrefixConn replays the already-consumed connection-header bytes
// in front of the proxy stream, mirroring the Windows prefixConn shape.
type darwinPrefixConn struct {
io.Reader
net.Conn
}
func (p *darwinPrefixConn) Read(b []byte) (int, error) { return p.Reader.Read(b) }