diff --git a/agent/boundary_logs_test.go b/agent/boundary_logs_test.go index 0cc8a7264f..5701e0dc43 100644 --- a/agent/boundary_logs_test.go +++ b/agent/boundary_logs_test.go @@ -79,10 +79,12 @@ func TestBoundaryLogs_EndToEnd(t *testing.T) { logger := slog.Make(sink) workspaceID := uuid.New() templateID := uuid.New() + templateVersionID := uuid.New() reporter := &agentapi.BoundaryLogsAPI{ - Log: logger, - WorkspaceID: workspaceID, - TemplateID: templateID, + Log: logger, + WorkspaceID: workspaceID, + TemplateID: templateID, + TemplateVersionID: templateVersionID, } ctx, cancel := context.WithCancel(context.Background()) @@ -126,6 +128,7 @@ func TestBoundaryLogs_EndToEnd(t *testing.T) { require.Equal(t, "allow", getField(entry.Fields, "decision")) require.Equal(t, workspaceID.String(), getField(entry.Fields, "workspace_id")) require.Equal(t, templateID.String(), getField(entry.Fields, "template_id")) + require.Equal(t, templateVersionID.String(), getField(entry.Fields, "template_version_id")) require.Equal(t, "GET", getField(entry.Fields, "http_method")) require.Equal(t, "https://example.com/allowed", getField(entry.Fields, "http_url")) require.Equal(t, "*.example.com", getField(entry.Fields, "matched_rule")) @@ -159,6 +162,7 @@ func TestBoundaryLogs_EndToEnd(t *testing.T) { require.Equal(t, "deny", getField(entry.Fields, "decision")) require.Equal(t, workspaceID.String(), getField(entry.Fields, "workspace_id")) require.Equal(t, templateID.String(), getField(entry.Fields, "template_id")) + require.Equal(t, templateVersionID.String(), getField(entry.Fields, "template_version_id")) require.Equal(t, "POST", getField(entry.Fields, "http_method")) require.Equal(t, "https://blocked.com/denied", getField(entry.Fields, "http_url")) require.Equal(t, nil, getField(entry.Fields, "matched_rule")) diff --git a/coderd/agentapi/api.go b/coderd/agentapi/api.go index 79dafa8d83..0253b27b9d 100644 --- a/coderd/agentapi/api.go +++ b/coderd/agentapi/api.go @@ -65,10 +65,11 @@ type API struct { var _ agentproto.DRPCAgentServer = &API{} type Options struct { - AgentID uuid.UUID - OwnerID uuid.UUID - WorkspaceID uuid.UUID - OrganizationID uuid.UUID + AgentID uuid.UUID + OwnerID uuid.UUID + WorkspaceID uuid.UUID + OrganizationID uuid.UUID + TemplateVersionID uuid.UUID AuthenticatedCtx context.Context Log slog.Logger @@ -221,9 +222,10 @@ func New(opts Options, workspace database.Workspace) *API { } api.BoundaryLogsAPI = &BoundaryLogsAPI{ - Log: opts.Log, - WorkspaceID: opts.WorkspaceID, - TemplateID: workspace.TemplateID, + Log: opts.Log, + WorkspaceID: opts.WorkspaceID, + TemplateID: workspace.TemplateID, + TemplateVersionID: opts.TemplateVersionID, } // Start background cache refresh loop to handle workspace changes diff --git a/coderd/agentapi/boundary_logs.go b/coderd/agentapi/boundary_logs.go index 5285c94086..91d4f8227f 100644 --- a/coderd/agentapi/boundary_logs.go +++ b/coderd/agentapi/boundary_logs.go @@ -11,9 +11,10 @@ import ( ) type BoundaryLogsAPI struct { - Log slog.Logger - WorkspaceID uuid.UUID - TemplateID uuid.UUID + Log slog.Logger + WorkspaceID uuid.UUID + TemplateID uuid.UUID + TemplateVersionID uuid.UUID } func (a *BoundaryLogsAPI) ReportBoundaryLogs(ctx context.Context, req *agentproto.ReportBoundaryLogsRequest) (*agentproto.ReportBoundaryLogsResponse, error) { @@ -35,6 +36,7 @@ func (a *BoundaryLogsAPI) ReportBoundaryLogs(ctx context.Context, req *agentprot slog.F("decision", allowBoolToString(l.Allowed)), slog.F("workspace_id", a.WorkspaceID.String()), slog.F("template_id", a.TemplateID.String()), + slog.F("template_version_id", a.TemplateVersionID.String()), slog.F("http_method", r.HttpRequest.Method), slog.F("http_url", r.HttpRequest.Url), slog.F("event_time", logTime.Format(time.RFC3339Nano)), diff --git a/coderd/workspaceagentsrpc.go b/coderd/workspaceagentsrpc.go index e2ce6e5fab..79273ae35f 100644 --- a/coderd/workspaceagentsrpc.go +++ b/coderd/workspaceagentsrpc.go @@ -127,10 +127,11 @@ func (api *API) workspaceAgentRPC(rw http.ResponseWriter, r *http.Request) { defer monitor.close() agentAPI := agentapi.New(agentapi.Options{ - AgentID: workspaceAgent.ID, - OwnerID: workspace.OwnerID, - WorkspaceID: workspace.ID, - OrganizationID: workspace.OrganizationID, + AgentID: workspaceAgent.ID, + OwnerID: workspace.OwnerID, + WorkspaceID: workspace.ID, + OrganizationID: workspace.OrganizationID, + TemplateVersionID: build.TemplateVersionID, AuthenticatedCtx: ctx, Log: logger, diff --git a/docs/ai-coder/boundary/agent-boundary.md b/docs/ai-coder/boundary/agent-boundary.md index 0be17cbc6b..c294ea93d0 100644 --- a/docs/ai-coder/boundary/agent-boundary.md +++ b/docs/ai-coder/boundary/agent-boundary.md @@ -127,17 +127,18 @@ commands run with `boundary-run`. Each boundary audit log entry includes: -| Field | Description | -|------------------|-----------------------------------------------------------------------------------------| -| `decision` | Whether the request was allowed (`allow`) or blocked (`deny`) | -| `workspace_id` | The UUID of the workspace where the request originated | -| `workspace_name` | The name of the workspace where the request originated | -| `owner` | The owner of the workspace where the request originated | -| `template_id` | The UUID of the template that the workspace was created from | -| `http_method` | The HTTP method used (GET, POST, PUT, DELETE, etc.) | -| `http_url` | The fully qualified URL that was requested | -| `event_time` | Timestamp when boundary processed the request (RFC3339 format) | -| `matched_rule` | The allowlist rule that permitted the request (only present when `decision` is `allow`) | +| Field | Description | +|-----------------------|-----------------------------------------------------------------------------------------| +| `decision` | Whether the request was allowed (`allow`) or blocked (`deny`) | +| `workspace_id` | The UUID of the workspace where the request originated | +| `workspace_name` | The name of the workspace where the request originated | +| `owner` | The owner of the workspace where the request originated | +| `template_id` | The UUID of the template that the workspace was created from | +| `template_version_id` | The UUID of the template version used by the current workspace build | +| `http_method` | The HTTP method used (GET, POST, PUT, DELETE, etc.) | +| `http_url` | The fully qualified URL that was requested | +| `event_time` | Timestamp when boundary processed the request (RFC3339 format) | +| `matched_rule` | The allowlist rule that permitted the request (only present when `decision` is `allow`) | ### Viewing Audit Logs