feat: log tailnet tunnels to the connection log (#27423)

Co-authored-by: Chris DiGiamo <cd@anthropic.com>
Co-authored-by: Chris DiGiamo <cdigiamo@anthropic.com>
This commit is contained in:
Jon Ayers
2026-07-28 15:30:12 -05:00
committed by GitHub
co-authored by Chris DiGiamo Chris DiGiamo
parent 8cc7f2bb0e
commit 1a6a8be96c
21 changed files with 381 additions and 30 deletions
+2 -1
View File
@@ -379,7 +379,8 @@ CREATE TYPE connection_type AS ENUM (
'jetbrains',
'reconnecting_pty',
'workspace_app',
'port_forwarding'
'port_forwarding',
'tunnel'
);
CREATE TYPE cors_behavior AS ENUM (
@@ -0,0 +1,8 @@
-- The 'tunnel' enum value is intentionally not removed. Postgres cannot
-- drop an enum value in place; removing it would require recreating
-- connection_type and rewriting the connection_logs type column, which
-- takes an exclusive lock on the table and would have to DELETE all
-- tunnel rows (audit data) because they cannot exist in the old type.
-- Leaving the value in place is harmless: old code never queries for it
-- and renders unknown types without error. This matches the precedent
-- of other enum-value additions (e.g. 000517, 000531).
@@ -0,0 +1 @@
ALTER TYPE connection_type ADD VALUE IF NOT EXISTS 'tunnel';
+4 -1
View File
@@ -1855,6 +1855,7 @@ const (
ConnectionTypeReconnectingPty ConnectionType = "reconnecting_pty"
ConnectionTypeWorkspaceApp ConnectionType = "workspace_app"
ConnectionTypePortForwarding ConnectionType = "port_forwarding"
ConnectionTypeTunnel ConnectionType = "tunnel"
)
func (e *ConnectionType) Scan(src interface{}) error {
@@ -1899,7 +1900,8 @@ func (e ConnectionType) Valid() bool {
ConnectionTypeJetbrains,
ConnectionTypeReconnectingPty,
ConnectionTypeWorkspaceApp,
ConnectionTypePortForwarding:
ConnectionTypePortForwarding,
ConnectionTypeTunnel:
return true
}
return false
@@ -1913,6 +1915,7 @@ func AllConnectionTypeValues() []ConnectionType {
ConnectionTypeReconnectingPty,
ConnectionTypeWorkspaceApp,
ConnectionTypePortForwarding,
ConnectionTypeTunnel,
}
}
+27 -6
View File
@@ -4049,6 +4049,20 @@ func TestConnectionLogsOffsetFilters(t *testing.T) {
UserID: uuid.NullUUID{UUID: user3.ID, Valid: true},
})
// Tunnel events are point-in-time (no disconnect event is ever
// reported), so despite having a NULL disconnect_time they must be
// excluded from both status filters.
log5 := dbgen.ConnectionLog(t, db, database.UpsertConnectionLogParams{
Time: now.Add(-30 * time.Minute),
OrganizationID: ws1.OrganizationID,
WorkspaceOwnerID: ws1.OwnerID,
WorkspaceID: ws1.ID,
WorkspaceName: ws1.Name,
Type: database.ConnectionTypeTunnel,
ConnectionStatus: database.ConnectionStatusConnected,
UserID: uuid.NullUUID{UUID: user1.ID, Valid: true},
})
testCases := []struct {
name string
params database.GetConnectionLogsOffsetParams
@@ -4058,7 +4072,7 @@ func TestConnectionLogsOffsetFilters(t *testing.T) {
name: "NoFilter",
params: database.GetConnectionLogsOffsetParams{},
expectedLogIDs: []uuid.UUID{
log1.ID, log2.ID, log3.ID, log4.ID,
log1.ID, log2.ID, log3.ID, log4.ID, log5.ID,
},
},
{
@@ -4073,14 +4087,14 @@ func TestConnectionLogsOffsetFilters(t *testing.T) {
params: database.GetConnectionLogsOffsetParams{
WorkspaceOwner: user1.Username,
},
expectedLogIDs: []uuid.UUID{log1.ID, log2.ID},
expectedLogIDs: []uuid.UUID{log1.ID, log2.ID, log5.ID},
},
{
name: "WorkspaceOwnerID",
params: database.GetConnectionLogsOffsetParams{
WorkspaceOwnerID: user1.ID,
},
expectedLogIDs: []uuid.UUID{log1.ID, log2.ID},
expectedLogIDs: []uuid.UUID{log1.ID, log2.ID, log5.ID},
},
{
name: "WorkspaceOwnerEmail",
@@ -4096,19 +4110,26 @@ func TestConnectionLogsOffsetFilters(t *testing.T) {
},
expectedLogIDs: []uuid.UUID{log2.ID, log4.ID},
},
{
name: "TypeTunnel",
params: database.GetConnectionLogsOffsetParams{
Type: string(database.ConnectionTypeTunnel),
},
expectedLogIDs: []uuid.UUID{log5.ID},
},
{
name: "UserID",
params: database.GetConnectionLogsOffsetParams{
UserID: user1.ID,
},
expectedLogIDs: []uuid.UUID{log1.ID},
expectedLogIDs: []uuid.UUID{log1.ID, log5.ID},
},
{
name: "Username",
params: database.GetConnectionLogsOffsetParams{
Username: user1.Username,
},
expectedLogIDs: []uuid.UUID{log1.ID},
expectedLogIDs: []uuid.UUID{log1.ID, log5.ID},
},
{
name: "UserEmail",
@@ -4122,7 +4143,7 @@ func TestConnectionLogsOffsetFilters(t *testing.T) {
params: database.GetConnectionLogsOffsetParams{
ConnectedAfter: now.Add(-90 * time.Minute), // 1.5 hours ago
},
expectedLogIDs: []uuid.UUID{log4.ID},
expectedLogIDs: []uuid.UUID{log4.ID, log5.ID},
},
{
name: "ConnectedBefore",
+6 -4
View File
@@ -13742,8 +13742,9 @@ SELECT COUNT(*) AS count FROM (
WHEN $13 :: text != '' THEN
(($13 = 'ongoing' AND disconnect_time IS NULL) OR
($13 = 'completed' AND disconnect_time IS NOT NULL)) AND
-- Exclude web events, since we don't know their close time.
"type" NOT IN ('workspace_app', 'port_forwarding')
-- Exclude point-in-time events reported by coderd, since we
-- don't know their close time.
"type" NOT IN ('workspace_app', 'port_forwarding', 'tunnel')
ELSE true
END
-- Authorize Filter clause will be injected below in
@@ -13936,8 +13937,9 @@ WHERE
WHEN $13 :: text != '' THEN
(($13 = 'ongoing' AND disconnect_time IS NULL) OR
($13 = 'completed' AND disconnect_time IS NOT NULL)) AND
-- Exclude web events, since we don't know their close time.
"type" NOT IN ('workspace_app', 'port_forwarding')
-- Exclude point-in-time events reported by coderd, since we
-- don't know their close time.
"type" NOT IN ('workspace_app', 'port_forwarding', 'tunnel')
ELSE true
END
-- Authorize Filter clause will be injected below in
+6 -4
View File
@@ -115,8 +115,9 @@ WHERE
WHEN @status :: text != '' THEN
((@status = 'ongoing' AND disconnect_time IS NULL) OR
(@status = 'completed' AND disconnect_time IS NOT NULL)) AND
-- Exclude web events, since we don't know their close time.
"type" NOT IN ('workspace_app', 'port_forwarding')
-- Exclude point-in-time events reported by coderd, since we
-- don't know their close time.
"type" NOT IN ('workspace_app', 'port_forwarding', 'tunnel')
ELSE true
END
-- Authorize Filter clause will be injected below in
@@ -230,8 +231,9 @@ SELECT COUNT(*) AS count FROM (
WHEN @status :: text != '' THEN
((@status = 'ongoing' AND disconnect_time IS NULL) OR
(@status = 'completed' AND disconnect_time IS NOT NULL)) AND
-- Exclude web events, since we don't know their close time.
"type" NOT IN ('workspace_app', 'port_forwarding')
-- Exclude point-in-time events reported by coderd, since we
-- don't know their close time.
"type" NOT IN ('workspace_app', 'port_forwarding', 'tunnel')
ELSE true
END
-- Authorize Filter clause will be injected below in