feat: add boundary log forwarding from agent to coderd (#21345)

Add agent forwarding of boundary audit logs from workspaces to coderd
via agent API, and re-emission of boundary logs to coderd stderr. This
change adds a server to the workspace agent that always listens on a
unix socket for boundary to connect and send audit logs.

coderd log format example:
```
[API] 2025-12-23 18:31:46.755 [info] coderd.agentrpc: boundary_request owner=.. workspace_name=.. agent_name=.. decision=.. workspace_id=.. http_method=.. http_url=.. event_time=.. request_id=..
```

Corresponding boundary PR: https://github.com/coder/boundary/pull/124
RFC:
https://www.notion.so/coderhq/Agent-Boundary-Logs-2afd579be59280f29629fc9823ac41ba
https://github.com/coder/coder/issues/21280
This commit is contained in:
Zach
2025-12-31 16:38:19 -07:00
committed by GitHub
parent 21241abc4e
commit 07924037e7
11 changed files with 1339 additions and 15 deletions
+12 -2
View File
@@ -31,6 +31,7 @@ import (
"github.com/coder/coder/v2/agent/agentcontainers"
"github.com/coder/coder/v2/agent/agentexec"
"github.com/coder/coder/v2/agent/agentssh"
"github.com/coder/coder/v2/agent/boundarylogproxy"
"github.com/coder/coder/v2/agent/reaper"
"github.com/coder/coder/v2/buildinfo"
"github.com/coder/coder/v2/cli/clilog"
@@ -59,6 +60,7 @@ func workspaceAgent() *serpent.Command {
devcontainerDiscoveryAutostart bool
socketServerEnabled bool
socketPath string
boundaryLogProxySocketPath string
)
agentAuth := &AgentAuth{}
cmd := &serpent.Command{
@@ -319,8 +321,9 @@ func workspaceAgent() *serpent.Command {
agentcontainers.WithProjectDiscovery(devcontainerProjectDiscovery),
agentcontainers.WithDiscoveryAutostart(devcontainerDiscoveryAutostart),
},
SocketPath: socketPath,
SocketServerEnabled: socketServerEnabled,
SocketPath: socketPath,
SocketServerEnabled: socketServerEnabled,
BoundaryLogProxySocketPath: boundaryLogProxySocketPath,
})
if debugAddress != "" {
@@ -494,6 +497,13 @@ func workspaceAgent() *serpent.Command {
Description: "Specify the path for the agent socket.",
Value: serpent.StringOf(&socketPath),
},
{
Flag: "boundary-log-proxy-socket-path",
Default: boundarylogproxy.DefaultSocketPath(),
Env: "CODER_AGENT_BOUNDARY_LOG_PROXY_SOCKET_PATH",
Description: "The path for the boundary log proxy server Unix socket. Boundary should write audit logs to this socket.",
Value: serpent.StringOf(&boundaryLogProxySocketPath),
},
}
agentAuth.AttachOptions(cmd, false)
return cmd
+11 -1
View File
@@ -138,6 +138,17 @@ func normalizeGoldenFile(t *testing.T, byt []byte) []byte {
// The home directory changes depending on the test environment.
byt = bytes.ReplaceAll(byt, []byte(homeDir), []byte("~"))
// Normalize the temp directory. os.TempDir() may include a trailing slash
// (macOS) or not (Linux/Windows), and the temp directory may be followed by
// more filepath elements with an OS-specific separator. We handle all cases
// by replacing tempdir+separator first, then tempdir alone.
tempDir := filepath.Clean(os.TempDir())
byt = bytes.ReplaceAll(byt, []byte(tempDir+string(filepath.Separator)), []byte("/tmp/"))
byt = bytes.ReplaceAll(byt, []byte(tempDir), []byte("/tmp"))
// Clean up trailing slash when temp dir is used standalone (e.g., "/tmp/)" -> "/tmp)").
byt = bytes.ReplaceAll(byt, []byte("/tmp/)"), []byte("/tmp)"))
for _, r := range []struct {
old string
new string
@@ -145,7 +156,6 @@ func normalizeGoldenFile(t *testing.T, byt []byte) []byte {
{"\r\n", "\n"},
{`~\.cache\coder`, "~/.cache/coder"},
{`C:\Users\RUNNER~1\AppData\Local\Temp`, "/tmp"},
{os.TempDir(), "/tmp"},
} {
byt = bytes.ReplaceAll(byt, []byte(r.old), []byte(r.new))
}
+4
View File
@@ -39,6 +39,10 @@ OPTIONS:
--block-file-transfer bool, $CODER_AGENT_BLOCK_FILE_TRANSFER (default: false)
Block file transfer using known applications: nc,rsync,scp,sftp.
--boundary-log-proxy-socket-path string, $CODER_AGENT_BOUNDARY_LOG_PROXY_SOCKET_PATH (default: /tmp/boundary-audit.sock)
The path for the boundary log proxy server Unix socket. Boundary
should write audit logs to this socket.
--debug-address string, $CODER_AGENT_DEBUG_ADDRESS (default: 127.0.0.1:2113)
The bind address to serve a debug HTTP server.