From 73b4787043e1c5f253d34cd4de4c3089af4ca391 Mon Sep 17 00:00:00 2001 From: noodlemctwoodle Date: Tue, 12 May 2026 00:25:19 +0100 Subject: [PATCH 01/27] Add Tailscale (CCF) solution MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Microsoft Sentinel solution for ingesting Tailscale audit and network telemetry via OAuth2-secured APIs. Targets the Codeless Connector Framework (CCF) so no Azure Function code is required. The solution ships two complementary data connectors split along the boundary that Tailscale's pricing draws — configuration audit is available on every plan, network flow logs require Premium or above. Users install the connector that matches their tailnet tier: - Tailscale Standard (CCF) - Endpoint: /logging/configuration - Plans: Personal (Free) + Standard - Custom table: Tailscale_Configuration_CL - Status: shipping in this release - Tailscale Premium (CCF) - Endpoints: /logging/configuration + /logging/network (two pollers, one OAuth client, one Connect) - Plans: Premium + Enterprise - Custom tables: Tailscale_Configuration_CL + Tailscale_Network_CL - Status: scaffolded but awaiting validation against a real Premium tailnet (network flow log shape verified against Tailscale docs) Files added: - Logos/Tailscale.svg - Solutions/Tailscale (CCF)/README.md - Solutions/Tailscale (CCF)/ReleaseNotes.md - Solutions/Tailscale (CCF)/SolutionMetadata.json - Solutions/Tailscale (CCF)/Data/Solution_Tailscale.json (manifest) - Solutions/Tailscale (CCF)/Tailscale.svg (per-solution copy) - Solutions/Tailscale (CCF)/Data Connectors/TailscaleAuditLogs_ccf/ - Tailscale_ConnectorDefinition.json (UI card, OAuth2 form) - Tailscale_PollerConfig.json (single RestApiPoller, client_credentials) - Tailscale_DCR.json (Custom-Tailscale_Configuration_CL stream) - Tailscale_tables.json (Tailscale_Configuration_CL) - Solutions/Tailscale (CCF)/Data Connectors/TailscalePremium_ccf/ - TailscalePremium_ConnectorDefinition.json - TailscalePremium_PollerConfig.json (two pollers, same OAuth client) - TailscalePremium_DCR.json (two streams + dataFlows) - TailscalePremium_tables.json (both custom tables) - Solutions/Tailscale (CCF)/Analytic Rules/ (5 detections) - TailscaleNewAPIaccesstokenorOAuthclientcreated.yaml (Medium) - TailscalePolicyfileACLmodified.yaml (Medium) - TailscaleAuthkeycreated.yaml (Low) - TailscaleExitnodeadvertisedorapproved.yaml (Low) - TailscaleMasscredentialrevocationinshortwindow.yaml (High) - Solutions/Tailscale (CCF)/Package/ (createSolutionV3 build output — mainTemplate.json, createUiDefinition.json, testParameters.json, 3.0.1.zip) Implementation notes: - OAuth2 client_credentials auth wired with the PascalCase field names expected by createCCPConnector.ps1 (ClientId, ClientSecret, GrantType, TokenEndpoint, TokenEndpointHeaders, TokenEndpointQueryParameters). The earlier camelCase / Mustache-placeholder shape caused the build tool to silently skip Package generation. - Parameter substitution uses the ARM double-bracket escape pattern ([[parameters('clientId')]) so that placeholders are evaluated at the inner deployment (Connect-click) scope, not at outer Solution-install. - The /logging/network stream schema mirrors the Tailscale API response (logs[].virtualTraffic[], subnetTraffic[], exitTraffic[], physicalTraffic[]) — arrays are stored as dynamic columns and unpacked via mv-expand in KQL on the consumer side. - Premium poller uses a single OAuth client with both scopes (logs:configuration:read + logs:network:read) so one Connect deploys both pollers with one shared guidValue. - sampleQueries field added (required by Sentinel deployment validation — its absence returned the BadRequest "Required property 'SampleQueries' not found" earlier). - Support metadata follows the Community-tier convention used elsewhere in the repo (support.name = "Community", link to Azure-Sentinel issues) to avoid the duplicate-author rendering quirk in Content Hub. Testing performed: - Built via createSolutionV3 in -VersionMode local — Package generated cleanly, three valid JSON files plus zip. - Standard connector deployed end-to-end into a test Sentinel workspace (Tailscale_Configuration_CL table created, UI card visible, OAuth Connect form renders all three inputs). - All five analytic rule YAMLs validated against Sentinel schema. - Premium connector files build into mainTemplate.json with two contentTemplates and proper guidValue / innerWorkspace plumbing for multi-poller single-card deployment (matches Proofpoint TAP pattern). - Live Premium endpoint testing pending — requires Premium tailnet. Breaking changes: None — net-new solution. --- Logos/Tailscale.svg | 1 + .../TailscaleAuthkeycreated.yaml | 42 + ...TailscaleExitnodeadvertisedorapproved.yaml | 45 + ...Masscredentialrevocationinshortwindow.yaml | 48 + ...NewAPIaccesstokenorOAuthclientcreated.yaml | 43 + .../TailscalePolicyfileACLmodified.yaml | 40 + .../Tailscale_ConnectorDefinition.json | 108 + .../TailscaleAuditLogs_ccf/Tailscale_DCR.json | 67 + .../Tailscale_PollerConfig.json | 50 + .../Tailscale_tables.json | 51 + .../TailscalePremium_ConnectorDefinition.json | 119 + .../TailscalePremium_DCR.json | 59 + .../TailscalePremium_PollerConfig.json | 98 + .../TailscalePremium_tables.json | 47 + .../Data/Solution_Tailscale.json | 22 + Solutions/Tailscale (CCF)/Package/3.0.1.zip | Bin 0 -> 13726 bytes .../Package/createUiDefinition.json | 200 ++ .../Tailscale (CCF)/Package/mainTemplate.json | 2153 +++++++++++++++++ .../Package/testParameters.json | 38 + Solutions/Tailscale (CCF)/README.md | 62 + Solutions/Tailscale (CCF)/ReleaseNotes.md | 3 + .../Tailscale (CCF)/SolutionMetadata.json | 22 + Solutions/Tailscale (CCF)/Tailscale.svg | 1 + 23 files changed, 3319 insertions(+) create mode 100644 Logos/Tailscale.svg create mode 100644 Solutions/Tailscale (CCF)/Analytic Rules/TailscaleAuthkeycreated.yaml create mode 100644 Solutions/Tailscale (CCF)/Analytic Rules/TailscaleExitnodeadvertisedorapproved.yaml create mode 100644 Solutions/Tailscale (CCF)/Analytic Rules/TailscaleMasscredentialrevocationinshortwindow.yaml create mode 100644 Solutions/Tailscale (CCF)/Analytic Rules/TailscaleNewAPIaccesstokenorOAuthclientcreated.yaml create mode 100644 Solutions/Tailscale (CCF)/Analytic Rules/TailscalePolicyfileACLmodified.yaml create mode 100644 Solutions/Tailscale (CCF)/Data Connectors/TailscaleAuditLogs_ccf/Tailscale_ConnectorDefinition.json create mode 100644 Solutions/Tailscale (CCF)/Data Connectors/TailscaleAuditLogs_ccf/Tailscale_DCR.json create mode 100644 Solutions/Tailscale (CCF)/Data Connectors/TailscaleAuditLogs_ccf/Tailscale_PollerConfig.json create mode 100644 Solutions/Tailscale (CCF)/Data Connectors/TailscaleAuditLogs_ccf/Tailscale_tables.json create mode 100644 Solutions/Tailscale (CCF)/Data Connectors/TailscalePremium_ccf/TailscalePremium_ConnectorDefinition.json create mode 100644 Solutions/Tailscale (CCF)/Data Connectors/TailscalePremium_ccf/TailscalePremium_DCR.json create mode 100644 Solutions/Tailscale (CCF)/Data Connectors/TailscalePremium_ccf/TailscalePremium_PollerConfig.json create mode 100644 Solutions/Tailscale (CCF)/Data Connectors/TailscalePremium_ccf/TailscalePremium_tables.json create mode 100644 Solutions/Tailscale (CCF)/Data/Solution_Tailscale.json create mode 100644 Solutions/Tailscale (CCF)/Package/3.0.1.zip create mode 100644 Solutions/Tailscale (CCF)/Package/createUiDefinition.json create mode 100644 Solutions/Tailscale (CCF)/Package/mainTemplate.json create mode 100644 Solutions/Tailscale (CCF)/Package/testParameters.json create mode 100644 Solutions/Tailscale (CCF)/README.md create mode 100644 Solutions/Tailscale (CCF)/ReleaseNotes.md create mode 100644 Solutions/Tailscale (CCF)/SolutionMetadata.json create mode 100644 Solutions/Tailscale (CCF)/Tailscale.svg diff --git a/Logos/Tailscale.svg b/Logos/Tailscale.svg new file mode 100644 index 00000000000..b7d6e8a1905 --- /dev/null +++ b/Logos/Tailscale.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleAuthkeycreated.yaml b/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleAuthkeycreated.yaml new file mode 100644 index 00000000000..9a163d91b35 --- /dev/null +++ b/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleAuthkeycreated.yaml @@ -0,0 +1,42 @@ +id: 6b052c8d-5de8-eab0-1956-69a297765a32 +name: "Tailscale: Auth key created" +description: | + A new auth key was generated. Auth keys allow unattended device enrollment into the tailnet — confirm it was expected and revoke if not. +severity: Low +status: Available +requiredDataConnectors: + - connectorId: TailscaleCCF + dataTypes: + - Tailscale_Configuration_CL +queryFrequency: PT15M +queryPeriod: PT15M +triggerOperator: GreaterThan +triggerThreshold: 0 +tactics: + - Persistence +relevantTechniques: + - T1098 +query: | + Tailscale_Configuration_CL + | where Action == "CREATE" + | where tostring(Target.type) == "AUTH_KEY" + | extend ActorLogin = tostring(Actor.loginName) + | extend KeyDescription = tostring(New.description) + | extend Reusable = tostring(New.reusable) + | extend Ephemeral = tostring(New.ephemeral) + | project TimeGenerated, ActorLogin, KeyDescription, Reusable, Ephemeral, Origin, New +entityMappings: + - entityType: Account + fieldMappings: + - identifier: FullName + columnName: ActorLogin +incidentConfiguration: + createIncident: true + groupingConfiguration: + enabled: true + reopenClosedIncident: false + lookbackDuration: PT5H + matchingMethod: AllEntities + groupByEntities: [] +kind: Scheduled +version: 1.0.0 diff --git a/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleExitnodeadvertisedorapproved.yaml b/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleExitnodeadvertisedorapproved.yaml new file mode 100644 index 00000000000..b6dcde87f30 --- /dev/null +++ b/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleExitnodeadvertisedorapproved.yaml @@ -0,0 +1,45 @@ +id: f42f2906-c8e6-23d0-e48c-0620e50d5510 +name: "Tailscale: Exit node advertised or approved" +description: | + A device started advertising itself as an exit node, or an admin approved one. Validate the device and operator — rogue exit nodes can intercept tailnet egress. +severity: Low +status: Available +requiredDataConnectors: + - connectorId: TailscaleCCF + dataTypes: + - Tailscale_Configuration_CL +queryFrequency: PT30M +queryPeriod: PT30M +triggerOperator: GreaterThan +triggerThreshold: 0 +tactics: + - CommandAndControl + - Exfiltration +relevantTechniques: + - T1090 +query: | + Tailscale_Configuration_CL + | where Action contains "EXIT" or tostring(New.advertisedExitNode) == "true" or tostring(New.allowedExitNode) == "true" + | extend ActorLogin = tostring(Actor.loginName) + | extend NodeName = tostring(Target.name) + | extend NodeId = tostring(Target.id) + | project TimeGenerated, ActorLogin, Action, NodeName, NodeId, Origin, New, Old +entityMappings: + - entityType: Account + fieldMappings: + - identifier: FullName + columnName: ActorLogin + - entityType: Host + fieldMappings: + - identifier: HostName + columnName: NodeName +incidentConfiguration: + createIncident: true + groupingConfiguration: + enabled: true + reopenClosedIncident: false + lookbackDuration: PT5H + matchingMethod: AllEntities + groupByEntities: [] +kind: Scheduled +version: 1.0.0 diff --git a/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleMasscredentialrevocationinshortwindow.yaml b/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleMasscredentialrevocationinshortwindow.yaml new file mode 100644 index 00000000000..693a9e44e41 --- /dev/null +++ b/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleMasscredentialrevocationinshortwindow.yaml @@ -0,0 +1,48 @@ +id: f817e2fa-6fa0-fc25-5369-cef9b58771af +name: "Tailscale: Mass credential revocation in short window" +description: | + Five or more API keys, OAuth clients, or auth keys were revoked/deleted within an hour. May be routine rotation, but also a typical cleanup pattern after credential compromise. +severity: High +status: Available +requiredDataConnectors: + - connectorId: TailscaleCCF + dataTypes: + - Tailscale_Configuration_CL +queryFrequency: PT1H +queryPeriod: PT1H +triggerOperator: GreaterThan +triggerThreshold: 0 +tactics: + - DefenseEvasion + - Impact +relevantTechniques: + - T1070 +query: | + Tailscale_Configuration_CL + | where Action in ("REVOKE", "DELETE") + | where tostring(Target.type) in ("API_KEY", "OAUTH_CLIENT", "AUTH_KEY") + | extend ActorLogin = tostring(Actor.loginName) + | summarize + RevokedCount = count(), + TargetTypes = make_set(tostring(Target.type)), + TargetIds = make_set(tostring(Target.id)), + FirstEvent = min(TimeGenerated), + LastEvent = max(TimeGenerated) + by ActorLogin, bin(TimeGenerated, 1h) + | where RevokedCount >= 5 + | project TimeGenerated = LastEvent, ActorLogin, RevokedCount, TargetTypes, TargetIds, FirstEvent, LastEvent +entityMappings: + - entityType: Account + fieldMappings: + - identifier: FullName + columnName: ActorLogin +incidentConfiguration: + createIncident: true + groupingConfiguration: + enabled: true + reopenClosedIncident: false + lookbackDuration: PT5H + matchingMethod: AllEntities + groupByEntities: [] +kind: Scheduled +version: 1.0.0 diff --git a/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleNewAPIaccesstokenorOAuthclientcreated.yaml b/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleNewAPIaccesstokenorOAuthclientcreated.yaml new file mode 100644 index 00000000000..7571356a77f --- /dev/null +++ b/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleNewAPIaccesstokenorOAuthclientcreated.yaml @@ -0,0 +1,43 @@ +id: 668b43fd-cf28-961a-85af-957850df5027 +name: "Tailscale: New API access token or OAuth client created" +description: | + A new API access token or OAuth client was created in the tailnet. These grant programmatic access — verify the actor and intent. +severity: Medium +status: Available +requiredDataConnectors: + - connectorId: TailscaleCCF + dataTypes: + - Tailscale_Configuration_CL +queryFrequency: PT15M +queryPeriod: PT15M +triggerOperator: GreaterThan +triggerThreshold: 0 +tactics: + - Persistence + - CredentialAccess +relevantTechniques: + - T1098 + - T1136 +query: | + Tailscale_Configuration_CL + | where Action == "CREATE" + | where tostring(Target.type) in ("API_KEY", "OAUTH_CLIENT") + | extend ActorLogin = tostring(Actor.loginName) + | extend TargetName = tostring(Target.name) + | extend TargetId = tostring(Target.id) + | project TimeGenerated, ActorLogin, Action, TargetName, TargetId, Origin, New +entityMappings: + - entityType: Account + fieldMappings: + - identifier: FullName + columnName: ActorLogin +incidentConfiguration: + createIncident: true + groupingConfiguration: + enabled: true + reopenClosedIncident: false + lookbackDuration: PT5H + matchingMethod: AllEntities + groupByEntities: [] +kind: Scheduled +version: 1.0.0 diff --git a/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePolicyfileACLmodified.yaml b/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePolicyfileACLmodified.yaml new file mode 100644 index 00000000000..cb63c61af5f --- /dev/null +++ b/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePolicyfileACLmodified.yaml @@ -0,0 +1,40 @@ +id: 1e7249c2-1a9d-05fd-45cb-c859eef5b8ae +name: "Tailscale: Policy file (ACL) modified" +description: | + The tailnet ACL/policy file was modified. Review the diff — incorrect ACLs can silently expand blast radius across the tailnet. +severity: Medium +status: Available +requiredDataConnectors: + - connectorId: TailscaleCCF + dataTypes: + - Tailscale_Configuration_CL +queryFrequency: PT15M +queryPeriod: PT15M +triggerOperator: GreaterThan +triggerThreshold: 0 +tactics: + - DefenseEvasion + - Persistence +relevantTechniques: + - T1556 +query: | + Tailscale_Configuration_CL + | where Action in ("UPDATE", "CREATE") + | where tostring(Target.type) == "ACL" + | extend ActorLogin = tostring(Actor.loginName) + | project TimeGenerated, ActorLogin, Action, Target, Origin, Old, New +entityMappings: + - entityType: Account + fieldMappings: + - identifier: FullName + columnName: ActorLogin +incidentConfiguration: + createIncident: true + groupingConfiguration: + enabled: true + reopenClosedIncident: false + lookbackDuration: PT5H + matchingMethod: AllEntities + groupByEntities: [] +kind: Scheduled +version: 1.0.0 diff --git a/Solutions/Tailscale (CCF)/Data Connectors/TailscaleAuditLogs_ccf/Tailscale_ConnectorDefinition.json b/Solutions/Tailscale (CCF)/Data Connectors/TailscaleAuditLogs_ccf/Tailscale_ConnectorDefinition.json new file mode 100644 index 00000000000..696ab22b6b5 --- /dev/null +++ b/Solutions/Tailscale (CCF)/Data Connectors/TailscaleAuditLogs_ccf/Tailscale_ConnectorDefinition.json @@ -0,0 +1,108 @@ +{ + "name": "TailscaleCCF", + "apiVersion": "2022-09-01-preview", + "type": "Microsoft.SecurityInsights/dataConnectorDefinitions", + "kind": "Customizable", + "properties": { + "connectorUiConfig": { + "title": "Tailscale Standard (CCF)", + "publisher": "Custom", + "logo": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbDpzcGFjZT0icHJlc2VydmUiIGlkPSJMYXllcl8xIiB4PSIwIiB5PSIwIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIj48c3R5bGU+LnN0MHtvcGFjaXR5Oi4yO2VuYWJsZS1iYWNrZ3JvdW5kOm5ld308L3N0eWxlPjxwYXRoIGQ9Ik02NS42IDEyNy43YzM1LjMgMCA2My45LTI4LjYgNjMuOS02My45UzEwMC45IDAgNjUuNiAwIDEuOCAyOC42IDEuOCA2My45czI4LjYgNjMuOCA2My44IDYzLjgiIGNsYXNzPSJzdDAiLz48cGF0aCBkPSJNNjUuNiAzMTguMWMzNS4zIDAgNjMuOS0yOC42IDYzLjktNjMuOXMtMjguNi02My45LTYzLjktNjMuOVMxLjggMjE5IDEuOCAyNTQuMnMyOC42IDYzLjkgNjMuOCA2My45Ii8+PHBhdGggZD0iTTY1LjYgNTEyYzM1LjMgMCA2My45LTI4LjYgNjMuOS02My45cy0yOC42LTYzLjktNjMuOS02My45LTYzLjggMjguNy02My44IDYzLjlTMzAuNCA1MTIgNjUuNiA1MTIiIGNsYXNzPSJzdDAiLz48cGF0aCBkPSJNMjU3LjIgMzE4LjFjMzUuMyAwIDYzLjktMjguNiA2My45LTYzLjlzLTI4LjYtNjMuOS02My45LTYzLjktNjMuOSAyOC42LTYzLjkgNjMuOSAyOC42IDYzLjkgNjMuOSA2My45bTAgMTkzLjljMzUuMyAwIDYzLjktMjguNiA2My45LTYzLjlzLTI4LjYtNjMuOS02My45LTYzLjktNjMuOSAyOC42LTYzLjkgNjMuOSAyOC42IDYzLjkgNjMuOSA2My45Ii8+PHBhdGggZD0iTTI1Ny4yIDEyNy43YzM1LjMgMCA2My45LTI4LjYgNjMuOS02My45UzI5Mi41IDAgMjU3LjIgMHMtNjMuOSAyOC42LTYzLjkgNjMuOSAyOC42IDYzLjggNjMuOSA2My44bTE4OS4yIDBjMzUuMyAwIDYzLjktMjguNiA2My45LTYzLjlTNDgxLjYgMCA0NDYuNCAwYy0zNS4zIDAtNjMuOSAyOC42LTYzLjkgNjMuOXMyOC42IDYzLjggNjMuOSA2My44IiBjbGFzcz0ic3QwIi8+PHBhdGggZD0iTTQ0Ni40IDMxOC4xYzM1LjMgMCA2My45LTI4LjYgNjMuOS02My45cy0yOC42LTYzLjktNjMuOS02My45LTYzLjkgMjguNi02My45IDYzLjkgMjguNiA2My45IDYzLjkgNjMuOSIvPjxwYXRoIGQ9Ik00NDYuNCA1MTJjMzUuMyAwIDYzLjktMjguNiA2My45LTYzLjlzLTI4LjYtNjMuOS02My45LTYzLjktNjMuOSAyOC42LTYzLjkgNjMuOSAyOC42IDYzLjkgNjMuOSA2My45IiBjbGFzcz0ic3QwIi8+PC9zdmc+", + "descriptionMarkdown": "Polls Tailscale **configuration audit** logs (`/logging/configuration` endpoint) into Sentinel using OAuth2 client credentials. **Use this connector for Tailscale Personal (Free) and Standard tier tailnets.** For Premium and Enterprise tailnets that also need network flow logs, install **Tailscale Premium (CCF)** instead.", + "graphQueries": [ + { + "metricName": "Total config events", + "legend": "Tailscale_Configuration_CL", + "baseQuery": "Tailscale_Configuration_CL" + } + ], + "dataTypes": [ + { + "name": "Tailscale_Configuration_CL", + "lastDataReceivedQuery": "[format('{0} | summarize Time=max(TimeGenerated)', 'Tailscale_Configuration_CL')]" + } + ], + "sampleQueries": [ + { + "description": "Recent Tailscale configuration audit events", + "query": "Tailscale_Configuration_CL\n| sort by TimeGenerated desc\n| take 100" + }, + { + "description": "ACL policy file modifications", + "query": "Tailscale_Configuration_CL\n| where Action == 'WRITE' and Target startswith 'acl/'\n| project TimeGenerated, Actor, Action, Target" + }, + { + "description": "New API tokens and OAuth clients created", + "query": "Tailscale_Configuration_CL\n| where Action == 'CREATE' and (Target startswith 'apikey/' or Target startswith 'oauth-client/')\n| project TimeGenerated, Actor, Target" + } + ], + "connectivityCriteria": [ + { + "type": "HasDataConnectors" + } + ], + "availability": { + "status": 1, + "isPreview": true + }, + "permissions": { + "resourceProvider": [ + { + "provider": "Microsoft.OperationalInsights/workspaces", + "permissionsDisplayText": "Read/Write on the workspace", + "providerDisplayName": "Workspace", + "scope": "Workspace", + "requiredPermissions": { + "write": true, + "read": true, + "delete": true + } + } + ] + }, + "instructionSteps": [ + { + "title": "Connect Tailscale", + "description": "Generate an OAuth client at https://login.tailscale.com/admin/settings/oauth with the **Audit Logs - Read** scope. Find your tailnet name on the Keys page.", + "instructions": [ + { + "type": "Textbox", + "parameters": { + "label": "Tailscale tailnet", + "placeholder": "tail-XXXX.ts.net", + "type": "text", + "name": "tailnetName" + } + }, + { + "type": "Textbox", + "parameters": { + "label": "OAuth Client ID", + "placeholder": "k...", + "type": "text", + "name": "clientId" + } + }, + { + "type": "Textbox", + "parameters": { + "label": "OAuth Client Secret", + "placeholder": "tskey-client-...", + "type": "password", + "name": "clientSecret" + } + }, + { + "type": "ConnectionToggleButton", + "parameters": { + "connectLabel": "Connect", + "disconnectLabel": "Disconnect" + } + } + ] + } + ], + "id": "TailscaleCCF" + } + } +} \ No newline at end of file diff --git a/Solutions/Tailscale (CCF)/Data Connectors/TailscaleAuditLogs_ccf/Tailscale_DCR.json b/Solutions/Tailscale (CCF)/Data Connectors/TailscaleAuditLogs_ccf/Tailscale_DCR.json new file mode 100644 index 00000000000..2eddd667b5c --- /dev/null +++ b/Solutions/Tailscale (CCF)/Data Connectors/TailscaleAuditLogs_ccf/Tailscale_DCR.json @@ -0,0 +1,67 @@ +{ + "name": "TailscaleDCR", + "apiVersion": "2021-09-01-preview", + "type": "Microsoft.Insights/dataCollectionRules", + "kind": "WorkspaceTransforms", + "properties": { + "dataCollectionEndpointId": "{{dataCollectionEndpointId}}", + "streamDeclarations": { + "Custom-Tailscale_Configuration_CL": { + "columns": [ + { + "name": "eventTime", + "type": "datetime" + }, + { + "name": "eventGroupID", + "type": "string" + }, + { + "name": "actor", + "type": "dynamic" + }, + { + "name": "action", + "type": "string" + }, + { + "name": "target", + "type": "dynamic" + }, + { + "name": "origin", + "type": "dynamic" + }, + { + "name": "new", + "type": "dynamic" + }, + { + "name": "old", + "type": "dynamic" + } + ] + } + }, + "destinations": { + "logAnalytics": [ + { + "workspaceResourceId": "{{workspaceResourceId}}", + "name": "sentinelWorkspace" + } + ] + }, + "dataFlows": [ + { + "streams": [ + "Custom-Tailscale_Configuration_CL" + ], + "destinations": [ + "sentinelWorkspace" + ], + "transformKql": " source\n | extend TimeGenerated = eventTime\n | extend EventTime = eventTime\n | extend EventGroupID = eventGroupID\n | extend Actor = actor\n | extend Action = action\n | extend Target = target\n | extend Origin = origin\n | extend New = new\n | extend Old = old\n | project TimeGenerated, EventTime, EventGroupID, Actor, Action, Target, Origin, New, Old\n ", + "outputStream": "Custom-Tailscale_Configuration_CL" + } + ] + } +} \ No newline at end of file diff --git a/Solutions/Tailscale (CCF)/Data Connectors/TailscaleAuditLogs_ccf/Tailscale_PollerConfig.json b/Solutions/Tailscale (CCF)/Data Connectors/TailscaleAuditLogs_ccf/Tailscale_PollerConfig.json new file mode 100644 index 00000000000..241b54d2e59 --- /dev/null +++ b/Solutions/Tailscale (CCF)/Data Connectors/TailscaleAuditLogs_ccf/Tailscale_PollerConfig.json @@ -0,0 +1,50 @@ +[ + { + "name": "TailscaleConfigPoller", + "apiVersion": "2023-02-01-preview", + "type": "Microsoft.SecurityInsights/dataConnectors", + "kind": "RestApiPoller", + "properties": { + "connectorDefinitionName": "TailscaleCCF", + "dataType": "Tailscale_Configuration_CL", + "dcrConfig": { + "dataCollectionEndpoint": "[[parameters('dcrConfig').dataCollectionEndpoint]", + "dataCollectionRuleImmutableId": "[[parameters('dcrConfig').dataCollectionRuleImmutableId]", + "streamName": "Custom-Tailscale_Configuration_CL" + }, + "auth": { + "type": "OAuth2", + "ClientId": "[[parameters('clientId')]", + "ClientSecret": "[[parameters('clientSecret')]", + "GrantType": "client_credentials", + "TokenEndpoint": "https://api.tailscale.com/api/v2/oauth/token", + "TokenEndpointHeaders": { + "Content-Type": "application/x-www-form-urlencoded" + }, + "TokenEndpointQueryParameters": { + "grant_type": "client_credentials" + } + }, + "request": { + "apiEndpoint": "[[concat('https://api.tailscale.com/api/v2/tailnet/', parameters('tailnetName'), '/logging/configuration')]", + "httpMethod": "GET", + "queryWindowInMin": 5, + "queryTimeFormat": "yyyy-MM-ddTHH:mm:ssZ", + "startTimeAttributeName": "start", + "endTimeAttributeName": "end", + "rateLimitQps": 1, + "retryCount": 3, + "timeoutInSeconds": 60, + "headers": { + "Accept": "application/json" + } + }, + "response": { + "eventsJsonPaths": [ + "$.logs" + ] + }, + "isActive": true + } + } +] diff --git a/Solutions/Tailscale (CCF)/Data Connectors/TailscaleAuditLogs_ccf/Tailscale_tables.json b/Solutions/Tailscale (CCF)/Data Connectors/TailscaleAuditLogs_ccf/Tailscale_tables.json new file mode 100644 index 00000000000..4b8c5f69af9 --- /dev/null +++ b/Solutions/Tailscale (CCF)/Data Connectors/TailscaleAuditLogs_ccf/Tailscale_tables.json @@ -0,0 +1,51 @@ +[ + { + "name": "Tailscale_Configuration_CL", + "apiVersion": "2022-10-01", + "type": "Microsoft.OperationalInsights/workspaces/tables", + "properties": { + "schema": { + "name": "Tailscale_Configuration_CL", + "columns": [ + { + "name": "TimeGenerated", + "type": "datetime" + }, + { + "name": "EventTime", + "type": "datetime" + }, + { + "name": "EventGroupID", + "type": "string" + }, + { + "name": "Actor", + "type": "dynamic" + }, + { + "name": "Action", + "type": "string" + }, + { + "name": "Target", + "type": "dynamic" + }, + { + "name": "Origin", + "type": "dynamic" + }, + { + "name": "New", + "type": "dynamic" + }, + { + "name": "Old", + "type": "dynamic" + } + ] + }, + "retentionInDays": 90 + } + } +] \ No newline at end of file diff --git a/Solutions/Tailscale (CCF)/Data Connectors/TailscalePremium_ccf/TailscalePremium_ConnectorDefinition.json b/Solutions/Tailscale (CCF)/Data Connectors/TailscalePremium_ccf/TailscalePremium_ConnectorDefinition.json new file mode 100644 index 00000000000..41d58f8cff4 --- /dev/null +++ b/Solutions/Tailscale (CCF)/Data Connectors/TailscalePremium_ccf/TailscalePremium_ConnectorDefinition.json @@ -0,0 +1,119 @@ +{ + "name": "TailscalePremiumCCF", + "apiVersion": "2022-09-01-preview", + "type": "Microsoft.SecurityInsights/dataConnectorDefinitions", + "kind": "Customizable", + "properties": { + "connectorUiConfig": { + "title": "Tailscale Premium (CCF)", + "publisher": "Custom", + "logo": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbDpzcGFjZT0icHJlc2VydmUiIGlkPSJMYXllcl8xIiB4PSIwIiB5PSIwIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIj48c3R5bGU+LnN0MHtvcGFjaXR5Oi4yO2VuYWJsZS1iYWNrZ3JvdW5kOm5ld308L3N0eWxlPjxwYXRoIGQ9Ik02NS42IDEyNy43YzM1LjMgMCA2My45LTI4LjYgNjMuOS02My45UzEwMC45IDAgNjUuNiAwIDEuOCAyOC42IDEuOCA2My45czI4LjYgNjMuOCA2My44IDYzLjgiIGNsYXNzPSJzdDAiLz48cGF0aCBkPSJNNjUuNiAzMTguMWMzNS4zIDAgNjMuOS0yOC42IDYzLjktNjMuOXMtMjguNi02My45LTYzLjktNjMuOVMxLjggMjE5IDEuOCAyNTQuMnMyOC42IDYzLjkgNjMuOCA2My45Ii8+PHBhdGggZD0iTTY1LjYgNTEyYzM1LjMgMCA2My45LTI4LjYgNjMuOS02My45cy0yOC42LTYzLjktNjMuOS02My45LTYzLjggMjguNy02My44IDYzLjlTMzAuNCA1MTIgNjUuNiA1MTIiIGNsYXNzPSJzdDAiLz48cGF0aCBkPSJNMjU3LjIgMzE4LjFjMzUuMyAwIDYzLjktMjguNiA2My45LTYzLjlzLTI4LjYtNjMuOS02My45LTYzLjktNjMuOSAyOC42LTYzLjkgNjMuOSAyOC42IDYzLjkgNjMuOSA2My45bTAgMTkzLjljMzUuMyAwIDYzLjktMjguNiA2My45LTYzLjlzLTI4LjYtNjMuOS02My45LTYzLjktNjMuOSAyOC42LTYzLjkgNjMuOSAyOC42IDYzLjkgNjMuOSA2My45Ii8+PHBhdGggZD0iTTI1Ny4yIDEyNy43YzM1LjMgMCA2My45LTI4LjYgNjMuOS02My45UzI5Mi41IDAgMjU3LjIgMHMtNjMuOSAyOC42LTYzLjkgNjMuOSAyOC42IDYzLjggNjMuOSA2My44bTE4OS4yIDBjMzUuMyAwIDYzLjktMjguNiA2My45LTYzLjlTNDgxLjYgMCA0NDYuNCAwYy0zNS4zIDAtNjMuOSAyOC42LTYzLjkgNjMuOXMyOC42IDYzLjggNjMuOSA2My44IiBjbGFzcz0ic3QwIi8+PHBhdGggZD0iTTQ0Ni40IDMxOC4xYzM1LjMgMCA2My45LTI4LjYgNjMuOS02My45cy0yOC42LTYzLjktNjMuOS02My45LTYzLjkgMjguNi02My45IDYzLjkgMjguNiA2My45IDYzLjkgNjMuOSIvPjxwYXRoIGQ9Ik00NDYuNCA1MTJjMzUuMyAwIDYzLjktMjguNiA2My45LTYzLjlzLTI4LjYtNjMuOS02My45LTYzLjktNjMuOSAyOC42LTYzLjkgNjMuOSAyOC42IDYzLjkgNjMuOSA2My45IiBjbGFzcz0ic3QwIi8+PC9zdmc+", + "descriptionMarkdown": "Polls Tailscale **configuration audit** logs (`/logging/configuration`) **and network flow logs** (`/logging/network`) into Sentinel using OAuth2 client credentials. **Use this connector for Tailscale Premium and Enterprise tier tailnets.** For Personal (Free) and Standard tailnets, install **Tailscale Standard (CCF)** instead.", + "graphQueries": [ + { + "metricName": "Configuration audit events", + "legend": "Tailscale_Configuration_CL", + "baseQuery": "Tailscale_Configuration_CL" + }, + { + "metricName": "Network flow events", + "legend": "Tailscale_Network_CL", + "baseQuery": "Tailscale_Network_CL" + } + ], + "dataTypes": [ + { + "name": "Tailscale_Configuration_CL", + "lastDataReceivedQuery": "[format('{0} | summarize Time=max(TimeGenerated)', 'Tailscale_Configuration_CL')]" + }, + { + "name": "Tailscale_Network_CL", + "lastDataReceivedQuery": "[format('{0} | summarize Time=max(TimeGenerated)', 'Tailscale_Network_CL')]" + } + ], + "sampleQueries": [ + { + "description": "Recent Tailscale configuration audit events", + "query": "Tailscale_Configuration_CL\n| sort by TimeGenerated desc\n| take 100" + }, + { + "description": "Recent Tailscale network flows", + "query": "Tailscale_Network_CL\n| sort by TimeGenerated desc\n| take 100" + }, + { + "description": "Top talkers (by bytes) on the tailnet", + "query": "Tailscale_Network_CL\n| mv-expand t = VirtualTraffic\n| extend src = tostring(t.src), dst = tostring(t.dst), txBytes = tolong(t.txBytes), rxBytes = tolong(t.rxBytes)\n| summarize TotalBytes = sum(txBytes + rxBytes) by src, dst\n| top 25 by TotalBytes" + }, + { + "description": "Exit-node traffic (data leaving the tailnet via an exit node)", + "query": "Tailscale_Network_CL\n| where array_length(ExitTraffic) > 0\n| mv-expand t = ExitTraffic\n| project TimeGenerated, NodeId, SrcNode, ExitDst = tostring(t.dst), Proto = toint(t.proto), TxBytes = tolong(t.txBytes), RxBytes = tolong(t.rxBytes)" + } + ], + "connectivityCriteria": [ + { "type": "HasDataConnectors" } + ], + "availability": { + "status": 1, + "isPreview": true + }, + "permissions": { + "resourceProvider": [ + { + "provider": "Microsoft.OperationalInsights/workspaces", + "permissionsDisplayText": "Read/Write on the workspace", + "providerDisplayName": "Workspace", + "scope": "Workspace", + "requiredPermissions": { + "write": true, + "read": true, + "delete": true + } + } + ] + }, + "instructionSteps": [ + { + "title": "Connect Tailscale (Premium)", + "description": "Generate an OAuth client at https://login.tailscale.com/admin/settings/oauth with the **Audit Logs - Read** and **Network Logs - Read** scopes. Find your tailnet name on the Keys page.", + "instructions": [ + { + "type": "Textbox", + "parameters": { + "label": "Tailscale tailnet", + "placeholder": "tail-XXXX.ts.net", + "type": "text", + "name": "tailnetName" + } + }, + { + "type": "Textbox", + "parameters": { + "label": "OAuth Client ID", + "placeholder": "k...", + "type": "text", + "name": "clientId" + } + }, + { + "type": "Textbox", + "parameters": { + "label": "OAuth Client Secret", + "placeholder": "tskey-client-...", + "type": "password", + "name": "clientSecret" + } + }, + { + "type": "ConnectionToggleButton", + "parameters": { + "connectLabel": "Connect", + "disconnectLabel": "Disconnect" + } + } + ] + } + ], + "id": "TailscalePremiumCCF" + } + } +} diff --git a/Solutions/Tailscale (CCF)/Data Connectors/TailscalePremium_ccf/TailscalePremium_DCR.json b/Solutions/Tailscale (CCF)/Data Connectors/TailscalePremium_ccf/TailscalePremium_DCR.json new file mode 100644 index 00000000000..59c721ead6e --- /dev/null +++ b/Solutions/Tailscale (CCF)/Data Connectors/TailscalePremium_ccf/TailscalePremium_DCR.json @@ -0,0 +1,59 @@ +{ + "name": "TailscalePremiumDCR", + "apiVersion": "2021-09-01-preview", + "type": "Microsoft.Insights/dataCollectionRules", + "kind": "WorkspaceTransforms", + "properties": { + "dataCollectionEndpointId": "{{dataCollectionEndpointId}}", + "streamDeclarations": { + "Custom-Tailscale_Configuration_CL": { + "columns": [ + { "name": "eventTime", "type": "datetime" }, + { "name": "eventGroupID", "type": "string" }, + { "name": "actor", "type": "dynamic" }, + { "name": "action", "type": "string" }, + { "name": "target", "type": "dynamic" }, + { "name": "origin", "type": "dynamic" }, + { "name": "new", "type": "dynamic" }, + { "name": "old", "type": "dynamic" } + ] + }, + "Custom-Tailscale_Network_CL": { + "columns": [ + { "name": "nodeId", "type": "string" }, + { "name": "logged", "type": "datetime" }, + { "name": "start", "type": "datetime" }, + { "name": "end", "type": "datetime" }, + { "name": "srcNode", "type": "dynamic" }, + { "name": "dstNodes", "type": "dynamic" }, + { "name": "virtualTraffic", "type": "dynamic" }, + { "name": "subnetTraffic", "type": "dynamic" }, + { "name": "exitTraffic", "type": "dynamic" }, + { "name": "physicalTraffic", "type": "dynamic" } + ] + } + }, + "destinations": { + "logAnalytics": [ + { + "workspaceResourceId": "{{workspaceResourceId}}", + "name": "sentinelWorkspace" + } + ] + }, + "dataFlows": [ + { + "streams": [ "Custom-Tailscale_Configuration_CL" ], + "destinations": [ "sentinelWorkspace" ], + "transformKql": "source | extend TimeGenerated = eventTime | extend EventTime = eventTime | extend EventGroupID = eventGroupID | extend Actor = actor | extend Action = action | extend Target = target | extend Origin = origin | extend New = new | extend Old = old | project TimeGenerated, EventTime, EventGroupID, Actor, Action, Target, Origin, New, Old", + "outputStream": "Custom-Tailscale_Configuration_CL" + }, + { + "streams": [ "Custom-Tailscale_Network_CL" ], + "destinations": [ "sentinelWorkspace" ], + "transformKql": "source | extend TimeGenerated = logged | extend NodeId = nodeId | extend FlowStart = start | extend FlowEnd = end | extend SrcNode = srcNode | extend DstNodes = dstNodes | extend VirtualTraffic = virtualTraffic | extend SubnetTraffic = subnetTraffic | extend ExitTraffic = exitTraffic | extend PhysicalTraffic = physicalTraffic | project TimeGenerated, NodeId, FlowStart, FlowEnd, SrcNode, DstNodes, VirtualTraffic, SubnetTraffic, ExitTraffic, PhysicalTraffic", + "outputStream": "Custom-Tailscale_Network_CL" + } + ] + } +} diff --git a/Solutions/Tailscale (CCF)/Data Connectors/TailscalePremium_ccf/TailscalePremium_PollerConfig.json b/Solutions/Tailscale (CCF)/Data Connectors/TailscalePremium_ccf/TailscalePremium_PollerConfig.json new file mode 100644 index 00000000000..3234b26713a --- /dev/null +++ b/Solutions/Tailscale (CCF)/Data Connectors/TailscalePremium_ccf/TailscalePremium_PollerConfig.json @@ -0,0 +1,98 @@ +[ + { + "name": "TailscalePremiumConfigPoller", + "apiVersion": "2023-02-01-preview", + "type": "Microsoft.SecurityInsights/dataConnectors", + "kind": "RestApiPoller", + "properties": { + "connectorDefinitionName": "TailscalePremiumCCF", + "dataType": "Tailscale_Configuration_CL", + "dcrConfig": { + "dataCollectionEndpoint": "[[parameters('dcrConfig').dataCollectionEndpoint]", + "dataCollectionRuleImmutableId": "[[parameters('dcrConfig').dataCollectionRuleImmutableId]", + "streamName": "Custom-Tailscale_Configuration_CL" + }, + "auth": { + "type": "OAuth2", + "ClientId": "[[parameters('clientId')]", + "ClientSecret": "[[parameters('clientSecret')]", + "GrantType": "client_credentials", + "TokenEndpoint": "https://api.tailscale.com/api/v2/oauth/token", + "TokenEndpointHeaders": { + "Content-Type": "application/x-www-form-urlencoded" + }, + "TokenEndpointQueryParameters": { + "grant_type": "client_credentials" + } + }, + "request": { + "apiEndpoint": "[[concat('https://api.tailscale.com/api/v2/tailnet/', parameters('tailnetName'), '/logging/configuration')]", + "httpMethod": "GET", + "queryWindowInMin": 5, + "queryTimeFormat": "yyyy-MM-ddTHH:mm:ssZ", + "startTimeAttributeName": "start", + "endTimeAttributeName": "end", + "rateLimitQps": 1, + "retryCount": 3, + "timeoutInSeconds": 60, + "headers": { + "Accept": "application/json" + } + }, + "response": { + "eventsJsonPaths": [ + "$.logs" + ] + }, + "isActive": true + } + }, + { + "name": "TailscalePremiumNetworkPoller", + "apiVersion": "2023-02-01-preview", + "type": "Microsoft.SecurityInsights/dataConnectors", + "kind": "RestApiPoller", + "properties": { + "connectorDefinitionName": "TailscalePremiumCCF", + "dataType": "Tailscale_Network_CL", + "dcrConfig": { + "dataCollectionEndpoint": "[[parameters('dcrConfig').dataCollectionEndpoint]", + "dataCollectionRuleImmutableId": "[[parameters('dcrConfig').dataCollectionRuleImmutableId]", + "streamName": "Custom-Tailscale_Network_CL" + }, + "auth": { + "type": "OAuth2", + "ClientId": "[[parameters('clientId')]", + "ClientSecret": "[[parameters('clientSecret')]", + "GrantType": "client_credentials", + "TokenEndpoint": "https://api.tailscale.com/api/v2/oauth/token", + "TokenEndpointHeaders": { + "Content-Type": "application/x-www-form-urlencoded" + }, + "TokenEndpointQueryParameters": { + "grant_type": "client_credentials" + } + }, + "request": { + "apiEndpoint": "[[concat('https://api.tailscale.com/api/v2/tailnet/', parameters('tailnetName'), '/logging/network')]", + "httpMethod": "GET", + "queryWindowInMin": 5, + "queryTimeFormat": "yyyy-MM-ddTHH:mm:ssZ", + "startTimeAttributeName": "start", + "endTimeAttributeName": "end", + "rateLimitQps": 1, + "retryCount": 3, + "timeoutInSeconds": 60, + "headers": { + "Accept": "application/json" + } + }, + "response": { + "eventsJsonPaths": [ + "$.logs" + ] + }, + "isActive": true + } + } +] diff --git a/Solutions/Tailscale (CCF)/Data Connectors/TailscalePremium_ccf/TailscalePremium_tables.json b/Solutions/Tailscale (CCF)/Data Connectors/TailscalePremium_ccf/TailscalePremium_tables.json new file mode 100644 index 00000000000..d42945375ce --- /dev/null +++ b/Solutions/Tailscale (CCF)/Data Connectors/TailscalePremium_ccf/TailscalePremium_tables.json @@ -0,0 +1,47 @@ +[ + { + "name": "Tailscale_Configuration_CL", + "apiVersion": "2022-10-01", + "type": "Microsoft.OperationalInsights/workspaces/tables", + "properties": { + "schema": { + "name": "Tailscale_Configuration_CL", + "columns": [ + { "name": "TimeGenerated", "type": "datetime" }, + { "name": "EventTime", "type": "datetime" }, + { "name": "EventGroupID", "type": "string" }, + { "name": "Actor", "type": "dynamic" }, + { "name": "Action", "type": "string" }, + { "name": "Target", "type": "dynamic" }, + { "name": "Origin", "type": "dynamic" }, + { "name": "New", "type": "dynamic" }, + { "name": "Old", "type": "dynamic" } + ] + }, + "retentionInDays": 90 + } + }, + { + "name": "Tailscale_Network_CL", + "apiVersion": "2022-10-01", + "type": "Microsoft.OperationalInsights/workspaces/tables", + "properties": { + "schema": { + "name": "Tailscale_Network_CL", + "columns": [ + { "name": "TimeGenerated", "type": "datetime" }, + { "name": "NodeId", "type": "string" }, + { "name": "FlowStart", "type": "datetime" }, + { "name": "FlowEnd", "type": "datetime" }, + { "name": "SrcNode", "type": "dynamic" }, + { "name": "DstNodes", "type": "dynamic" }, + { "name": "VirtualTraffic", "type": "dynamic" }, + { "name": "SubnetTraffic", "type": "dynamic" }, + { "name": "ExitTraffic", "type": "dynamic" }, + { "name": "PhysicalTraffic", "type": "dynamic" } + ] + }, + "retentionInDays": 90 + } + } +] diff --git a/Solutions/Tailscale (CCF)/Data/Solution_Tailscale.json b/Solutions/Tailscale (CCF)/Data/Solution_Tailscale.json new file mode 100644 index 00000000000..e90cbcef696 --- /dev/null +++ b/Solutions/Tailscale (CCF)/Data/Solution_Tailscale.json @@ -0,0 +1,22 @@ +{ + "Name": "Tailscale (CCF)", + "Author": "noodlemctwoodle", + "Logo": "", + "Description": "The [Tailscale](https://tailscale.com/) solution for Microsoft Sentinel ingests Tailscale telemetry via OAuth2-secured APIs.\n\n**Data connectors in this solution (install one based on your Tailscale plan):**\n- **Tailscale Standard (CCF)** — Configuration audit logs (`/logging/configuration`). Use on **Personal (Free) and Standard** tailnets. Included in this release.\n- **Tailscale Premium (CCF)** — Configuration audit + network flow logs (`/logging/configuration` + `/logging/network`). Use on **Premium and Enterprise** tailnets. *Planned for a future release.*\n\n**Underlying technology:** Microsoft Sentinel Codeless Connector Framework (CCF) with OAuth2 client-credentials auth.\n\n**Pre-requisites:**\n1. Sign in to [Tailscale OAuth settings](https://login.tailscale.com/admin/settings/oauth)\n2. Create an OAuth client with the **Audit Logs - Read** scope (Standard connector). For the Premium connector also tick **Network Logs - Read**.\n3. Copy the client ID and client secret (secret shown once)\n4. Note your tailnet name from the [Keys page](https://login.tailscale.com/admin/settings/keys)", + "BasePath": "C:\\GitHub\\Azure-Sentinel\\Solutions\\Tailscale (CCF)", + "Version": "3.0.1", + "TemplateSpec": true, + "Is1PConnector": false, + "Data Connectors": [ + "Data Connectors/TailscaleAuditLogs_ccf/Tailscale_ConnectorDefinition.json", + "Data Connectors/TailscalePremium_ccf/TailscalePremium_ConnectorDefinition.json" + ], + "Analytic Rules": [ + "Analytic Rules/TailscaleNewAPIaccesstokenorOAuthclientcreated.yaml", + "Analytic Rules/TailscalePolicyfileACLmodified.yaml", + "Analytic Rules/TailscaleAuthkeycreated.yaml", + "Analytic Rules/TailscaleExitnodeadvertisedorapproved.yaml", + "Analytic Rules/TailscaleMasscredentialrevocationinshortwindow.yaml" + ], + "Metadata": "SolutionMetadata.json" +} diff --git a/Solutions/Tailscale (CCF)/Package/3.0.1.zip b/Solutions/Tailscale (CCF)/Package/3.0.1.zip new file mode 100644 index 0000000000000000000000000000000000000000..38245235a0ec7fb220f34daca7cffcf2e7f5135f GIT binary patch literal 13726 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 literal 0 HcmV?d00001 diff --git a/Solutions/Tailscale (CCF)/Package/createUiDefinition.json b/Solutions/Tailscale (CCF)/Package/createUiDefinition.json new file mode 100644 index 00000000000..98f301d0624 --- /dev/null +++ b/Solutions/Tailscale (CCF)/Package/createUiDefinition.json @@ -0,0 +1,200 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/0.1.2-preview/CreateUIDefinition.MultiVm.json#", + "handler": "Microsoft.Azure.CreateUIDef", + "version": "0.1.2-preview", + "parameters": { + "config": { + "isWizard": false, + "basics": { + "description": "\n\n**Note:** Please refer to the following before installing the solution: \n\n• Review the solution [Release Notes](https://github.com/Azure/Azure-Sentinel/tree/master/Solutions/Tailscale%20%28CCF%29/ReleaseNotes.md)\n\n • There may be [known issues](https://aka.ms/sentinelsolutionsknownissues) pertaining to this Solution, please refer to them before installing.\n\nThe [Tailscale](https://tailscale.com/) solution for Microsoft Sentinel ingests Tailscale telemetry via OAuth2-secured APIs.\n\n**Data connectors in this solution (install one based on your Tailscale plan):**\n- **Tailscale Standard (CCF)** — Configuration audit logs (`/logging/configuration`). Use on **Personal (Free) and Standard** tailnets. Included in this release.\n- **Tailscale Premium (CCF)** — Configuration audit + network flow logs (`/logging/configuration` + `/logging/network`). Use on **Premium and Enterprise** tailnets. *Planned for a future release.*\n\n**Underlying technology:** Microsoft Sentinel Codeless Connector Framework (CCF) with OAuth2 client-credentials auth.\n\n**Pre-requisites:**\n1. Sign in to [Tailscale OAuth settings](https://login.tailscale.com/admin/settings/oauth)\n2. Create an OAuth client with the **Audit Logs - Read** scope (Standard connector). For the Premium connector also tick **Network Logs - Read**.\n3. Copy the client ID and client secret (secret shown once)\n4. Note your tailnet name from the [Keys page](https://login.tailscale.com/admin/settings/keys)\n\n**Data Connectors:** 2, **Analytic Rules:** 5\n\n[Learn more about Microsoft Sentinel](https://aka.ms/azuresentinel) | [Learn more about Solutions](https://aka.ms/azuresentinelsolutionsdoc)", + "subscription": { + "resourceProviders": [ + "Microsoft.OperationsManagement/solutions", + "Microsoft.OperationalInsights/workspaces/providers/alertRules", + "Microsoft.Insights/workbooks", + "Microsoft.Logic/workflows" + ] + }, + "location": { + "metadata": { + "hidden": "Hiding location, we get it from the log analytics workspace" + }, + "visible": false + }, + "resourceGroup": { + "allowExisting": true + } + } + }, + "basics": [ + { + "name": "getLAWorkspace", + "type": "Microsoft.Solutions.ArmApiControl", + "toolTip": "This filters by workspaces that exist in the Resource Group selected", + "condition": "[greater(length(resourceGroup().name),0)]", + "request": { + "method": "GET", + "path": "[concat(subscription().id,'/providers/Microsoft.OperationalInsights/workspaces?api-version=2020-08-01')]" + } + }, + { + "name": "workspace", + "type": "Microsoft.Common.DropDown", + "label": "Workspace", + "placeholder": "Select a workspace", + "toolTip": "This dropdown will list only workspace that exists in the Resource Group selected", + "constraints": { + "allowedValues": "[map(filter(basics('getLAWorkspace').value, (filter) => contains(toLower(filter.id), toLower(resourceGroup().name))), (item) => parse(concat('{\"label\":\"', item.name, '\",\"value\":\"', item.name, '\"}')))]", + "required": true + }, + "visible": true + } + ], + "steps": [ + { + "name": "dataconnectors", + "label": "Data Connectors", + "bladeTitle": "Data Connectors", + "elements": [ + { + "name": "dataconnectors1-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "This Solution installs the data connector for Tailscale Standard (CCF). You can get Tailscale Standard (CCF) data in your Microsoft Sentinel workspace. After installing the solution, configure and enable this data connector by following guidance in Manage solution view." + } + }, + { + "name": "dataconnectors-link1", + "type": "Microsoft.Common.TextBlock", + "options": { + "link": { + "label": "Learn more about connecting data sources", + "uri": "https://docs.microsoft.com/azure/sentinel/connect-data-sources" + } + } + }, + { + "name": "dataconnectors2-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "This Solution installs the data connector for Tailscale Premium (CCF). You can get Tailscale Premium (CCF) data in your Microsoft Sentinel workspace. After installing the solution, configure and enable this data connector by following guidance in Manage solution view." + } + }, + { + "name": "dataconnectors-link2", + "type": "Microsoft.Common.TextBlock", + "options": { + "link": { + "label": "Learn more about connecting data sources", + "uri": "https://docs.microsoft.com/azure/sentinel/connect-data-sources" + } + } + } + ] + }, + { + "name": "analytics", + "label": "Analytics", + "subLabel": { + "preValidation": "Configure the analytics", + "postValidation": "Done" + }, + "bladeTitle": "Analytics", + "elements": [ + { + "name": "analytics-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "This solution installs the following analytic rule templates. After installing the solution, create and enable analytic rules in Manage solution view." + } + }, + { + "name": "analytics-link", + "type": "Microsoft.Common.TextBlock", + "options": { + "link": { + "label": "Learn more", + "uri": "https://docs.microsoft.com/azure/sentinel/tutorial-detect-threats-custom?WT.mc_id=Portal-Microsoft_Azure_CreateUIDef" + } + } + }, + { + "name": "analytic1", + "type": "Microsoft.Common.Section", + "label": "Tailscale: New API access token or OAuth client created", + "elements": [ + { + "name": "analytic1-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "A new API access token or OAuth client was created in the tailnet. These grant programmatic access — verify the actor and intent." + } + } + ] + }, + { + "name": "analytic2", + "type": "Microsoft.Common.Section", + "label": "Tailscale: Policy file (ACL) modified", + "elements": [ + { + "name": "analytic2-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "The tailnet ACL/policy file was modified. Review the diff — incorrect ACLs can silently expand blast radius across the tailnet." + } + } + ] + }, + { + "name": "analytic3", + "type": "Microsoft.Common.Section", + "label": "Tailscale: Auth key created", + "elements": [ + { + "name": "analytic3-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "A new auth key was generated. Auth keys allow unattended device enrollment into the tailnet — confirm it was expected and revoke if not." + } + } + ] + }, + { + "name": "analytic4", + "type": "Microsoft.Common.Section", + "label": "Tailscale: Exit node advertised or approved", + "elements": [ + { + "name": "analytic4-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "A device started advertising itself as an exit node, or an admin approved one. Validate the device and operator — rogue exit nodes can intercept tailnet egress." + } + } + ] + }, + { + "name": "analytic5", + "type": "Microsoft.Common.Section", + "label": "Tailscale: Mass credential revocation in short window", + "elements": [ + { + "name": "analytic5-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "Five or more API keys, OAuth clients, or auth keys were revoked/deleted within an hour. May be routine rotation, but also a typical cleanup pattern after credential compromise." + } + } + ] + } + ] + } + ], + "outputs": { + "workspace-location": "[first(map(filter(basics('getLAWorkspace').value, (filter) => and(contains(toLower(filter.id), toLower(resourceGroup().name)),equals(filter.name,basics('workspace')))), (item) => item.location))]", + "location": "[location()]", + "workspace": "[basics('workspace')]" + } + } +} diff --git a/Solutions/Tailscale (CCF)/Package/mainTemplate.json b/Solutions/Tailscale (CCF)/Package/mainTemplate.json new file mode 100644 index 00000000000..41c52cc1c5b --- /dev/null +++ b/Solutions/Tailscale (CCF)/Package/mainTemplate.json @@ -0,0 +1,2153 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "author": "noodlemctwoodle", + "comments": "Solution template for Tailscale (CCF)" + }, + "parameters": { + "location": { + "type": "string", + "minLength": 1, + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Not used, but needed to pass arm-ttk test `Location-Should-Not-Be-Hardcoded`. We instead use the `workspace-location` which is derived from the LA workspace" + } + }, + "workspace-location": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "[concat('Region to deploy solution resources -- separate from location selection',parameters('location'))]" + } + }, + "workspace": { + "defaultValue": "", + "type": "string", + "metadata": { + "description": "Workspace name for Log Analytics where Microsoft Sentinel is setup" + } + }, + "resourceGroupName": { + "type": "string", + "defaultValue": "[resourceGroup().name]", + "metadata": { + "description": "resource group name where Microsoft Sentinel is setup" + } + }, + "subscription": { + "type": "string", + "defaultValue": "[last(split(subscription().id, '/'))]", + "metadata": { + "description": "subscription id where Microsoft Sentinel is setup" + } + } + }, + "variables": { + "_solutionName": "Tailscale (CCF)", + "_solutionVersion": "3.0.1", + "solutionId": "noodlemctwoodle.TailscaleCCF", + "_solutionId": "[variables('solutionId')]", + "workspaceResourceId": "[resourceId('microsoft.OperationalInsights/Workspaces', parameters('workspace'))]", + "dataConnectorCCPVersion": "3.0.1", + "_dataConnectorContentIdConnectorDefinition1": "TailscaleCCF", + "dataConnectorTemplateNameConnectorDefinition1": "[concat(parameters('workspace'),'-dc-',uniquestring(variables('_dataConnectorContentIdConnectorDefinition1')))]", + "_dataConnectorContentIdConnections1": "TailscaleCCFConnections", + "dataConnectorTemplateNameConnections1": "[concat(parameters('workspace'),'-dc-',uniquestring(variables('_dataConnectorContentIdConnections1')))]", + "dataCollectionEndpointId1": "[concat('/subscriptions/',parameters('subscription'),'/resourceGroups/',parameters('resourceGroupName'),'/providers/Microsoft.Insights/dataCollectionEndpoints/',parameters('workspace'))]", + "_dataConnectorContentIdConnectorDefinition2": "TailscalePremiumCCF", + "dataConnectorTemplateNameConnectorDefinition2": "[concat(parameters('workspace'),'-dc-',uniquestring(variables('_dataConnectorContentIdConnectorDefinition2')))]", + "_dataConnectorContentIdConnections2": "TailscalePremiumCCFConnections", + "dataConnectorTemplateNameConnections2": "[concat(parameters('workspace'),'-dc-',uniquestring(variables('_dataConnectorContentIdConnections2')))]", + "dataCollectionEndpointId2": "[concat('/subscriptions/',parameters('subscription'),'/resourceGroups/',parameters('resourceGroupName'),'/providers/Microsoft.Insights/dataCollectionEndpoints/',parameters('workspace'))]", + "analyticRuleObject1": { + "analyticRuleVersion1": "1.0.0", + "_analyticRulecontentId1": "668b43fd-cf28-961a-85af-957850df5027", + "analyticRuleId1": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', '668b43fd-cf28-961a-85af-957850df5027')]", + "analyticRuleTemplateSpecName1": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('668b43fd-cf28-961a-85af-957850df5027')))]", + "_analyticRulecontentProductId1": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','668b43fd-cf28-961a-85af-957850df5027','-', '1.0.0')))]" + }, + "analyticRuleObject2": { + "analyticRuleVersion2": "1.0.0", + "_analyticRulecontentId2": "1e7249c2-1a9d-05fd-45cb-c859eef5b8ae", + "analyticRuleId2": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', '1e7249c2-1a9d-05fd-45cb-c859eef5b8ae')]", + "analyticRuleTemplateSpecName2": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('1e7249c2-1a9d-05fd-45cb-c859eef5b8ae')))]", + "_analyticRulecontentProductId2": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','1e7249c2-1a9d-05fd-45cb-c859eef5b8ae','-', '1.0.0')))]" + }, + "analyticRuleObject3": { + "analyticRuleVersion3": "1.0.0", + "_analyticRulecontentId3": "6b052c8d-5de8-eab0-1956-69a297765a32", + "analyticRuleId3": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', '6b052c8d-5de8-eab0-1956-69a297765a32')]", + "analyticRuleTemplateSpecName3": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('6b052c8d-5de8-eab0-1956-69a297765a32')))]", + "_analyticRulecontentProductId3": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','6b052c8d-5de8-eab0-1956-69a297765a32','-', '1.0.0')))]" + }, + "analyticRuleObject4": { + "analyticRuleVersion4": "1.0.0", + "_analyticRulecontentId4": "f42f2906-c8e6-23d0-e48c-0620e50d5510", + "analyticRuleId4": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', 'f42f2906-c8e6-23d0-e48c-0620e50d5510')]", + "analyticRuleTemplateSpecName4": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('f42f2906-c8e6-23d0-e48c-0620e50d5510')))]", + "_analyticRulecontentProductId4": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','f42f2906-c8e6-23d0-e48c-0620e50d5510','-', '1.0.0')))]" + }, + "analyticRuleObject5": { + "analyticRuleVersion5": "1.0.0", + "_analyticRulecontentId5": "f817e2fa-6fa0-fc25-5369-cef9b58771af", + "analyticRuleId5": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', 'f817e2fa-6fa0-fc25-5369-cef9b58771af')]", + "analyticRuleTemplateSpecName5": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('f817e2fa-6fa0-fc25-5369-cef9b58771af')))]", + "_analyticRulecontentProductId5": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','f817e2fa-6fa0-fc25-5369-cef9b58771af','-', '1.0.0')))]" + }, + "_solutioncontentProductId": "[concat(take(variables('_solutionId'),50),'-','sl','-', uniqueString(concat(variables('_solutionId'),'-','Solution','-',variables('_solutionId'),'-', variables('_solutionVersion'))))]" + }, + "resources": [ + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/', variables('dataConnectorTemplateNameConnectorDefinition1'), variables('dataConnectorCCPVersion'))]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "contentId": "[variables('_dataConnectorContentIdConnectorDefinition1')]", + "displayName": "Tailscale Standard (CCF)", + "contentKind": "DataConnector", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('dataConnectorCCPVersion')]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',variables('_dataConnectorContentIdConnectorDefinition1'))]", + "apiVersion": "2022-09-01-preview", + "type": "Microsoft.OperationalInsights/workspaces/providers/dataConnectorDefinitions", + "location": "[parameters('workspace-location')]", + "kind": "Customizable", + "properties": { + "connectorUiConfig": { + "title": "Tailscale Standard (CCF)", + "publisher": "Custom", + "logo": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbDpzcGFjZT0icHJlc2VydmUiIGlkPSJMYXllcl8xIiB4PSIwIiB5PSIwIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIj48c3R5bGU+LnN0MHtvcGFjaXR5Oi4yO2VuYWJsZS1iYWNrZ3JvdW5kOm5ld308L3N0eWxlPjxwYXRoIGQ9Ik02NS42IDEyNy43YzM1LjMgMCA2My45LTI4LjYgNjMuOS02My45UzEwMC45IDAgNjUuNiAwIDEuOCAyOC42IDEuOCA2My45czI4LjYgNjMuOCA2My44IDYzLjgiIGNsYXNzPSJzdDAiLz48cGF0aCBkPSJNNjUuNiAzMTguMWMzNS4zIDAgNjMuOS0yOC42IDYzLjktNjMuOXMtMjguNi02My45LTYzLjktNjMuOVMxLjggMjE5IDEuOCAyNTQuMnMyOC42IDYzLjkgNjMuOCA2My45Ii8+PHBhdGggZD0iTTY1LjYgNTEyYzM1LjMgMCA2My45LTI4LjYgNjMuOS02My45cy0yOC42LTYzLjktNjMuOS02My45LTYzLjggMjguNy02My44IDYzLjlTMzAuNCA1MTIgNjUuNiA1MTIiIGNsYXNzPSJzdDAiLz48cGF0aCBkPSJNMjU3LjIgMzE4LjFjMzUuMyAwIDYzLjktMjguNiA2My45LTYzLjlzLTI4LjYtNjMuOS02My45LTYzLjktNjMuOSAyOC42LTYzLjkgNjMuOSAyOC42IDYzLjkgNjMuOSA2My45bTAgMTkzLjljMzUuMyAwIDYzLjktMjguNiA2My45LTYzLjlzLTI4LjYtNjMuOS02My45LTYzLjktNjMuOSAyOC42LTYzLjkgNjMuOSAyOC42IDYzLjkgNjMuOSA2My45Ii8+PHBhdGggZD0iTTI1Ny4yIDEyNy43YzM1LjMgMCA2My45LTI4LjYgNjMuOS02My45UzI5Mi41IDAgMjU3LjIgMHMtNjMuOSAyOC42LTYzLjkgNjMuOSAyOC42IDYzLjggNjMuOSA2My44bTE4OS4yIDBjMzUuMyAwIDYzLjktMjguNiA2My45LTYzLjlTNDgxLjYgMCA0NDYuNCAwYy0zNS4zIDAtNjMuOSAyOC42LTYzLjkgNjMuOXMyOC42IDYzLjggNjMuOSA2My44IiBjbGFzcz0ic3QwIi8+PHBhdGggZD0iTTQ0Ni40IDMxOC4xYzM1LjMgMCA2My45LTI4LjYgNjMuOS02My45cy0yOC42LTYzLjktNjMuOS02My45LTYzLjkgMjguNi02My45IDYzLjkgMjguNiA2My45IDYzLjkgNjMuOSIvPjxwYXRoIGQ9Ik00NDYuNCA1MTJjMzUuMyAwIDYzLjktMjguNiA2My45LTYzLjlzLTI4LjYtNjMuOS02My45LTYzLjktNjMuOSAyOC42LTYzLjkgNjMuOSAyOC42IDYzLjkgNjMuOSA2My45IiBjbGFzcz0ic3QwIi8+PC9zdmc+", + "descriptionMarkdown": "Polls Tailscale **configuration audit** logs (`/logging/configuration` endpoint) into Sentinel using OAuth2 client credentials. **Use this connector for Tailscale Personal (Free) and Standard tier tailnets.** For Premium and Enterprise tailnets that also need network flow logs, install **Tailscale Premium (CCF)** instead.", + "graphQueries": [ + { + "metricName": "Total config events", + "legend": "Tailscale_Configuration_CL", + "baseQuery": "Tailscale_Configuration_CL" + } + ], + "dataTypes": [ + { + "name": "Tailscale_Configuration_CL", + "lastDataReceivedQuery": "[format('{0} | summarize Time=max(TimeGenerated)', 'Tailscale_Configuration_CL')]" + } + ], + "sampleQueries": [ + { + "description": "Recent Tailscale configuration audit events", + "query": "Tailscale_Configuration_CL\n| sort by TimeGenerated desc\n| take 100" + }, + { + "description": "ACL policy file modifications", + "query": "Tailscale_Configuration_CL\n| where Action == 'WRITE' and Target startswith 'acl/'\n| project TimeGenerated, Actor, Action, Target" + }, + { + "description": "New API tokens and OAuth clients created", + "query": "Tailscale_Configuration_CL\n| where Action == 'CREATE' and (Target startswith 'apikey/' or Target startswith 'oauth-client/')\n| project TimeGenerated, Actor, Target" + } + ], + "connectivityCriteria": [ + { + "type": "HasDataConnectors" + } + ], + "availability": { + "status": 1, + "isPreview": true + }, + "permissions": { + "resourceProvider": [ + { + "provider": "Microsoft.OperationalInsights/workspaces", + "permissionsDisplayText": "Read/Write on the workspace", + "providerDisplayName": "Workspace", + "scope": "Workspace", + "requiredPermissions": { + "write": true, + "read": true, + "delete": true + } + } + ] + }, + "instructionSteps": [ + { + "title": "Connect Tailscale", + "description": "Generate an OAuth client at https://login.tailscale.com/admin/settings/oauth with the **Audit Logs - Read** scope. Find your tailnet name on the Keys page.", + "instructions": [ + { + "type": "Textbox", + "parameters": { + "label": "Tailscale tailnet", + "placeholder": "tail-XXXX.ts.net", + "type": "text", + "name": "tailnetName" + } + }, + { + "type": "Textbox", + "parameters": { + "label": "OAuth Client ID", + "placeholder": "k...", + "type": "text", + "name": "clientId" + } + }, + { + "type": "Textbox", + "parameters": { + "label": "OAuth Client Secret", + "placeholder": "tskey-client-...", + "type": "password", + "name": "clientSecret" + } + }, + { + "type": "ConnectionToggleButton", + "parameters": { + "connectLabel": "Connect", + "disconnectLabel": "Disconnect" + } + } + ] + } + ], + "id": "TailscaleCCF" + } + } + }, + { + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('DataConnector-', variables('_dataConnectorContentIdConnectorDefinition1')))]", + "apiVersion": "2022-01-01-preview", + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "properties": { + "parentId": "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/dataConnectorDefinitions', variables('_dataConnectorContentIdConnectorDefinition1'))]", + "contentId": "[variables('_dataConnectorContentIdConnectorDefinition1')]", + "kind": "DataConnector", + "version": "[variables('dataConnectorCCPVersion')]", + "source": { + "sourceId": "[variables('_solutionId')]", + "name": "[variables('_solutionName')]", + "kind": "Solution" + }, + "author": { + "name": "noodlemctwoodle" + }, + "support": { + "name": "Community", + "tier": "Community", + "link": "https://github.com/Azure/Azure-Sentinel/issues" + }, + "dependencies": { + "criteria": [ + { + "version": "[variables('dataConnectorCCPVersion')]", + "contentId": "[variables('_dataConnectorContentIdConnections1')]", + "kind": "ResourcesDataConnector" + } + ] + } + } + }, + { + "name": "TailscaleDCR", + "apiVersion": "2022-06-01", + "type": "Microsoft.Insights/dataCollectionRules", + "location": "[parameters('workspace-location')]", + "kind": "WorkspaceTransforms", + "properties": { + "dataCollectionEndpointId": "[variables('dataCollectionEndpointId1')]", + "streamDeclarations": { + "Custom-Tailscale_Configuration_CL": { + "columns": [ + { + "name": "eventTime", + "type": "datetime" + }, + { + "name": "eventGroupID", + "type": "string" + }, + { + "name": "actor", + "type": "dynamic" + }, + { + "name": "action", + "type": "string" + }, + { + "name": "target", + "type": "dynamic" + }, + { + "name": "origin", + "type": "dynamic" + }, + { + "name": "new", + "type": "dynamic" + }, + { + "name": "old", + "type": "dynamic" + } + ] + } + }, + "destinations": { + "logAnalytics": [ + { + "workspaceResourceId": "[variables('workspaceResourceId')]", + "name": "sentinelWorkspace" + } + ] + }, + "dataFlows": [ + { + "streams": [ + "Custom-Tailscale_Configuration_CL" + ], + "destinations": [ + "sentinelWorkspace" + ], + "transformKql": " source\n | extend TimeGenerated = eventTime\n | extend EventTime = eventTime\n | extend EventGroupID = eventGroupID\n | extend Actor = actor\n | extend Action = action\n | extend Target = target\n | extend Origin = origin\n | extend New = new\n | extend Old = old\n | project TimeGenerated, EventTime, EventGroupID, Actor, Action, Target, Origin, New, Old\n ", + "outputStream": "Custom-Tailscale_Configuration_CL" + } + ] + } + }, + { + "name": "Tailscale_Configuration_CL", + "apiVersion": "2022-10-01", + "type": "Microsoft.OperationalInsights/workspaces/tables", + "location": "[parameters('workspace-location')]", + "kind": null, + "properties": { + "schema": { + "name": "Tailscale_Configuration_CL", + "columns": [ + { + "name": "TimeGenerated", + "type": "datetime" + }, + { + "name": "EventTime", + "type": "datetime" + }, + { + "name": "EventGroupID", + "type": "string" + }, + { + "name": "Actor", + "type": "dynamic" + }, + { + "name": "Action", + "type": "string" + }, + { + "name": "Target", + "type": "dynamic" + }, + { + "name": "Origin", + "type": "dynamic" + }, + { + "name": "New", + "type": "dynamic" + }, + { + "name": "Old", + "type": "dynamic" + } + ] + }, + "retentionInDays": 90 + } + }, + { + "name": "Tailscale_Network_CL", + "apiVersion": "2022-10-01", + "type": "Microsoft.OperationalInsights/workspaces/tables", + "location": "[parameters('workspace-location')]", + "kind": null, + "properties": { + "schema": { + "name": "Tailscale_Network_CL", + "columns": [ + { + "name": "TimeGenerated", + "type": "datetime" + }, + { + "name": "NodeId", + "type": "string" + }, + { + "name": "FlowStart", + "type": "datetime" + }, + { + "name": "FlowEnd", + "type": "datetime" + }, + { + "name": "SrcNode", + "type": "dynamic" + }, + { + "name": "DstNodes", + "type": "dynamic" + }, + { + "name": "VirtualTraffic", + "type": "dynamic" + }, + { + "name": "SubnetTraffic", + "type": "dynamic" + }, + { + "name": "ExitTraffic", + "type": "dynamic" + }, + { + "name": "PhysicalTraffic", + "type": "dynamic" + } + ] + }, + "retentionInDays": 90 + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "contentProductId": "[concat(take(variables('_solutionId'), 50),'-','dc','-', uniqueString(concat(variables('_solutionId'),'-','DataConnector','-',variables('_dataConnectorContentIdConnectorDefinition1'),'-', variables('dataConnectorCCPVersion'))))]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "version": "[variables('dataConnectorCCPVersion')]" + } + }, + { + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',variables('_dataConnectorContentIdConnectorDefinition1'))]", + "apiVersion": "2022-09-01-preview", + "type": "Microsoft.OperationalInsights/workspaces/providers/dataConnectorDefinitions", + "location": "[parameters('workspace-location')]", + "kind": "Customizable", + "properties": { + "connectorUiConfig": { + "title": "Tailscale Standard (CCF)", + "publisher": "Custom", + "logo": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbDpzcGFjZT0icHJlc2VydmUiIGlkPSJMYXllcl8xIiB4PSIwIiB5PSIwIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIj48c3R5bGU+LnN0MHtvcGFjaXR5Oi4yO2VuYWJsZS1iYWNrZ3JvdW5kOm5ld308L3N0eWxlPjxwYXRoIGQ9Ik02NS42IDEyNy43YzM1LjMgMCA2My45LTI4LjYgNjMuOS02My45UzEwMC45IDAgNjUuNiAwIDEuOCAyOC42IDEuOCA2My45czI4LjYgNjMuOCA2My44IDYzLjgiIGNsYXNzPSJzdDAiLz48cGF0aCBkPSJNNjUuNiAzMTguMWMzNS4zIDAgNjMuOS0yOC42IDYzLjktNjMuOXMtMjguNi02My45LTYzLjktNjMuOVMxLjggMjE5IDEuOCAyNTQuMnMyOC42IDYzLjkgNjMuOCA2My45Ii8+PHBhdGggZD0iTTY1LjYgNTEyYzM1LjMgMCA2My45LTI4LjYgNjMuOS02My45cy0yOC42LTYzLjktNjMuOS02My45LTYzLjggMjguNy02My44IDYzLjlTMzAuNCA1MTIgNjUuNiA1MTIiIGNsYXNzPSJzdDAiLz48cGF0aCBkPSJNMjU3LjIgMzE4LjFjMzUuMyAwIDYzLjktMjguNiA2My45LTYzLjlzLTI4LjYtNjMuOS02My45LTYzLjktNjMuOSAyOC42LTYzLjkgNjMuOSAyOC42IDYzLjkgNjMuOSA2My45bTAgMTkzLjljMzUuMyAwIDYzLjktMjguNiA2My45LTYzLjlzLTI4LjYtNjMuOS02My45LTYzLjktNjMuOSAyOC42LTYzLjkgNjMuOSAyOC42IDYzLjkgNjMuOSA2My45Ii8+PHBhdGggZD0iTTI1Ny4yIDEyNy43YzM1LjMgMCA2My45LTI4LjYgNjMuOS02My45UzI5Mi41IDAgMjU3LjIgMHMtNjMuOSAyOC42LTYzLjkgNjMuOSAyOC42IDYzLjggNjMuOSA2My44bTE4OS4yIDBjMzUuMyAwIDYzLjktMjguNiA2My45LTYzLjlTNDgxLjYgMCA0NDYuNCAwYy0zNS4zIDAtNjMuOSAyOC42LTYzLjkgNjMuOXMyOC42IDYzLjggNjMuOSA2My44IiBjbGFzcz0ic3QwIi8+PHBhdGggZD0iTTQ0Ni40IDMxOC4xYzM1LjMgMCA2My45LTI4LjYgNjMuOS02My45cy0yOC42LTYzLjktNjMuOS02My45LTYzLjkgMjguNi02My45IDYzLjkgMjguNiA2My45IDYzLjkgNjMuOSIvPjxwYXRoIGQ9Ik00NDYuNCA1MTJjMzUuMyAwIDYzLjktMjguNiA2My45LTYzLjlzLTI4LjYtNjMuOS02My45LTYzLjktNjMuOSAyOC42LTYzLjkgNjMuOSAyOC42IDYzLjkgNjMuOSA2My45IiBjbGFzcz0ic3QwIi8+PC9zdmc+", + "descriptionMarkdown": "Polls Tailscale **configuration audit** logs (`/logging/configuration` endpoint) into Sentinel using OAuth2 client credentials. **Use this connector for Tailscale Personal (Free) and Standard tier tailnets.** For Premium and Enterprise tailnets that also need network flow logs, install **Tailscale Premium (CCF)** instead.", + "graphQueries": [ + { + "metricName": "Total config events", + "legend": "Tailscale_Configuration_CL", + "baseQuery": "Tailscale_Configuration_CL" + } + ], + "dataTypes": [ + { + "name": "Tailscale_Configuration_CL", + "lastDataReceivedQuery": "[format('{0} | summarize Time=max(TimeGenerated)', 'Tailscale_Configuration_CL')]" + } + ], + "sampleQueries": [ + { + "description": "Recent Tailscale configuration audit events", + "query": "Tailscale_Configuration_CL\n| sort by TimeGenerated desc\n| take 100" + }, + { + "description": "ACL policy file modifications", + "query": "Tailscale_Configuration_CL\n| where Action == 'WRITE' and Target startswith 'acl/'\n| project TimeGenerated, Actor, Action, Target" + }, + { + "description": "New API tokens and OAuth clients created", + "query": "Tailscale_Configuration_CL\n| where Action == 'CREATE' and (Target startswith 'apikey/' or Target startswith 'oauth-client/')\n| project TimeGenerated, Actor, Target" + } + ], + "connectivityCriteria": [ + { + "type": "HasDataConnectors" + } + ], + "availability": { + "status": 1, + "isPreview": true + }, + "permissions": { + "resourceProvider": [ + { + "provider": "Microsoft.OperationalInsights/workspaces", + "permissionsDisplayText": "Read/Write on the workspace", + "providerDisplayName": "Workspace", + "scope": "Workspace", + "requiredPermissions": { + "write": true, + "read": true, + "delete": true + } + } + ] + }, + "instructionSteps": [ + { + "title": "Connect Tailscale", + "description": "Generate an OAuth client at https://login.tailscale.com/admin/settings/oauth with the **Audit Logs - Read** scope. Find your tailnet name on the Keys page.", + "instructions": [ + { + "type": "Textbox", + "parameters": { + "label": "Tailscale tailnet", + "placeholder": "tail-XXXX.ts.net", + "type": "text", + "name": "tailnetName" + } + }, + { + "type": "Textbox", + "parameters": { + "label": "OAuth Client ID", + "placeholder": "k...", + "type": "text", + "name": "clientId" + } + }, + { + "type": "Textbox", + "parameters": { + "label": "OAuth Client Secret", + "placeholder": "tskey-client-...", + "type": "password", + "name": "clientSecret" + } + }, + { + "type": "ConnectionToggleButton", + "parameters": { + "connectLabel": "Connect", + "disconnectLabel": "Disconnect" + } + } + ] + } + ], + "id": "TailscaleCCF" + } + } + }, + { + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('DataConnector-', variables('_dataConnectorContentIdConnectorDefinition1')))]", + "apiVersion": "2022-01-01-preview", + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "properties": { + "parentId": "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/dataConnectorDefinitions', variables('_dataConnectorContentIdConnectorDefinition1'))]", + "contentId": "[variables('_dataConnectorContentIdConnectorDefinition1')]", + "kind": "DataConnector", + "version": "[variables('dataConnectorCCPVersion')]", + "source": { + "sourceId": "[variables('_solutionId')]", + "name": "[variables('_solutionName')]", + "kind": "Solution" + }, + "author": { + "name": "noodlemctwoodle" + }, + "support": { + "name": "Community", + "tier": "Community", + "link": "https://github.com/Azure/Azure-Sentinel/issues" + }, + "dependencies": { + "criteria": [ + { + "version": "[variables('dataConnectorCCPVersion')]", + "contentId": "[variables('_dataConnectorContentIdConnections1')]", + "kind": "ResourcesDataConnector" + } + ] + } + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/', variables('dataConnectorTemplateNameConnections1'), variables('dataConnectorCCPVersion'))]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "contentId": "[variables('_dataConnectorContentIdConnections1')]", + "displayName": "Tailscale Standard (CCF)", + "contentKind": "ResourcesDataConnector", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('dataConnectorCCPVersion')]", + "parameters": { + "guidValue": { + "defaultValue": "[[newGuid()]", + "type": "securestring" + }, + "innerWorkspace": { + "defaultValue": "[parameters('workspace')]", + "type": "securestring" + }, + "connectorDefinitionName": { + "defaultValue": "Tailscale Standard (CCF)", + "type": "securestring", + "minLength": 1 + }, + "workspace": { + "defaultValue": "[parameters('workspace')]", + "type": "securestring" + }, + "dcrConfig": { + "defaultValue": { + "dataCollectionEndpoint": "data collection Endpoint", + "dataCollectionRuleImmutableId": "data collection rule immutableId" + }, + "type": "object" + }, + "tailnetName": { + "defaultValue": "tailnetName", + "type": "securestring", + "minLength": 1 + }, + "clientId": { + "defaultValue": "clientId", + "type": "securestring", + "minLength": 1 + }, + "clientSecret": { + "defaultValue": "clientSecret", + "type": "securestring", + "minLength": 1 + } + }, + "variables": { + "_dataConnectorContentIdConnections1": "[variables('_dataConnectorContentIdConnections1')]" + }, + "resources": [ + { + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('DataConnector-', variables('_dataConnectorContentIdConnections1')))]", + "apiVersion": "2022-01-01-preview", + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "properties": { + "parentId": "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/dataConnectors', variables('_dataConnectorContentIdConnections1'))]", + "contentId": "[variables('_dataConnectorContentIdConnections1')]", + "kind": "ResourcesDataConnector", + "version": "[variables('dataConnectorCCPVersion')]", + "source": { + "sourceId": "[variables('_solutionId')]", + "name": "[variables('_solutionName')]", + "kind": "Solution" + }, + "author": { + "name": "noodlemctwoodle" + }, + "support": { + "name": "Community", + "tier": "Community", + "link": "https://github.com/Azure/Azure-Sentinel/issues" + } + } + }, + { + "name": "[[concat(parameters('innerWorkspace'),'/Microsoft.SecurityInsights/', 'TailscaleConfigPoller', parameters('guidValue'))]", + "apiVersion": "2023-02-01-preview", + "type": "Microsoft.OperationalInsights/workspaces/providers/dataConnectors", + "location": "[parameters('workspace-location')]", + "kind": "RestApiPoller", + "properties": { + "connectorDefinitionName": "TailscaleCCF", + "dataType": "Tailscale_Configuration_CL", + "dcrConfig": { + "dataCollectionEndpoint": "[[parameters('dcrConfig').dataCollectionEndpoint]", + "dataCollectionRuleImmutableId": "[[parameters('dcrConfig').dataCollectionRuleImmutableId]", + "streamName": "Custom-Tailscale_Configuration_CL" + }, + "auth": { + "type": "OAuth2", + "ClientId": "[[parameters('clientId')]", + "ClientSecret": "[[parameters('clientSecret')]", + "GrantType": "client_credentials", + "TokenEndpoint": "https://api.tailscale.com/api/v2/oauth/token", + "TokenEndpointHeaders": { + "Content-Type": "application/x-www-form-urlencoded" + }, + "TokenEndpointQueryParameters": { + "grant_type": "client_credentials" + } + }, + "request": { + "apiEndpoint": "[[concat('https://api.tailscale.com/api/v2/tailnet/', parameters('tailnetName'), '/logging/configuration')]", + "httpMethod": "GET", + "queryWindowInMin": 5, + "queryTimeFormat": "yyyy-MM-ddTHH:mm:ssZ", + "startTimeAttributeName": "start", + "endTimeAttributeName": "end", + "rateLimitQps": 1, + "retryCount": 3, + "timeoutInSeconds": 60, + "headers": { + "Accept": "application/json" + } + }, + "response": { + "eventsJsonPaths": [ + "$.logs" + ] + }, + "isActive": true + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "contentProductId": "[concat(take(variables('_solutionId'), 50),'-','rdc','-', uniqueString(concat(variables('_solutionId'),'-','ResourcesDataConnector','-',variables('_dataConnectorContentIdConnections1'),'-', variables('dataConnectorCCPVersion'))))]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "version": "[variables('dataConnectorCCPVersion')]" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/', variables('dataConnectorTemplateNameConnectorDefinition2'), variables('dataConnectorCCPVersion'))]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "contentId": "[variables('_dataConnectorContentIdConnectorDefinition2')]", + "displayName": "Tailscale Premium (CCF)", + "contentKind": "DataConnector", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('dataConnectorCCPVersion')]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',variables('_dataConnectorContentIdConnectorDefinition2'))]", + "apiVersion": "2022-09-01-preview", + "type": "Microsoft.OperationalInsights/workspaces/providers/dataConnectorDefinitions", + "location": "[parameters('workspace-location')]", + "kind": "Customizable", + "properties": { + "connectorUiConfig": { + "title": "Tailscale Premium (CCF)", + "publisher": "Custom", + "logo": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbDpzcGFjZT0icHJlc2VydmUiIGlkPSJMYXllcl8xIiB4PSIwIiB5PSIwIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIj48c3R5bGU+LnN0MHtvcGFjaXR5Oi4yO2VuYWJsZS1iYWNrZ3JvdW5kOm5ld308L3N0eWxlPjxwYXRoIGQ9Ik02NS42IDEyNy43YzM1LjMgMCA2My45LTI4LjYgNjMuOS02My45UzEwMC45IDAgNjUuNiAwIDEuOCAyOC42IDEuOCA2My45czI4LjYgNjMuOCA2My44IDYzLjgiIGNsYXNzPSJzdDAiLz48cGF0aCBkPSJNNjUuNiAzMTguMWMzNS4zIDAgNjMuOS0yOC42IDYzLjktNjMuOXMtMjguNi02My45LTYzLjktNjMuOVMxLjggMjE5IDEuOCAyNTQuMnMyOC42IDYzLjkgNjMuOCA2My45Ii8+PHBhdGggZD0iTTY1LjYgNTEyYzM1LjMgMCA2My45LTI4LjYgNjMuOS02My45cy0yOC42LTYzLjktNjMuOS02My45LTYzLjggMjguNy02My44IDYzLjlTMzAuNCA1MTIgNjUuNiA1MTIiIGNsYXNzPSJzdDAiLz48cGF0aCBkPSJNMjU3LjIgMzE4LjFjMzUuMyAwIDYzLjktMjguNiA2My45LTYzLjlzLTI4LjYtNjMuOS02My45LTYzLjktNjMuOSAyOC42LTYzLjkgNjMuOSAyOC42IDYzLjkgNjMuOSA2My45bTAgMTkzLjljMzUuMyAwIDYzLjktMjguNiA2My45LTYzLjlzLTI4LjYtNjMuOS02My45LTYzLjktNjMuOSAyOC42LTYzLjkgNjMuOSAyOC42IDYzLjkgNjMuOSA2My45Ii8+PHBhdGggZD0iTTI1Ny4yIDEyNy43YzM1LjMgMCA2My45LTI4LjYgNjMuOS02My45UzI5Mi41IDAgMjU3LjIgMHMtNjMuOSAyOC42LTYzLjkgNjMuOSAyOC42IDYzLjggNjMuOSA2My44bTE4OS4yIDBjMzUuMyAwIDYzLjktMjguNiA2My45LTYzLjlTNDgxLjYgMCA0NDYuNCAwYy0zNS4zIDAtNjMuOSAyOC42LTYzLjkgNjMuOXMyOC42IDYzLjggNjMuOSA2My44IiBjbGFzcz0ic3QwIi8+PHBhdGggZD0iTTQ0Ni40IDMxOC4xYzM1LjMgMCA2My45LTI4LjYgNjMuOS02My45cy0yOC42LTYzLjktNjMuOS02My45LTYzLjkgMjguNi02My45IDYzLjkgMjguNiA2My45IDYzLjkgNjMuOSIvPjxwYXRoIGQ9Ik00NDYuNCA1MTJjMzUuMyAwIDYzLjktMjguNiA2My45LTYzLjlzLTI4LjYtNjMuOS02My45LTYzLjktNjMuOSAyOC42LTYzLjkgNjMuOSAyOC42IDYzLjkgNjMuOSA2My45IiBjbGFzcz0ic3QwIi8+PC9zdmc+", + "descriptionMarkdown": "Polls Tailscale **configuration audit** logs (`/logging/configuration`) **and network flow logs** (`/logging/network`) into Sentinel using OAuth2 client credentials. **Use this connector for Tailscale Premium and Enterprise tier tailnets.** For Personal (Free) and Standard tailnets, install **Tailscale Standard (CCF)** instead.", + "graphQueries": [ + { + "metricName": "Configuration audit events", + "legend": "Tailscale_Configuration_CL", + "baseQuery": "Tailscale_Configuration_CL" + }, + { + "metricName": "Network flow events", + "legend": "Tailscale_Network_CL", + "baseQuery": "Tailscale_Network_CL" + } + ], + "dataTypes": [ + { + "name": "Tailscale_Configuration_CL", + "lastDataReceivedQuery": "[format('{0} | summarize Time=max(TimeGenerated)', 'Tailscale_Configuration_CL')]" + }, + { + "name": "Tailscale_Network_CL", + "lastDataReceivedQuery": "[format('{0} | summarize Time=max(TimeGenerated)', 'Tailscale_Network_CL')]" + } + ], + "sampleQueries": [ + { + "description": "Recent Tailscale configuration audit events", + "query": "Tailscale_Configuration_CL\n| sort by TimeGenerated desc\n| take 100" + }, + { + "description": "Recent Tailscale network flows", + "query": "Tailscale_Network_CL\n| sort by TimeGenerated desc\n| take 100" + }, + { + "description": "Top talkers (by bytes) on the tailnet", + "query": "Tailscale_Network_CL\n| mv-expand t = VirtualTraffic\n| extend src = tostring(t.src), dst = tostring(t.dst), txBytes = tolong(t.txBytes), rxBytes = tolong(t.rxBytes)\n| summarize TotalBytes = sum(txBytes + rxBytes) by src, dst\n| top 25 by TotalBytes" + }, + { + "description": "Exit-node traffic (data leaving the tailnet via an exit node)", + "query": "Tailscale_Network_CL\n| where array_length(ExitTraffic) > 0\n| mv-expand t = ExitTraffic\n| project TimeGenerated, NodeId, SrcNode, ExitDst = tostring(t.dst), Proto = toint(t.proto), TxBytes = tolong(t.txBytes), RxBytes = tolong(t.rxBytes)" + } + ], + "connectivityCriteria": [ + { + "type": "HasDataConnectors" + } + ], + "availability": { + "status": 1, + "isPreview": true + }, + "permissions": { + "resourceProvider": [ + { + "provider": "Microsoft.OperationalInsights/workspaces", + "permissionsDisplayText": "Read/Write on the workspace", + "providerDisplayName": "Workspace", + "scope": "Workspace", + "requiredPermissions": { + "write": true, + "read": true, + "delete": true + } + } + ] + }, + "instructionSteps": [ + { + "title": "Connect Tailscale (Premium)", + "description": "Generate an OAuth client at https://login.tailscale.com/admin/settings/oauth with the **Audit Logs - Read** and **Network Logs - Read** scopes. Find your tailnet name on the Keys page.", + "instructions": [ + { + "type": "Textbox", + "parameters": { + "label": "Tailscale tailnet", + "placeholder": "tail-XXXX.ts.net", + "type": "text", + "name": "tailnetName" + } + }, + { + "type": "Textbox", + "parameters": { + "label": "OAuth Client ID", + "placeholder": "k...", + "type": "text", + "name": "clientId" + } + }, + { + "type": "Textbox", + "parameters": { + "label": "OAuth Client Secret", + "placeholder": "tskey-client-...", + "type": "password", + "name": "clientSecret" + } + }, + { + "type": "ConnectionToggleButton", + "parameters": { + "connectLabel": "Connect", + "disconnectLabel": "Disconnect" + } + } + ] + } + ], + "id": "TailscalePremiumCCF" + } + } + }, + { + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('DataConnector-', variables('_dataConnectorContentIdConnectorDefinition2')))]", + "apiVersion": "2022-01-01-preview", + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "properties": { + "parentId": "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/dataConnectorDefinitions', variables('_dataConnectorContentIdConnectorDefinition2'))]", + "contentId": "[variables('_dataConnectorContentIdConnectorDefinition2')]", + "kind": "DataConnector", + "version": "[variables('dataConnectorCCPVersion')]", + "source": { + "sourceId": "[variables('_solutionId')]", + "name": "[variables('_solutionName')]", + "kind": "Solution" + }, + "author": { + "name": "noodlemctwoodle" + }, + "support": { + "name": "Community", + "tier": "Community", + "link": "https://github.com/Azure/Azure-Sentinel/issues" + }, + "dependencies": { + "criteria": [ + { + "version": "[variables('dataConnectorCCPVersion')]", + "contentId": "[variables('_dataConnectorContentIdConnections2')]", + "kind": "ResourcesDataConnector" + } + ] + } + } + }, + { + "name": "TailscalePremiumDCR", + "apiVersion": "2022-06-01", + "type": "Microsoft.Insights/dataCollectionRules", + "location": "[parameters('workspace-location')]", + "kind": "WorkspaceTransforms", + "properties": { + "dataCollectionEndpointId": "[variables('dataCollectionEndpointId2')]", + "streamDeclarations": { + "Custom-Tailscale_Configuration_CL": { + "columns": [ + { + "name": "eventTime", + "type": "datetime" + }, + { + "name": "eventGroupID", + "type": "string" + }, + { + "name": "actor", + "type": "dynamic" + }, + { + "name": "action", + "type": "string" + }, + { + "name": "target", + "type": "dynamic" + }, + { + "name": "origin", + "type": "dynamic" + }, + { + "name": "new", + "type": "dynamic" + }, + { + "name": "old", + "type": "dynamic" + } + ] + }, + "Custom-Tailscale_Network_CL": { + "columns": [ + { + "name": "nodeId", + "type": "string" + }, + { + "name": "logged", + "type": "datetime" + }, + { + "name": "start", + "type": "datetime" + }, + { + "name": "end", + "type": "datetime" + }, + { + "name": "srcNode", + "type": "dynamic" + }, + { + "name": "dstNodes", + "type": "dynamic" + }, + { + "name": "virtualTraffic", + "type": "dynamic" + }, + { + "name": "subnetTraffic", + "type": "dynamic" + }, + { + "name": "exitTraffic", + "type": "dynamic" + }, + { + "name": "physicalTraffic", + "type": "dynamic" + } + ] + } + }, + "destinations": { + "logAnalytics": [ + { + "workspaceResourceId": "[variables('workspaceResourceId')]", + "name": "sentinelWorkspace" + } + ] + }, + "dataFlows": [ + { + "streams": [ + "Custom-Tailscale_Configuration_CL" + ], + "destinations": [ + "sentinelWorkspace" + ], + "transformKql": "source | extend TimeGenerated = eventTime | extend EventTime = eventTime | extend EventGroupID = eventGroupID | extend Actor = actor | extend Action = action | extend Target = target | extend Origin = origin | extend New = new | extend Old = old | project TimeGenerated, EventTime, EventGroupID, Actor, Action, Target, Origin, New, Old", + "outputStream": "Custom-Tailscale_Configuration_CL" + }, + { + "streams": [ + "Custom-Tailscale_Network_CL" + ], + "destinations": [ + "sentinelWorkspace" + ], + "transformKql": "source | extend TimeGenerated = logged | extend NodeId = nodeId | extend FlowStart = start | extend FlowEnd = end | extend SrcNode = srcNode | extend DstNodes = dstNodes | extend VirtualTraffic = virtualTraffic | extend SubnetTraffic = subnetTraffic | extend ExitTraffic = exitTraffic | extend PhysicalTraffic = physicalTraffic | project TimeGenerated, NodeId, FlowStart, FlowEnd, SrcNode, DstNodes, VirtualTraffic, SubnetTraffic, ExitTraffic, PhysicalTraffic", + "outputStream": "Custom-Tailscale_Network_CL" + } + ] + } + }, + { + "name": "Tailscale_Configuration_CL", + "apiVersion": "2022-10-01", + "type": "Microsoft.OperationalInsights/workspaces/tables", + "location": "[parameters('workspace-location')]", + "kind": null, + "properties": { + "schema": { + "name": "Tailscale_Configuration_CL", + "columns": [ + { + "name": "TimeGenerated", + "type": "datetime" + }, + { + "name": "EventTime", + "type": "datetime" + }, + { + "name": "EventGroupID", + "type": "string" + }, + { + "name": "Actor", + "type": "dynamic" + }, + { + "name": "Action", + "type": "string" + }, + { + "name": "Target", + "type": "dynamic" + }, + { + "name": "Origin", + "type": "dynamic" + }, + { + "name": "New", + "type": "dynamic" + }, + { + "name": "Old", + "type": "dynamic" + } + ] + }, + "retentionInDays": 90 + } + }, + { + "name": "Tailscale_Network_CL", + "apiVersion": "2022-10-01", + "type": "Microsoft.OperationalInsights/workspaces/tables", + "location": "[parameters('workspace-location')]", + "kind": null, + "properties": { + "schema": { + "name": "Tailscale_Network_CL", + "columns": [ + { + "name": "TimeGenerated", + "type": "datetime" + }, + { + "name": "NodeId", + "type": "string" + }, + { + "name": "FlowStart", + "type": "datetime" + }, + { + "name": "FlowEnd", + "type": "datetime" + }, + { + "name": "SrcNode", + "type": "dynamic" + }, + { + "name": "DstNodes", + "type": "dynamic" + }, + { + "name": "VirtualTraffic", + "type": "dynamic" + }, + { + "name": "SubnetTraffic", + "type": "dynamic" + }, + { + "name": "ExitTraffic", + "type": "dynamic" + }, + { + "name": "PhysicalTraffic", + "type": "dynamic" + } + ] + }, + "retentionInDays": 90 + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "contentProductId": "[concat(take(variables('_solutionId'), 50),'-','dc','-', uniqueString(concat(variables('_solutionId'),'-','DataConnector','-',variables('_dataConnectorContentIdConnectorDefinition2'),'-', variables('dataConnectorCCPVersion'))))]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "version": "[variables('dataConnectorCCPVersion')]" + } + }, + { + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',variables('_dataConnectorContentIdConnectorDefinition2'))]", + "apiVersion": "2022-09-01-preview", + "type": "Microsoft.OperationalInsights/workspaces/providers/dataConnectorDefinitions", + "location": "[parameters('workspace-location')]", + "kind": "Customizable", + "properties": { + "connectorUiConfig": { + "title": "Tailscale Premium (CCF)", + "publisher": "Custom", + "logo": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbDpzcGFjZT0icHJlc2VydmUiIGlkPSJMYXllcl8xIiB4PSIwIiB5PSIwIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIj48c3R5bGU+LnN0MHtvcGFjaXR5Oi4yO2VuYWJsZS1iYWNrZ3JvdW5kOm5ld308L3N0eWxlPjxwYXRoIGQ9Ik02NS42IDEyNy43YzM1LjMgMCA2My45LTI4LjYgNjMuOS02My45UzEwMC45IDAgNjUuNiAwIDEuOCAyOC42IDEuOCA2My45czI4LjYgNjMuOCA2My44IDYzLjgiIGNsYXNzPSJzdDAiLz48cGF0aCBkPSJNNjUuNiAzMTguMWMzNS4zIDAgNjMuOS0yOC42IDYzLjktNjMuOXMtMjguNi02My45LTYzLjktNjMuOVMxLjggMjE5IDEuOCAyNTQuMnMyOC42IDYzLjkgNjMuOCA2My45Ii8+PHBhdGggZD0iTTY1LjYgNTEyYzM1LjMgMCA2My45LTI4LjYgNjMuOS02My45cy0yOC42LTYzLjktNjMuOS02My45LTYzLjggMjguNy02My44IDYzLjlTMzAuNCA1MTIgNjUuNiA1MTIiIGNsYXNzPSJzdDAiLz48cGF0aCBkPSJNMjU3LjIgMzE4LjFjMzUuMyAwIDYzLjktMjguNiA2My45LTYzLjlzLTI4LjYtNjMuOS02My45LTYzLjktNjMuOSAyOC42LTYzLjkgNjMuOSAyOC42IDYzLjkgNjMuOSA2My45bTAgMTkzLjljMzUuMyAwIDYzLjktMjguNiA2My45LTYzLjlzLTI4LjYtNjMuOS02My45LTYzLjktNjMuOSAyOC42LTYzLjkgNjMuOSAyOC42IDYzLjkgNjMuOSA2My45Ii8+PHBhdGggZD0iTTI1Ny4yIDEyNy43YzM1LjMgMCA2My45LTI4LjYgNjMuOS02My45UzI5Mi41IDAgMjU3LjIgMHMtNjMuOSAyOC42LTYzLjkgNjMuOSAyOC42IDYzLjggNjMuOSA2My44bTE4OS4yIDBjMzUuMyAwIDYzLjktMjguNiA2My45LTYzLjlTNDgxLjYgMCA0NDYuNCAwYy0zNS4zIDAtNjMuOSAyOC42LTYzLjkgNjMuOXMyOC42IDYzLjggNjMuOSA2My44IiBjbGFzcz0ic3QwIi8+PHBhdGggZD0iTTQ0Ni40IDMxOC4xYzM1LjMgMCA2My45LTI4LjYgNjMuOS02My45cy0yOC42LTYzLjktNjMuOS02My45LTYzLjkgMjguNi02My45IDYzLjkgMjguNiA2My45IDYzLjkgNjMuOSIvPjxwYXRoIGQ9Ik00NDYuNCA1MTJjMzUuMyAwIDYzLjktMjguNiA2My45LTYzLjlzLTI4LjYtNjMuOS02My45LTYzLjktNjMuOSAyOC42LTYzLjkgNjMuOSAyOC42IDYzLjkgNjMuOSA2My45IiBjbGFzcz0ic3QwIi8+PC9zdmc+", + "descriptionMarkdown": "Polls Tailscale **configuration audit** logs (`/logging/configuration`) **and network flow logs** (`/logging/network`) into Sentinel using OAuth2 client credentials. **Use this connector for Tailscale Premium and Enterprise tier tailnets.** For Personal (Free) and Standard tailnets, install **Tailscale Standard (CCF)** instead.", + "graphQueries": [ + { + "metricName": "Configuration audit events", + "legend": "Tailscale_Configuration_CL", + "baseQuery": "Tailscale_Configuration_CL" + }, + { + "metricName": "Network flow events", + "legend": "Tailscale_Network_CL", + "baseQuery": "Tailscale_Network_CL" + } + ], + "dataTypes": [ + { + "name": "Tailscale_Configuration_CL", + "lastDataReceivedQuery": "[format('{0} | summarize Time=max(TimeGenerated)', 'Tailscale_Configuration_CL')]" + }, + { + "name": "Tailscale_Network_CL", + "lastDataReceivedQuery": "[format('{0} | summarize Time=max(TimeGenerated)', 'Tailscale_Network_CL')]" + } + ], + "sampleQueries": [ + { + "description": "Recent Tailscale configuration audit events", + "query": "Tailscale_Configuration_CL\n| sort by TimeGenerated desc\n| take 100" + }, + { + "description": "Recent Tailscale network flows", + "query": "Tailscale_Network_CL\n| sort by TimeGenerated desc\n| take 100" + }, + { + "description": "Top talkers (by bytes) on the tailnet", + "query": "Tailscale_Network_CL\n| mv-expand t = VirtualTraffic\n| extend src = tostring(t.src), dst = tostring(t.dst), txBytes = tolong(t.txBytes), rxBytes = tolong(t.rxBytes)\n| summarize TotalBytes = sum(txBytes + rxBytes) by src, dst\n| top 25 by TotalBytes" + }, + { + "description": "Exit-node traffic (data leaving the tailnet via an exit node)", + "query": "Tailscale_Network_CL\n| where array_length(ExitTraffic) > 0\n| mv-expand t = ExitTraffic\n| project TimeGenerated, NodeId, SrcNode, ExitDst = tostring(t.dst), Proto = toint(t.proto), TxBytes = tolong(t.txBytes), RxBytes = tolong(t.rxBytes)" + } + ], + "connectivityCriteria": [ + { + "type": "HasDataConnectors" + } + ], + "availability": { + "status": 1, + "isPreview": true + }, + "permissions": { + "resourceProvider": [ + { + "provider": "Microsoft.OperationalInsights/workspaces", + "permissionsDisplayText": "Read/Write on the workspace", + "providerDisplayName": "Workspace", + "scope": "Workspace", + "requiredPermissions": { + "write": true, + "read": true, + "delete": true + } + } + ] + }, + "instructionSteps": [ + { + "title": "Connect Tailscale (Premium)", + "description": "Generate an OAuth client at https://login.tailscale.com/admin/settings/oauth with the **Audit Logs - Read** and **Network Logs - Read** scopes. Find your tailnet name on the Keys page.", + "instructions": [ + { + "type": "Textbox", + "parameters": { + "label": "Tailscale tailnet", + "placeholder": "tail-XXXX.ts.net", + "type": "text", + "name": "tailnetName" + } + }, + { + "type": "Textbox", + "parameters": { + "label": "OAuth Client ID", + "placeholder": "k...", + "type": "text", + "name": "clientId" + } + }, + { + "type": "Textbox", + "parameters": { + "label": "OAuth Client Secret", + "placeholder": "tskey-client-...", + "type": "password", + "name": "clientSecret" + } + }, + { + "type": "ConnectionToggleButton", + "parameters": { + "connectLabel": "Connect", + "disconnectLabel": "Disconnect" + } + } + ] + } + ], + "id": "TailscalePremiumCCF" + } + } + }, + { + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('DataConnector-', variables('_dataConnectorContentIdConnectorDefinition2')))]", + "apiVersion": "2022-01-01-preview", + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "properties": { + "parentId": "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/dataConnectorDefinitions', variables('_dataConnectorContentIdConnectorDefinition2'))]", + "contentId": "[variables('_dataConnectorContentIdConnectorDefinition2')]", + "kind": "DataConnector", + "version": "[variables('dataConnectorCCPVersion')]", + "source": { + "sourceId": "[variables('_solutionId')]", + "name": "[variables('_solutionName')]", + "kind": "Solution" + }, + "author": { + "name": "noodlemctwoodle" + }, + "support": { + "name": "Community", + "tier": "Community", + "link": "https://github.com/Azure/Azure-Sentinel/issues" + }, + "dependencies": { + "criteria": [ + { + "version": "[variables('dataConnectorCCPVersion')]", + "contentId": "[variables('_dataConnectorContentIdConnections2')]", + "kind": "ResourcesDataConnector" + } + ] + } + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/', variables('dataConnectorTemplateNameConnections2'), variables('dataConnectorCCPVersion'))]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "contentId": "[variables('_dataConnectorContentIdConnections2')]", + "displayName": "Tailscale Premium (CCF)", + "contentKind": "ResourcesDataConnector", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('dataConnectorCCPVersion')]", + "parameters": { + "guidValue": { + "defaultValue": "[[newGuid()]", + "type": "securestring" + }, + "innerWorkspace": { + "defaultValue": "[parameters('workspace')]", + "type": "securestring" + }, + "connectorDefinitionName": { + "defaultValue": "Tailscale Premium (CCF)", + "type": "securestring", + "minLength": 1 + }, + "workspace": { + "defaultValue": "[parameters('workspace')]", + "type": "securestring" + }, + "dcrConfig": { + "defaultValue": { + "dataCollectionEndpoint": "data collection Endpoint", + "dataCollectionRuleImmutableId": "data collection rule immutableId" + }, + "type": "object" + }, + "tailnetName": { + "defaultValue": "tailnetName", + "type": "securestring", + "minLength": 1 + }, + "clientId": { + "defaultValue": "clientId", + "type": "securestring", + "minLength": 1 + }, + "clientSecret": { + "defaultValue": "clientSecret", + "type": "securestring", + "minLength": 1 + } + }, + "variables": { + "_dataConnectorContentIdConnections2": "[variables('_dataConnectorContentIdConnections2')]" + }, + "resources": [ + { + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('DataConnector-', variables('_dataConnectorContentIdConnections2')))]", + "apiVersion": "2022-01-01-preview", + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "properties": { + "parentId": "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/dataConnectors', variables('_dataConnectorContentIdConnections2'))]", + "contentId": "[variables('_dataConnectorContentIdConnections2')]", + "kind": "ResourcesDataConnector", + "version": "[variables('dataConnectorCCPVersion')]", + "source": { + "sourceId": "[variables('_solutionId')]", + "name": "[variables('_solutionName')]", + "kind": "Solution" + }, + "author": { + "name": "noodlemctwoodle" + }, + "support": { + "name": "Community", + "tier": "Community", + "link": "https://github.com/Azure/Azure-Sentinel/issues" + } + } + }, + { + "name": "[[concat(parameters('innerWorkspace'),'/Microsoft.SecurityInsights/', 'TailscalePremiumConfigPoller', parameters('guidValue'))]", + "apiVersion": "2023-02-01-preview", + "type": "Microsoft.OperationalInsights/workspaces/providers/dataConnectors", + "location": "[parameters('workspace-location')]", + "kind": "RestApiPoller", + "properties": { + "connectorDefinitionName": "TailscalePremiumCCF", + "dataType": "Tailscale_Configuration_CL", + "dcrConfig": { + "dataCollectionEndpoint": "[[parameters('dcrConfig').dataCollectionEndpoint]", + "dataCollectionRuleImmutableId": "[[parameters('dcrConfig').dataCollectionRuleImmutableId]", + "streamName": "Custom-Tailscale_Configuration_CL" + }, + "auth": { + "type": "OAuth2", + "ClientId": "[[parameters('clientId')]", + "ClientSecret": "[[parameters('clientSecret')]", + "GrantType": "client_credentials", + "TokenEndpoint": "https://api.tailscale.com/api/v2/oauth/token", + "TokenEndpointHeaders": { + "Content-Type": "application/x-www-form-urlencoded" + }, + "TokenEndpointQueryParameters": { + "grant_type": "client_credentials" + } + }, + "request": { + "apiEndpoint": "[[concat('https://api.tailscale.com/api/v2/tailnet/', parameters('tailnetName'), '/logging/configuration')]", + "httpMethod": "GET", + "queryWindowInMin": 5, + "queryTimeFormat": "yyyy-MM-ddTHH:mm:ssZ", + "startTimeAttributeName": "start", + "endTimeAttributeName": "end", + "rateLimitQps": 1, + "retryCount": 3, + "timeoutInSeconds": 60, + "headers": { + "Accept": "application/json" + } + }, + "response": { + "eventsJsonPaths": [ + "$.logs" + ] + }, + "isActive": true + } + }, + { + "name": "[[concat(parameters('innerWorkspace'),'/Microsoft.SecurityInsights/', 'TailscalePremiumNetworkPoller', parameters('guidValue'))]", + "apiVersion": "2023-02-01-preview", + "type": "Microsoft.OperationalInsights/workspaces/providers/dataConnectors", + "location": "[parameters('workspace-location')]", + "kind": "RestApiPoller", + "properties": { + "connectorDefinitionName": "TailscalePremiumCCF", + "dataType": "Tailscale_Network_CL", + "dcrConfig": { + "dataCollectionEndpoint": "[[parameters('dcrConfig').dataCollectionEndpoint]", + "dataCollectionRuleImmutableId": "[[parameters('dcrConfig').dataCollectionRuleImmutableId]", + "streamName": "Custom-Tailscale_Network_CL" + }, + "auth": { + "type": "OAuth2", + "ClientId": "[[parameters('clientId')]", + "ClientSecret": "[[parameters('clientSecret')]", + "GrantType": "client_credentials", + "TokenEndpoint": "https://api.tailscale.com/api/v2/oauth/token", + "TokenEndpointHeaders": { + "Content-Type": "application/x-www-form-urlencoded" + }, + "TokenEndpointQueryParameters": { + "grant_type": "client_credentials" + } + }, + "request": { + "apiEndpoint": "[[concat('https://api.tailscale.com/api/v2/tailnet/', parameters('tailnetName'), '/logging/network')]", + "httpMethod": "GET", + "queryWindowInMin": 5, + "queryTimeFormat": "yyyy-MM-ddTHH:mm:ssZ", + "startTimeAttributeName": "start", + "endTimeAttributeName": "end", + "rateLimitQps": 1, + "retryCount": 3, + "timeoutInSeconds": 60, + "headers": { + "Accept": "application/json" + } + }, + "response": { + "eventsJsonPaths": [ + "$.logs" + ] + }, + "isActive": true + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "contentProductId": "[concat(take(variables('_solutionId'), 50),'-','rdc','-', uniqueString(concat(variables('_solutionId'),'-','ResourcesDataConnector','-',variables('_dataConnectorContentIdConnections2'),'-', variables('dataConnectorCCPVersion'))))]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "version": "[variables('dataConnectorCCPVersion')]" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('analyticRuleObject1').analyticRuleTemplateSpecName1]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "TailscaleNewAPIaccesstokenorOAuthclientcreated_AnalyticalRules Analytics Rule with template version 3.0.1", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('analyticRuleObject1').analyticRuleVersion1]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.SecurityInsights/AlertRuleTemplates", + "name": "[variables('analyticRuleObject1')._analyticRulecontentId1]", + "apiVersion": "2023-02-01-preview", + "kind": "Scheduled", + "location": "[parameters('workspace-location')]", + "properties": { + "description": "A new API access token or OAuth client was created in the tailnet. These grant programmatic access — verify the actor and intent.", + "displayName": "Tailscale: New API access token or OAuth client created", + "enabled": false, + "query": "Tailscale_Configuration_CL\n | where Action == \"CREATE\"\n | where tostring(Target.type) in (\"API_KEY\", \"OAUTH_CLIENT\")\n | extend ActorLogin = tostring(Actor.loginName)\n | extend TargetName = tostring(Target.name)\n | extend TargetId = tostring(Target.id)\n | project TimeGenerated, ActorLogin, Action, TargetName, TargetId, Origin, New\n", + "queryFrequency": "PTPT15M", + "queryPeriod": "PTPT15M", + "severity": "Medium", + "suppressionDuration": "PT1H", + "suppressionEnabled": false, + "triggerOperator": "GreaterThan", + "triggerThreshold": 0, + "status": "Available", + "requiredDataConnectors": [ + { + "dataTypes": [ + "Tailscale_Configuration_CL" + ], + "connectorId": "TailscaleCCF" + } + ], + "tactics": [ + "Persistence", + "CredentialAccess" + ], + "techniques": [ + "T1098", + "T1136" + ], + "entityMappings": [ + { + "fieldMappings": [ + { + "columnName": "ActorLogin", + "identifier": "FullName" + } + ], + "entityType": "Account" + } + ], + "incidentConfiguration": { + "createIncident": true, + "groupingConfiguration": { + "lookbackDuration": "PT5H", + "matchingMethod": "AllEntities", + "reopenClosedIncident": false, + "groupByEntities": [], + "enabled": true + } + } + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('AnalyticsRule-', last(split(variables('analyticRuleObject1').analyticRuleId1,'/'))))]", + "properties": { + "description": "Tailscale (CCF) Analytics Rule 1", + "parentId": "[variables('analyticRuleObject1').analyticRuleId1]", + "contentId": "[variables('analyticRuleObject1')._analyticRulecontentId1]", + "kind": "AnalyticsRule", + "version": "[variables('analyticRuleObject1').analyticRuleVersion1]", + "source": { + "kind": "Solution", + "name": "Tailscale (CCF)", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "noodlemctwoodle" + }, + "support": { + "name": "Community", + "tier": "Community", + "link": "https://github.com/Azure/Azure-Sentinel/issues" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('analyticRuleObject1')._analyticRulecontentId1]", + "contentKind": "AnalyticsRule", + "displayName": "Tailscale: New API access token or OAuth client created", + "contentProductId": "[variables('analyticRuleObject1')._analyticRulecontentProductId1]", + "id": "[variables('analyticRuleObject1')._analyticRulecontentProductId1]", + "version": "[variables('analyticRuleObject1').analyticRuleVersion1]" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('analyticRuleObject2').analyticRuleTemplateSpecName2]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "TailscalePolicyfileACLmodified_AnalyticalRules Analytics Rule with template version 3.0.1", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('analyticRuleObject2').analyticRuleVersion2]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.SecurityInsights/AlertRuleTemplates", + "name": "[variables('analyticRuleObject2')._analyticRulecontentId2]", + "apiVersion": "2023-02-01-preview", + "kind": "Scheduled", + "location": "[parameters('workspace-location')]", + "properties": { + "description": "The tailnet ACL/policy file was modified. Review the diff — incorrect ACLs can silently expand blast radius across the tailnet.", + "displayName": "Tailscale: Policy file (ACL) modified", + "enabled": false, + "query": "Tailscale_Configuration_CL\n | where Action in (\"UPDATE\", \"CREATE\")\n | where tostring(Target.type) == \"ACL\"\n | extend ActorLogin = tostring(Actor.loginName)\n | project TimeGenerated, ActorLogin, Action, Target, Origin, Old, New\n", + "queryFrequency": "PTPT15M", + "queryPeriod": "PTPT15M", + "severity": "Medium", + "suppressionDuration": "PT1H", + "suppressionEnabled": false, + "triggerOperator": "GreaterThan", + "triggerThreshold": 0, + "status": "Available", + "requiredDataConnectors": [ + { + "dataTypes": [ + "Tailscale_Configuration_CL" + ], + "connectorId": "TailscaleCCF" + } + ], + "tactics": [ + "DefenseEvasion", + "Persistence" + ], + "techniques": [ + "T1556" + ], + "entityMappings": [ + { + "fieldMappings": [ + { + "columnName": "ActorLogin", + "identifier": "FullName" + } + ], + "entityType": "Account" + } + ], + "incidentConfiguration": { + "createIncident": true, + "groupingConfiguration": { + "lookbackDuration": "PT5H", + "matchingMethod": "AllEntities", + "reopenClosedIncident": false, + "groupByEntities": [], + "enabled": true + } + } + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('AnalyticsRule-', last(split(variables('analyticRuleObject2').analyticRuleId2,'/'))))]", + "properties": { + "description": "Tailscale (CCF) Analytics Rule 2", + "parentId": "[variables('analyticRuleObject2').analyticRuleId2]", + "contentId": "[variables('analyticRuleObject2')._analyticRulecontentId2]", + "kind": "AnalyticsRule", + "version": "[variables('analyticRuleObject2').analyticRuleVersion2]", + "source": { + "kind": "Solution", + "name": "Tailscale (CCF)", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "noodlemctwoodle" + }, + "support": { + "name": "Community", + "tier": "Community", + "link": "https://github.com/Azure/Azure-Sentinel/issues" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('analyticRuleObject2')._analyticRulecontentId2]", + "contentKind": "AnalyticsRule", + "displayName": "Tailscale: Policy file (ACL) modified", + "contentProductId": "[variables('analyticRuleObject2')._analyticRulecontentProductId2]", + "id": "[variables('analyticRuleObject2')._analyticRulecontentProductId2]", + "version": "[variables('analyticRuleObject2').analyticRuleVersion2]" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('analyticRuleObject3').analyticRuleTemplateSpecName3]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "TailscaleAuthkeycreated_AnalyticalRules Analytics Rule with template version 3.0.1", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('analyticRuleObject3').analyticRuleVersion3]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.SecurityInsights/AlertRuleTemplates", + "name": "[variables('analyticRuleObject3')._analyticRulecontentId3]", + "apiVersion": "2023-02-01-preview", + "kind": "Scheduled", + "location": "[parameters('workspace-location')]", + "properties": { + "description": "A new auth key was generated. Auth keys allow unattended device enrollment into the tailnet — confirm it was expected and revoke if not.", + "displayName": "Tailscale: Auth key created", + "enabled": false, + "query": "Tailscale_Configuration_CL\n | where Action == \"CREATE\"\n | where tostring(Target.type) == \"AUTH_KEY\"\n | extend ActorLogin = tostring(Actor.loginName)\n | extend KeyDescription = tostring(New.description)\n | extend Reusable = tostring(New.reusable)\n | extend Ephemeral = tostring(New.ephemeral)\n | project TimeGenerated, ActorLogin, KeyDescription, Reusable, Ephemeral, Origin, New\n", + "queryFrequency": "PTPT15M", + "queryPeriod": "PTPT15M", + "severity": "Low", + "suppressionDuration": "PT1H", + "suppressionEnabled": false, + "triggerOperator": "GreaterThan", + "triggerThreshold": 0, + "status": "Available", + "requiredDataConnectors": [ + { + "dataTypes": [ + "Tailscale_Configuration_CL" + ], + "connectorId": "TailscaleCCF" + } + ], + "tactics": [ + "Persistence" + ], + "techniques": [ + "T1098" + ], + "entityMappings": [ + { + "fieldMappings": [ + { + "columnName": "ActorLogin", + "identifier": "FullName" + } + ], + "entityType": "Account" + } + ], + "incidentConfiguration": { + "createIncident": true, + "groupingConfiguration": { + "lookbackDuration": "PT5H", + "matchingMethod": "AllEntities", + "reopenClosedIncident": false, + "groupByEntities": [], + "enabled": true + } + } + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('AnalyticsRule-', last(split(variables('analyticRuleObject3').analyticRuleId3,'/'))))]", + "properties": { + "description": "Tailscale (CCF) Analytics Rule 3", + "parentId": "[variables('analyticRuleObject3').analyticRuleId3]", + "contentId": "[variables('analyticRuleObject3')._analyticRulecontentId3]", + "kind": "AnalyticsRule", + "version": "[variables('analyticRuleObject3').analyticRuleVersion3]", + "source": { + "kind": "Solution", + "name": "Tailscale (CCF)", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "noodlemctwoodle" + }, + "support": { + "name": "Community", + "tier": "Community", + "link": "https://github.com/Azure/Azure-Sentinel/issues" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('analyticRuleObject3')._analyticRulecontentId3]", + "contentKind": "AnalyticsRule", + "displayName": "Tailscale: Auth key created", + "contentProductId": "[variables('analyticRuleObject3')._analyticRulecontentProductId3]", + "id": "[variables('analyticRuleObject3')._analyticRulecontentProductId3]", + "version": "[variables('analyticRuleObject3').analyticRuleVersion3]" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('analyticRuleObject4').analyticRuleTemplateSpecName4]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "TailscaleExitnodeadvertisedorapproved_AnalyticalRules Analytics Rule with template version 3.0.1", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('analyticRuleObject4').analyticRuleVersion4]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.SecurityInsights/AlertRuleTemplates", + "name": "[variables('analyticRuleObject4')._analyticRulecontentId4]", + "apiVersion": "2023-02-01-preview", + "kind": "Scheduled", + "location": "[parameters('workspace-location')]", + "properties": { + "description": "A device started advertising itself as an exit node, or an admin approved one. Validate the device and operator — rogue exit nodes can intercept tailnet egress.", + "displayName": "Tailscale: Exit node advertised or approved", + "enabled": false, + "query": "Tailscale_Configuration_CL\n | where Action contains \"EXIT\" or tostring(New.advertisedExitNode) == \"true\" or tostring(New.allowedExitNode) == \"true\"\n | extend ActorLogin = tostring(Actor.loginName)\n | extend NodeName = tostring(Target.name)\n | extend NodeId = tostring(Target.id)\n | project TimeGenerated, ActorLogin, Action, NodeName, NodeId, Origin, New, Old\n", + "queryFrequency": "PTPT30M", + "queryPeriod": "PTPT30M", + "severity": "Low", + "suppressionDuration": "PT1H", + "suppressionEnabled": false, + "triggerOperator": "GreaterThan", + "triggerThreshold": 0, + "status": "Available", + "requiredDataConnectors": [ + { + "dataTypes": [ + "Tailscale_Configuration_CL" + ], + "connectorId": "TailscaleCCF" + } + ], + "tactics": [ + "CommandAndControl", + "Exfiltration" + ], + "techniques": [ + "T1090" + ], + "entityMappings": [ + { + "fieldMappings": [ + { + "columnName": "ActorLogin", + "identifier": "FullName" + } + ], + "entityType": "Account" + }, + { + "fieldMappings": [ + { + "columnName": "NodeName", + "identifier": "HostName" + } + ], + "entityType": "Host" + } + ], + "incidentConfiguration": { + "createIncident": true, + "groupingConfiguration": { + "lookbackDuration": "PT5H", + "matchingMethod": "AllEntities", + "reopenClosedIncident": false, + "groupByEntities": [], + "enabled": true + } + } + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('AnalyticsRule-', last(split(variables('analyticRuleObject4').analyticRuleId4,'/'))))]", + "properties": { + "description": "Tailscale (CCF) Analytics Rule 4", + "parentId": "[variables('analyticRuleObject4').analyticRuleId4]", + "contentId": "[variables('analyticRuleObject4')._analyticRulecontentId4]", + "kind": "AnalyticsRule", + "version": "[variables('analyticRuleObject4').analyticRuleVersion4]", + "source": { + "kind": "Solution", + "name": "Tailscale (CCF)", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "noodlemctwoodle" + }, + "support": { + "name": "Community", + "tier": "Community", + "link": "https://github.com/Azure/Azure-Sentinel/issues" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('analyticRuleObject4')._analyticRulecontentId4]", + "contentKind": "AnalyticsRule", + "displayName": "Tailscale: Exit node advertised or approved", + "contentProductId": "[variables('analyticRuleObject4')._analyticRulecontentProductId4]", + "id": "[variables('analyticRuleObject4')._analyticRulecontentProductId4]", + "version": "[variables('analyticRuleObject4').analyticRuleVersion4]" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('analyticRuleObject5').analyticRuleTemplateSpecName5]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "TailscaleMasscredentialrevocationinshortwindow_AnalyticalRules Analytics Rule with template version 3.0.1", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('analyticRuleObject5').analyticRuleVersion5]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.SecurityInsights/AlertRuleTemplates", + "name": "[variables('analyticRuleObject5')._analyticRulecontentId5]", + "apiVersion": "2023-02-01-preview", + "kind": "Scheduled", + "location": "[parameters('workspace-location')]", + "properties": { + "description": "Five or more API keys, OAuth clients, or auth keys were revoked/deleted within an hour. May be routine rotation, but also a typical cleanup pattern after credential compromise.", + "displayName": "Tailscale: Mass credential revocation in short window", + "enabled": false, + "query": "Tailscale_Configuration_CL\n | where Action in (\"REVOKE\", \"DELETE\")\n | where tostring(Target.type) in (\"API_KEY\", \"OAUTH_CLIENT\", \"AUTH_KEY\")\n | extend ActorLogin = tostring(Actor.loginName)\n | summarize\n RevokedCount = count(),\n TargetTypes = make_set(tostring(Target.type)),\n TargetIds = make_set(tostring(Target.id)),\n FirstEvent = min(TimeGenerated),\n LastEvent = max(TimeGenerated)\n by ActorLogin, bin(TimeGenerated, 1h)\n | where RevokedCount >= 5\n | project TimeGenerated = LastEvent, ActorLogin, RevokedCount, TargetTypes, TargetIds, FirstEvent, LastEvent\n", + "queryFrequency": "PTPT1H", + "queryPeriod": "PTPT1H", + "severity": "High", + "suppressionDuration": "PT1H", + "suppressionEnabled": false, + "triggerOperator": "GreaterThan", + "triggerThreshold": 0, + "status": "Available", + "requiredDataConnectors": [ + { + "dataTypes": [ + "Tailscale_Configuration_CL" + ], + "connectorId": "TailscaleCCF" + } + ], + "tactics": [ + "DefenseEvasion", + "Impact" + ], + "techniques": [ + "T1070" + ], + "entityMappings": [ + { + "fieldMappings": [ + { + "columnName": "ActorLogin", + "identifier": "FullName" + } + ], + "entityType": "Account" + } + ], + "incidentConfiguration": { + "createIncident": true, + "groupingConfiguration": { + "lookbackDuration": "PT5H", + "matchingMethod": "AllEntities", + "reopenClosedIncident": false, + "groupByEntities": [], + "enabled": true + } + } + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('AnalyticsRule-', last(split(variables('analyticRuleObject5').analyticRuleId5,'/'))))]", + "properties": { + "description": "Tailscale (CCF) Analytics Rule 5", + "parentId": "[variables('analyticRuleObject5').analyticRuleId5]", + "contentId": "[variables('analyticRuleObject5')._analyticRulecontentId5]", + "kind": "AnalyticsRule", + "version": "[variables('analyticRuleObject5').analyticRuleVersion5]", + "source": { + "kind": "Solution", + "name": "Tailscale (CCF)", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "noodlemctwoodle" + }, + "support": { + "name": "Community", + "tier": "Community", + "link": "https://github.com/Azure/Azure-Sentinel/issues" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('analyticRuleObject5')._analyticRulecontentId5]", + "contentKind": "AnalyticsRule", + "displayName": "Tailscale: Mass credential revocation in short window", + "contentProductId": "[variables('analyticRuleObject5')._analyticRulecontentProductId5]", + "id": "[variables('analyticRuleObject5')._analyticRulecontentProductId5]", + "version": "[variables('analyticRuleObject5').analyticRuleVersion5]" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentPackages", + "apiVersion": "2023-04-01-preview", + "location": "[parameters('workspace-location')]", + "properties": { + "version": "3.0.1", + "kind": "Solution", + "contentSchemaVersion": "3.0.0", + "displayName": "Tailscale (CCF)", + "publisherDisplayName": "Community", + "descriptionHtml": "

Note: Please refer to the following before installing the solution:

\n

• Review the solution Release Notes

\n

• There may be known issues pertaining to this Solution, please refer to them before installing.

\n

The Tailscale solution for Microsoft Sentinel ingests Tailscale telemetry via OAuth2-secured APIs.

\n

Data connectors in this solution (install one based on your Tailscale plan):

\n
    \n
  • Tailscale Standard (CCF) — Configuration audit logs (/logging/configuration). Use on Personal (Free) and Standard tailnets. Included in this release.
  • \n
  • Tailscale Premium (CCF) — Configuration audit + network flow logs (/logging/configuration + /logging/network). Use on Premium and Enterprise tailnets. Planned for a future release.
  • \n
\n

Underlying technology: Microsoft Sentinel Codeless Connector Framework (CCF) with OAuth2 client-credentials auth.

\n

Pre-requisites:

\n
    \n
  1. Sign in to Tailscale OAuth settings
  2. \n
  3. Create an OAuth client with the Audit Logs - Read scope (Standard connector). For the Premium connector also tick Network Logs - Read.
  4. \n
  5. Copy the client ID and client secret (secret shown once)
  6. \n
  7. Note your tailnet name from the Keys page
  8. \n
\n

Data Connectors: 2, Analytic Rules: 5

\n

Learn more about Microsoft Sentinel | Learn more about Solutions

\n", + "contentKind": "Solution", + "contentProductId": "[variables('_solutioncontentProductId')]", + "id": "[variables('_solutioncontentProductId')]", + "icon": "", + "contentId": "[variables('_solutionId')]", + "parentId": "[variables('_solutionId')]", + "source": { + "kind": "Solution", + "name": "Tailscale (CCF)", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "noodlemctwoodle" + }, + "support": { + "name": "Community", + "tier": "Community", + "link": "https://github.com/Azure/Azure-Sentinel/issues" + }, + "dependencies": { + "operator": "AND", + "criteria": [ + { + "kind": "DataConnector", + "contentId": "[variables('_dataConnectorContentIdConnections1')]", + "version": "[variables('dataConnectorCCPVersion')]" + }, + { + "kind": "DataConnector", + "contentId": "[variables('_dataConnectorContentIdConnections2')]", + "version": "[variables('dataConnectorCCPVersion')]" + }, + { + "kind": "AnalyticsRule", + "contentId": "[variables('analyticRuleObject1')._analyticRulecontentId1]", + "version": "[variables('analyticRuleObject1').analyticRuleVersion1]" + }, + { + "kind": "AnalyticsRule", + "contentId": "[variables('analyticRuleObject2')._analyticRulecontentId2]", + "version": "[variables('analyticRuleObject2').analyticRuleVersion2]" + }, + { + "kind": "AnalyticsRule", + "contentId": "[variables('analyticRuleObject3')._analyticRulecontentId3]", + "version": "[variables('analyticRuleObject3').analyticRuleVersion3]" + }, + { + "kind": "AnalyticsRule", + "contentId": "[variables('analyticRuleObject4')._analyticRulecontentId4]", + "version": "[variables('analyticRuleObject4').analyticRuleVersion4]" + }, + { + "kind": "AnalyticsRule", + "contentId": "[variables('analyticRuleObject5')._analyticRulecontentId5]", + "version": "[variables('analyticRuleObject5').analyticRuleVersion5]" + } + ] + }, + "firstPublishDate": "2026-05-11", + "lastPublishDate": "2026-05-11", + "providers": [ + "Community" + ], + "categories": { + "domains": [ + "Networking", + "Security - Network", + "Identity" + ] + } + }, + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/', variables('_solutionId'))]" + } + ], + "outputs": {} +} diff --git a/Solutions/Tailscale (CCF)/Package/testParameters.json b/Solutions/Tailscale (CCF)/Package/testParameters.json new file mode 100644 index 00000000000..554801e41b7 --- /dev/null +++ b/Solutions/Tailscale (CCF)/Package/testParameters.json @@ -0,0 +1,38 @@ +{ + "location": { + "type": "string", + "minLength": 1, + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Not used, but needed to pass arm-ttk test `Location-Should-Not-Be-Hardcoded`. We instead use the `workspace-location` which is derived from the LA workspace" + } + }, + "workspace-location": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "[concat('Region to deploy solution resources -- separate from location selection',parameters('location'))]" + } + }, + "workspace": { + "defaultValue": "", + "type": "string", + "metadata": { + "description": "Workspace name for Log Analytics where Microsoft Sentinel is setup" + } + }, + "resourceGroupName": { + "type": "string", + "defaultValue": "[resourceGroup().name]", + "metadata": { + "description": "resource group name where Microsoft Sentinel is setup" + } + }, + "subscription": { + "type": "string", + "defaultValue": "[last(split(subscription().id, '/'))]", + "metadata": { + "description": "subscription id where Microsoft Sentinel is setup" + } + } +} diff --git a/Solutions/Tailscale (CCF)/README.md b/Solutions/Tailscale (CCF)/README.md new file mode 100644 index 00000000000..25c08cde364 --- /dev/null +++ b/Solutions/Tailscale (CCF)/README.md @@ -0,0 +1,62 @@ +# Tailscale (CCF) + +Microsoft Sentinel solution that ingests Tailscale telemetry via the OAuth2-secured Tailscale API. + +## Data connectors + +Install **one** of these connectors based on your Tailscale plan: + +| Connector | Endpoint(s) | Tailscale plan | Status | +|---|---|---|---| +| **Tailscale Standard (CCF)** | `/logging/configuration` | Personal (Free) + Standard | **Included** | +| Tailscale Premium (CCF) | `/logging/configuration` + `/logging/network` | Premium + Enterprise | Planned | + +The split mirrors what the Tailscale API actually exposes per tier — Network flow logs are only available on Premium and above. If you're on Personal or Standard, install Tailscale Standard; if you're on Premium or Enterprise install Tailscale Premium (which covers both endpoints). + +## Contents + +- **Data connector** — Sentinel UI card ("Tailscale Standard (CCF)") that uses OAuth2 client-credentials auth, polling the configuration endpoint every 5 minutes. +- **Custom table** — `Tailscale_Configuration_CL` (typed columns for actor, action, target, origin, new, old). +- **5 analytic rules**: + - New API access token or OAuth client created (Medium) + - Policy file (ACL) modified (Medium) + - Auth key created (Low) + - Exit node advertised or approved (Low) + - Mass credential revocation in short window (High) + +## Pre-requisites + +1. A Microsoft Sentinel-enabled Log Analytics workspace. +2. A Data Collection Endpoint (DCE) in the same region. +3. A Tailscale OAuth client with the **Audit Logs - Read** scope: + - Generate at + - Copy the Client ID and Client Secret (secret shown only once) +4. Your tailnet name (e.g. `tail-XXXX.ts.net`) from the [Keys page](https://login.tailscale.com/admin/settings/keys). + +## Connect + +1. Install this solution via **Content Hub**. +2. Sentinel → **Data Connectors** → search "Tailscale Standard (CCF)" → **Open connector page**. +3. Supply: + - Tailscale tailnet name + - OAuth Client ID + - OAuth Client Secret +4. Click **Connect**. Polling begins on a 5-minute cadence. + +## Why OAuth client, not a personal API token + +Tailscale's `logging/configuration` endpoint requires the `logs:configuration:read` scope. Personal API access tokens (`tskey-api-...`) are unscoped — when used against this endpoint, the API returns HTTP 200 but with `logs: null`, silently. OAuth clients are required to grant the specific scope. + +## Verification + +After Connect, in Sentinel Logs: + +```kql +Tailscale_Configuration_CL +| sort by TimeGenerated desc +| take 50 +``` + +## Support + +Community-supported solution maintained by noodlemctwoodle. Issues: . diff --git a/Solutions/Tailscale (CCF)/ReleaseNotes.md b/Solutions/Tailscale (CCF)/ReleaseNotes.md new file mode 100644 index 00000000000..87485d6bcee --- /dev/null +++ b/Solutions/Tailscale (CCF)/ReleaseNotes.md @@ -0,0 +1,3 @@ +| **Version** | **Date Modified (DD-MM-YYYY)** | **Changes** | +|-------------|--------------------------------|-------------| +| 3.0.0 | 11-05-2026 | Initial Solution Release | diff --git a/Solutions/Tailscale (CCF)/SolutionMetadata.json b/Solutions/Tailscale (CCF)/SolutionMetadata.json new file mode 100644 index 00000000000..116e0785314 --- /dev/null +++ b/Solutions/Tailscale (CCF)/SolutionMetadata.json @@ -0,0 +1,22 @@ +{ + "publisherId": "noodlemctwoodle", + "offerId": "TailscaleCCF", + "firstPublishDate": "2026-05-11", + "lastPublishDate": "2026-05-11", + "providers": [ + "Community" + ], + "categories": { + "domains": [ + "Networking", + "Security - Network", + "Identity" + ], + "verticals": [] + }, + "support": { + "name": "Community", + "tier": "Community", + "link": "https://github.com/Azure/Azure-Sentinel/issues" + } +} diff --git a/Solutions/Tailscale (CCF)/Tailscale.svg b/Solutions/Tailscale (CCF)/Tailscale.svg new file mode 100644 index 00000000000..b7d6e8a1905 --- /dev/null +++ b/Solutions/Tailscale (CCF)/Tailscale.svg @@ -0,0 +1 @@ + \ No newline at end of file From f556403bdaf946f008d9f687a91030295a6f71f3 Mon Sep 17 00:00:00 2001 From: noodlemctwoodle Date: Tue, 12 May 2026 08:20:52 +0100 Subject: [PATCH 02/27] Tailscale (CCF): add hunting queries + Standard/Premium workbooks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Expands the Tailscale (CCF) solution with proactive hunting content and two operational workbooks. The split mirrors the connector split: Standard-tier consumers get configuration-audit visibility, Premium-tier consumers additionally get network-flow visibility. Hunting queries (8 total): Configuration tier (works on Standard + Premium): - TailscaleFirstSeenActor — first-time configuration actors vs 14-day baseline (T1078) - TailscaleACLPolicyChurn — short windows with 3+ ACL rewrites, indicating rule-hunting or misconfiguration spiral (T1098/T1556) - TailscaleOffHoursConfigChanges — events outside Mon-Fri 08-18 UTC window for impromptu / suspicious changes (T1078) - TailscaleAuthKeySprawl — actors creating 5+ auth keys in an hour (T1098/T1136) Network tier (requires Premium connector + Tailscale_Network_CL): - TailscaleNewNodePairs — src->dst pairs in last 24h that did not appear in prior 7d baseline; lateral-movement candidates (T1021/T1018) - TailscaleTopTalkers — top 50 src->dst pairs by total bytes, virtual traffic (T1041/T1567) - TailscaleExitNodeUsage — egress via exit nodes, by node and destination, with byte totals (T1090/T1041) - TailscaleBeaconingCandidates — flows with 80%+ inter-flow gaps clustered at a single delta (10+ flows minimum); C2 / scheduled exfil signature (T1071/T1095/T1029) Workbooks (2 variants — install matches connector tier): TailscaleStandardOperations.json (3 tabs): - Overview: summary tiles, activity time-series, top actors, target-type distribution - Identity & Configuration: actor/action/target heatmap, ACL change history, auth-key lifecycle, API/OAuth client activity - Security Signals: rule-summary, severity distribution, recent Tailscale alerts TailscalePremiumOperations.json (4 tabs — Standard tabs + Network): - Adds Network Flows tab: summary tiles, top-20 talkers chart, exit-node traffic table, subnet router throughput, virtual- traffic-bytes timechart Manifest + metadata wiring: - Solutions/Tailscale (CCF)/Data/Solution_Tailscale.json: Hunting Queries array (8) + Workbooks array (2) - Workbooks/WorkbooksMetadata.json: appended two entries (TailscaleStandardOperationsWorkbook, TailscalePremiumOperationsWorkbook) with correct dataTypesDependencies and dataConnectorsDependencies so Content Hub gates visibility on the right tables and connectors - Solutions/Tailscale (CCF)/Package/ rebuilt with createSolutionV3: 24 ARM resources (2 dataConnectorDefinitions, 19 contentTemplates covering 2 DC pollers + 2 DC connections + 5 analytic rules + 8 hunts + 2 workbooks, 2 metadata blocks, 1 contentPackage) Testing performed: - All 8 hunting query YAMLs parse cleanly via PyYAML - Both workbook JSON documents validate - createSolutionV3 build succeeds; mainTemplate.json, createUiDefinition.json and testParameters.json all valid JSON - Total ARM resource count went 10 -> 24 as expected - Hunting queries reference correct connector IDs (TailscaleCCF for config-tier hunts, TailscalePremiumCCF for network-tier hunts) so Content Hub gates them on the appropriate connector being deployed Live deployment to test workspace deferred (Azure session expired); will redeploy after re-authentication. Breaking changes: None — additive only. --- .../Data/Solution_Tailscale.json | 14 + .../TailscaleACLPolicyChurn.yaml | 31 + .../TailscaleAuthKeySprawl.yaml | 31 + .../TailscaleBeaconingCandidates.yaml | 37 + .../TailscaleExitNodeUsage.yaml | 26 + .../TailscaleFirstSeenActor.yaml | 34 + .../TailscaleNewNodePairs.yaml | 33 + .../TailscaleOffHoursConfigChanges.yaml | 31 + .../Hunting Queries/TailscaleTopTalkers.yaml | 23 + Solutions/Tailscale (CCF)/Package/3.0.1.zip | Bin 13726 -> 22108 bytes .../Package/createUiDefinition.json | 194 ++- .../Tailscale (CCF)/Package/mainTemplate.json | 1174 +++++++++++++++-- .../Package/testParameters.json | 16 + .../Workbooks/TailscalePremiumOperations.json | 410 ++++++ .../TailscaleStandardOperations.json | 314 +++++ Workbooks/WorkbooksMetadata.json | 35 + 16 files changed, 2294 insertions(+), 109 deletions(-) create mode 100644 Solutions/Tailscale (CCF)/Hunting Queries/TailscaleACLPolicyChurn.yaml create mode 100644 Solutions/Tailscale (CCF)/Hunting Queries/TailscaleAuthKeySprawl.yaml create mode 100644 Solutions/Tailscale (CCF)/Hunting Queries/TailscaleBeaconingCandidates.yaml create mode 100644 Solutions/Tailscale (CCF)/Hunting Queries/TailscaleExitNodeUsage.yaml create mode 100644 Solutions/Tailscale (CCF)/Hunting Queries/TailscaleFirstSeenActor.yaml create mode 100644 Solutions/Tailscale (CCF)/Hunting Queries/TailscaleNewNodePairs.yaml create mode 100644 Solutions/Tailscale (CCF)/Hunting Queries/TailscaleOffHoursConfigChanges.yaml create mode 100644 Solutions/Tailscale (CCF)/Hunting Queries/TailscaleTopTalkers.yaml create mode 100644 Solutions/Tailscale (CCF)/Workbooks/TailscalePremiumOperations.json create mode 100644 Solutions/Tailscale (CCF)/Workbooks/TailscaleStandardOperations.json diff --git a/Solutions/Tailscale (CCF)/Data/Solution_Tailscale.json b/Solutions/Tailscale (CCF)/Data/Solution_Tailscale.json index e90cbcef696..971860475ea 100644 --- a/Solutions/Tailscale (CCF)/Data/Solution_Tailscale.json +++ b/Solutions/Tailscale (CCF)/Data/Solution_Tailscale.json @@ -18,5 +18,19 @@ "Analytic Rules/TailscaleExitnodeadvertisedorapproved.yaml", "Analytic Rules/TailscaleMasscredentialrevocationinshortwindow.yaml" ], + "Hunting Queries": [ + "Hunting Queries/TailscaleFirstSeenActor.yaml", + "Hunting Queries/TailscaleACLPolicyChurn.yaml", + "Hunting Queries/TailscaleOffHoursConfigChanges.yaml", + "Hunting Queries/TailscaleAuthKeySprawl.yaml", + "Hunting Queries/TailscaleNewNodePairs.yaml", + "Hunting Queries/TailscaleTopTalkers.yaml", + "Hunting Queries/TailscaleExitNodeUsage.yaml", + "Hunting Queries/TailscaleBeaconingCandidates.yaml" + ], + "Workbooks": [ + "Workbooks/TailscaleStandardOperations.json", + "Workbooks/TailscalePremiumOperations.json" + ], "Metadata": "SolutionMetadata.json" } diff --git a/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleACLPolicyChurn.yaml b/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleACLPolicyChurn.yaml new file mode 100644 index 00000000000..18ed36bcd13 --- /dev/null +++ b/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleACLPolicyChurn.yaml @@ -0,0 +1,31 @@ +id: b82e5d4d-2c8f-5e3b-ad2e-1f4c6e8d9eab +name: "Tailscale: ACL policy churn" +description: | + Identifies short windows where the tailnet ACL policy file was rewritten multiple times. Iterative policy edits during an incident can indicate rule-hunting by an attacker or a misconfiguration spiral; legitimate ACL refactors usually land as a single change. +requiredDataConnectors: + - connectorId: TailscaleCCF + dataTypes: + - Tailscale_Configuration_CL +tactics: + - DefenseEvasion + - PrivilegeEscalation +relevantTechniques: + - T1098 + - T1556 +query: | + let bucket = 30m; + let churnThreshold = 3; + Tailscale_Configuration_CL + | where Action == "WRITE" + | where tostring(Target.type) == "ACL" + | extend ActorLogin = tostring(Actor.loginName) + | summarize EditCount = count(), Editors = make_set(ActorLogin), FirstEdit = min(TimeGenerated), LastEdit = max(TimeGenerated) + by bin(TimeGenerated, bucket) + | where EditCount >= churnThreshold + | order by EditCount desc +entityMappings: + - entityType: Account + fieldMappings: + - identifier: FullName + columnName: Editors +version: 1.0.0 diff --git a/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleAuthKeySprawl.yaml b/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleAuthKeySprawl.yaml new file mode 100644 index 00000000000..8c47377239a --- /dev/null +++ b/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleAuthKeySprawl.yaml @@ -0,0 +1,31 @@ +id: d64e7f6f-4eab-7a5d-cf4a-3b6e8aafbcad +name: "Tailscale: Auth key sprawl" +description: | + Actors creating multiple auth keys in a short window. A single admin creating many keys for unattended enrollment is normal during a rollout; the same pattern can also indicate token-spraying for persistence. Cross-reference with the Tailscale: Auth key created alert to filter context. +requiredDataConnectors: + - connectorId: TailscaleCCF + dataTypes: + - Tailscale_Configuration_CL +tactics: + - Persistence + - CredentialAccess +relevantTechniques: + - T1098 + - T1136 +query: | + let bucket = 1h; + let sprawlThreshold = 5; + Tailscale_Configuration_CL + | where Action == "CREATE" + | where tostring(Target.type) == "AUTH_KEY" + | extend ActorLogin = tostring(Actor.loginName) + | summarize KeysCreated = count(), KeyIDs = make_set(tostring(Target.id)), Reusable = make_set(tostring(New.reusable)), Ephemeral = make_set(tostring(New.ephemeral)) + by bin(TimeGenerated, bucket), ActorLogin + | where KeysCreated >= sprawlThreshold + | order by KeysCreated desc +entityMappings: + - entityType: Account + fieldMappings: + - identifier: FullName + columnName: ActorLogin +version: 1.0.0 diff --git a/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleBeaconingCandidates.yaml b/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleBeaconingCandidates.yaml new file mode 100644 index 00000000000..c7990870aa0 --- /dev/null +++ b/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleBeaconingCandidates.yaml @@ -0,0 +1,37 @@ +id: b28cbdd2-8cef-be9b-a38e-7faceedfdbec +name: "Tailscale: Beaconing candidates (regular periodic flows)" +description: | + Detects flows that recur at a highly regular interval, which is the signature of C2 beaconing or scheduled exfiltration jobs. Looks for src->dst pairs where 80%+ of inter-flow gaps cluster around the same delta. Requires Tailscale Premium or Enterprise. +requiredDataConnectors: + - connectorId: TailscalePremiumCCF + dataTypes: + - Tailscale_Network_CL +tactics: + - CommandAndControl + - Exfiltration +relevantTechniques: + - T1071 + - T1095 + - T1029 +query: | + let lookback = 2d; + let minFlows = 10; + let beaconPercentThreshold = 80.0; + Tailscale_Network_CL + | where TimeGenerated > ago(lookback) + | mv-expand t = VirtualTraffic + | extend Src = tostring(t.src), Dst = tostring(t.dst), Proto = toint(t.proto) + | project TimeGenerated, Src, Dst, Proto + | sort by Src asc, Dst asc, Proto asc, TimeGenerated asc + | serialize + | extend NextTime = next(TimeGenerated), NextSrc = next(Src), NextDst = next(Dst), NextProto = next(Proto) + | where Src == NextSrc and Dst == NextDst and Proto == NextProto + | extend DeltaSec = datetime_diff('second', NextTime, TimeGenerated) + | where DeltaSec > 5 + | summarize DeltaCount = count() by Src, Dst, Proto, DeltaSec + | summarize (MostFrequentDeltaCount, MostFrequentDeltaSec) = arg_max(DeltaCount, DeltaSec), TotalFlows = sum(DeltaCount) by Src, Dst, Proto + | where TotalFlows >= minFlows + | extend BeaconPercent = round(MostFrequentDeltaCount * 100.0 / TotalFlows, 1) + | where BeaconPercent >= beaconPercentThreshold + | order by BeaconPercent desc +version: 1.0.0 diff --git a/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleExitNodeUsage.yaml b/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleExitNodeUsage.yaml new file mode 100644 index 00000000000..cd2929ff458 --- /dev/null +++ b/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleExitNodeUsage.yaml @@ -0,0 +1,26 @@ +id: a37bacc1-7bde-ad8a-f27d-6e9bcdcecadb +name: "Tailscale: Exit-node usage patterns" +description: | + Summarises traffic leaving the tailnet via exit nodes. Exit node use is typically intentional (regional egress, privacy routing) but unexpected exit-node traffic from a node should be investigated as a potential pivot point or unsanctioned egress. Requires Tailscale Premium or Enterprise. +requiredDataConnectors: + - connectorId: TailscalePremiumCCF + dataTypes: + - Tailscale_Network_CL +tactics: + - CommandAndControl + - Exfiltration +relevantTechniques: + - T1090 + - T1041 +query: | + Tailscale_Network_CL + | where TimeGenerated > ago(1d) + | where array_length(ExitTraffic) > 0 + | mv-expand t = ExitTraffic + | extend Src = tostring(t.src), ExitDst = tostring(t.dst), Proto = toint(t.proto), TxBytes = tolong(t.txBytes), RxBytes = tolong(t.rxBytes) + | summarize TotalBytes = sum(TxBytes + RxBytes), FlowCount = count(), ExitDestinations = make_set(ExitDst, 25) + by NodeId, SrcNodeName = tostring(SrcNode.name), Src + | extend TotalMB = round(TotalBytes / 1024.0 / 1024.0, 2) + | order by TotalBytes desc + | take 100 +version: 1.0.0 diff --git a/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleFirstSeenActor.yaml b/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleFirstSeenActor.yaml new file mode 100644 index 00000000000..3f05846f874 --- /dev/null +++ b/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleFirstSeenActor.yaml @@ -0,0 +1,34 @@ +id: a91f4d3c-1b7e-4f2a-9c1d-0e3b5f7c8d9a +name: "Tailscale: First-seen actor making configuration changes" +description: | + Surfaces actors making their first configuration change in the lookback window. New legitimate admins look identical to compromised credentials — review whether each surfaced actor is expected to have admin rights. +requiredDataConnectors: + - connectorId: TailscaleCCF + dataTypes: + - Tailscale_Configuration_CL +tactics: + - InitialAccess + - Persistence +relevantTechniques: + - T1078 +query: | + let lookback = 14d; + let baselineWindow = 14d; + Tailscale_Configuration_CL + | where TimeGenerated > ago(lookback) + | extend ActorLogin = tostring(Actor.loginName) + | where isnotempty(ActorLogin) + | summarize FirstSeen = min(TimeGenerated), Actions = make_set(Action), Targets = make_set(tostring(Target.type)), TotalEvents = count() by ActorLogin + | join kind=leftanti ( + Tailscale_Configuration_CL + | where TimeGenerated between (ago(lookback + baselineWindow) .. ago(lookback)) + | extend ActorLogin = tostring(Actor.loginName) + | distinct ActorLogin + ) on ActorLogin + | order by FirstSeen asc +entityMappings: + - entityType: Account + fieldMappings: + - identifier: FullName + columnName: ActorLogin +version: 1.0.0 diff --git a/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleNewNodePairs.yaml b/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleNewNodePairs.yaml new file mode 100644 index 00000000000..ac682cb2d1f --- /dev/null +++ b/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleNewNodePairs.yaml @@ -0,0 +1,33 @@ +id: e55f8aaf-5fbc-8b6e-d05b-4c7faabcadbe +name: "Tailscale: New src->dst node pairs (lateral movement candidates)" +description: | + Lists tailnet src->dst pairs observed in the last 24h that were NOT observed in the prior 7-day baseline. Useful for spotting lateral movement to nodes that don't usually talk to each other. Requires Tailscale Premium or Enterprise (network flow logs). +requiredDataConnectors: + - connectorId: TailscalePremiumCCF + dataTypes: + - Tailscale_Network_CL +tactics: + - LateralMovement + - Discovery +relevantTechniques: + - T1021 + - T1018 +query: | + let recent = 1d; + let baseline = 7d; + let recentPairs = + Tailscale_Network_CL + | where TimeGenerated > ago(recent) + | mv-expand t = VirtualTraffic + | extend Src = tostring(t.src), Dst = tostring(t.dst), Proto = toint(t.proto) + | summarize FirstSeen = min(TimeGenerated), TxBytes = sum(tolong(t.txBytes)), RxBytes = sum(tolong(t.rxBytes)), FlowCount = count() by Src, Dst, Proto; + let baselinePairs = + Tailscale_Network_CL + | where TimeGenerated between (ago(baseline + recent) .. ago(recent)) + | mv-expand t = VirtualTraffic + | extend Src = tostring(t.src), Dst = tostring(t.dst), Proto = toint(t.proto) + | distinct Src, Dst, Proto; + recentPairs + | join kind=leftanti baselinePairs on Src, Dst, Proto + | order by FirstSeen asc +version: 1.0.0 diff --git a/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleOffHoursConfigChanges.yaml b/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleOffHoursConfigChanges.yaml new file mode 100644 index 00000000000..a8858f9cc40 --- /dev/null +++ b/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleOffHoursConfigChanges.yaml @@ -0,0 +1,31 @@ +id: c73f6e5e-3d9a-6f4c-be3f-2a5d7f9eafbc +name: "Tailscale: Off-hours configuration changes" +description: | + Lists configuration audit events that occurred outside typical business hours (defined as Monday-Friday 08:00-18:00 UTC). Adjust the time window to match your operating region. Useful for spotting impromptu maintenance, account compromise, or insider activity. +requiredDataConnectors: + - connectorId: TailscaleCCF + dataTypes: + - Tailscale_Configuration_CL +tactics: + - InitialAccess + - Persistence +relevantTechniques: + - T1078 +query: | + let businessStartUtc = 8; + let businessEndUtc = 18; + Tailscale_Configuration_CL + | extend HourOfDay = datetime_part("hour", TimeGenerated), DayOfWeek = dayofweek(TimeGenerated) + | where DayOfWeek in (0d, 6d) // Sunday and Saturday + or HourOfDay < businessStartUtc + or HourOfDay >= businessEndUtc + | extend ActorLogin = tostring(Actor.loginName) + | extend TargetType = tostring(Target.type) + | project TimeGenerated, ActorLogin, Action, TargetType, Target, Origin + | order by TimeGenerated desc +entityMappings: + - entityType: Account + fieldMappings: + - identifier: FullName + columnName: ActorLogin +version: 1.0.0 diff --git a/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleTopTalkers.yaml b/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleTopTalkers.yaml new file mode 100644 index 00000000000..c6d15a32a93 --- /dev/null +++ b/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleTopTalkers.yaml @@ -0,0 +1,23 @@ +id: f46a9bb0-6acd-9c7f-e16c-5d8abbcdbeca +name: "Tailscale: Top talkers by bytes (virtual traffic)" +description: | + Ranks tailnet src->dst pairs by total bytes transferred over the last 24h. Useful for capacity planning, identifying data-heavy flows, and spotting unexpected volume that could indicate data staging. Requires Tailscale Premium or Enterprise. +requiredDataConnectors: + - connectorId: TailscalePremiumCCF + dataTypes: + - Tailscale_Network_CL +tactics: + - Exfiltration + - Collection +relevantTechniques: + - T1041 + - T1567 +query: | + Tailscale_Network_CL + | where TimeGenerated > ago(1d) + | mv-expand t = VirtualTraffic + | extend Src = tostring(t.src), Dst = tostring(t.dst), Proto = toint(t.proto), TxBytes = tolong(t.txBytes), RxBytes = tolong(t.rxBytes) + | summarize TotalBytes = sum(TxBytes + RxBytes), TotalPackets = sum(tolong(t.txPkts) + tolong(t.rxPkts)), FlowCount = count() by Src, Dst, Proto + | extend TotalMB = round(TotalBytes / 1024.0 / 1024.0, 2) + | top 50 by TotalBytes +version: 1.0.0 diff --git a/Solutions/Tailscale (CCF)/Package/3.0.1.zip b/Solutions/Tailscale (CCF)/Package/3.0.1.zip index 38245235a0ec7fb220f34daca7cffcf2e7f5135f..f42b2df7d903435791266c2aa1526694c8003cad 100644 GIT binary patch literal 22108 zcmZ^p1B@@hzvaiaZQQYKTX$^Rwr$(C@BGHLZQHzK?)|^_c9Y#?lTM|o)9IZ4_DS`p zPnDt!C>Rgl$Fe8j*QebQ|HrwzIl*P?`Gg< z(ARw;l1MzRd;6NCaV-rStZx}S+(()rXO1>-h=a(1NGF|YN%10Afndq84kk)*m6i4q zzO7BmTXBM0TJ8P*R`9?$TJ?lZpJjZO6IsXn@~U*qJwX}kxL zW{rbJPm^Cj#<-A*E$E`}I_Q`}$yyeRb6BS}Vu4<@A%He%K;G5CwMc+#aS^P5t8Jch z0Y@OmIGe&r3+m^f+&Pvoj;`bw+duk@gg-n$ zjrS+0<++Saiz*Wb4>4Tb3T|-!(_Xo60$hOO{(Ep^}fymXY z@FzWTNs~f_U24rZ`(h%%00V{_1cyNNV0 zV&{y(O)ke8tuYqIo%Znjm}>w@S^L6obL)kDe8x?QEl#$vN=OPi#@h!Nu2YCn2>-_cm~eKR?=P^tXj$;cON<8NaD#cS!ltw)sa)t( zm-!hF;-wNho_W@zJ!yP$Bsq3Q)hEnT%yL%HF=K7^ZG!;uPoS*&^Z4FC4dDl$_=xf3M^QUv&W`m3qr{CeVn>{*n zC3-p;b@*r_5-raY+YOb!eANE|EEn#3wix;TP2}SJ0WMQXF+t9l<$4>TO}DwSzkD^! z705sMvYWN+YnqTlE%VX^3D4zNxi-Hg_hMudN_H zvmBl(lXVaf)^*N9k^f7Ee?=aO7hpoQA*C75XNVFDJJ8eKERSf|YiH+O#*!EoW|C`D zzkDP1_h*$(Nh-HV*NA$KX$Ei8OWsD>Auh1`Q_4YDYblLAyCcF(NzN>9G#!Y zykNxQVotfs@z;KOq;_)7s2LXCFBw%iC&j2Ll|{>qe?dzRD$D0KXlp@7vM7~N+djmk z|9A>`L96IT*h7sDe_*4(XlRMp-aXf97!y1&cT&#By^^h1(XEwbfY*y8>g9S8OA=op_;{59>OR(uYqbi8FL8Y@XU4np)oo861Fu86 z%_uK2AFl&Y=(#469&wo|gsc0iges_mJIA5Rw8S^C{@iCVc~nC;#9>43tk_>fUP-lc zpdf0!R4ZL-E9so(vImVj7N$CE#Ec6=vi0avs>9xIj}JOGsdnJQyMoV8+_J4F$raf) zd~L&Px%p0XmUqi+`t5x8f`jT#Z?=p;+#IFivR{B@CGg7EQ^rdW2_r1m2A0axhHA&} znRr21&ZvYXz+V2*j5jiKc5?a#%h4+Up*qQ^Q;sa&vlW5J_i7|jmffHm-WVBv+SUJ* zUBbJ@W<%F(H=VmGaq5MbTiUW&nE2{N7F3kB?}S9cb>4CB02@65iN#my>5EN0f!b+; zlQ8&-NtzV*X1<{3*f94z-`-0vJnjA^k_^2ig?(}L*H$r1m-LHdqQTkeRsuVVYn#jT zEbnOLFUa9wrlFFwczBsTJn%(wM%a(LSCjVvFfNnd6mLK=Daox9N)vwh4!7>-_eXSz zq(02|nGWvgQJd!Rq{Hv;Cf!+w>aIeJcLI+=CZAXu?pTPlG8cMm-#qqh(4Gi(Kc2cm z&6(}CFjU3crX%#`r>4sxdsp!cMf6~?F*s!e>0*|_sG!6PRlX!h%_8K#9?atho@GUG z!e3)OR3dcye2QVloU6Y}thrf*&hlkvArvj>PYwY4j_Ol}&GVv&l&040A(38->GltQLWn zUm7v9Hw#7$UX~$2aOm^WK|1LN&zMWd+UJ z7r6SVOe}&HOl?cBU`&vu0(k5MSD?81-6#kkG-f`LBaw>ZCIP7~+@6~no(}37mF#?E z78a=0EhwR=GetR|%_>5{Eu7*)P%y&CQl#)z&(Z}4lZoo;e5kZ85mqj6YB4jscJI+< z;X1KWjV|y@j4T!}g!L=9cW?ZRk16YWY1K{@1q6*CK{TMDhnhMz9n<{#g-3(i$& z6X%eb*F-hPW@5NShs9r6#Vy3Z)8?)2pwY9Dp(+KV8%l{*cgXHoF${k5QlovjsG^Tg zb!vDF_tnZIGDBC2mia)FXR;Lsg!YxRKvvRA7sDFfgNiqM(nh~Csfk^~S+-J_+wouf ziVVKMGIu7wr;24Q9~ES6Ym>?zf?l??eVIzScpnSnltv}m!Ex-T4`*wmd_e`zScSIY zG69AFJ2Fci)G@Bc(G^u^Wd^W8k{tFJ%GJ+qvX16*?#6NX-YkeMhKADsy+4ymSQ*KZ zZZHLLgrzId*fN-@;nKoabWEu7XvCQ!V0~d`s!dr}^^0_{liFb%BboP6U^PxITsVSh zCkBVSo-G_j!&Fl4>rFVYvYh!v0cvY;S@nU&kx|;zsdmr4+ohBq7x`fk(!efinF z9O`Je_Hx(>Mfm4w-VLaAnKPMDa8=%nlbwo9$Y*@;ZUvB%6O*k?ya|+=suDXIim>a9 zwY>=_7_Wi?TtRYEW-%fDlStV07xI-|BFq_(7YYgiDgy+?dQKWlDoc=3#!)r`^UJ=t zT;p?+fk+`@8}&sSD!Alc>uS{LrM<2NReQc$R#k=2b60$ zwc{0H_iW(95#tdNM_~zv207nH{O_FrL%*^N5p`- z7k9qs!zf+&%Hu@GO+yS??s17z1p{Vmytp6;qmy0WSKFQx%B;=9cwi$L)h}8p;CjKLOb#L z*pPX2kD&&kbgDsD6-le~3m}NY=FsHlh$Sj^?e%SHX(<0#rprIp>rUf!HHWG2^=K+p z@dUWnlJu^JN9ov9NkrVttRT=mrVnr`AQu?^Z9mg#?j+BBo!&khVkyJmxEQj5m**)` z6oQ#yG(E+VT47e1wRtQ7eNRcHEuEn4fR*DIyiBV1bbu(N<&XgAKt^m-{_CqlH-e%*FQuB`+J!M(7MDm0nUW=few!^@cvl zRci6&I{2mx_uCNc2^6EPpwyyjnuwzDr*p?llBF0`;-_f+l2Ck{U`BV#d5;g5|5>kk za3HLDfGoMhAU@xiD+2;z&@AW{cHWS0a`kq=N4ypHCkS7l#Z5<-`%@UDW8iYmf~iTr zst8M*6G)$l*H4XUN$?mJ1XZ;K?>{ayYv={mES@BqyMWi8F2ojbr(XDj=w*h=S&Z^J z*3cnOb23gq;P*iFggt@}Ns`jIAWY5owT@;W?mf&A2q$sAHd=L^1qb8*3i;U z#njfp=3iFu-}!*=xzlDF;O55r(|dkYW((Xdhy9pY5o23;~x+seSeqapl{Pq^Z-Kqfnhv5sdX zHR%lH)$vK0&u>CwRK)R&jIOv|ZuUy+l{Mc{cJ0^`z1sOYwybRKOso~O3EhPIp}w4@ zUdGAKcxm*FRoZ0KT>~pdcwDer_fC6u>RsYTIUm~~TV9HsHQLua zLrbizyL=**{YB`&6YSPXE*Al~rQd%uGDP^RxT|Wap0k#A47FEjbI<&jk>5p;*sv)z!qt!oksnuMP4ye0S0txN9~fL% ziZ-B>!d#3Nt&55SZDYmm4?&OAfhq@j?GJi|d!SU!HYZ6}E5KkU&Jm}X*``)k`4b8i zAz-CSMW+@K>t(R2mXSycpbBXFG9-Q(4H4mdYE?r-HT9fy>u;{TvIJYEuA1bbw9p{d z=Xco?qsEtZ5NeCK7#JB(RF2F|N34P6Doz73*!IqHGIFwp0uW3?c#=H`gt@(=Mosek zvgdsY8Y@=3Z3~E5N!D|csZJi3%cB1p8;q>34;A?6+FJV-%RfION5Caq)!&YXw*j;TdaW$Luda$P!l`{BvlNPb9=rRjgvSTV) zdIbDFmrrmu(O%$-Xxa0-p~vwJQ_p^@#-=E*OxJo9?>xeI+rQP zYlaWF1Jv8wyLER5=ZwvEZlm_vmc!AuH5js`duGP2ybzH)c133#Hv{cd!0AqEV zH-EOavlji_j-TS;($4y+BRjfuprbRjuv5F;RXt_vhJ%~VR=wWUrAeE#ZMTB$g?qE* znr~}kbAA10uU0MT#*WT#XZrqatE01}%98^V_084&5_qc9Q-ivu^BQj3_H;#8$!X3G zUduc7Itw5Ju&$-+RwBLDtzB8OW0Nc|bmyKH|0f0Wf23UhZzEz*&x#3(5G&K2f?!D>GSVs&-RR380y6jRODo=E94COG}GZz74z;-PXK2 zgf|%CyB7brU9Du3C+8UXx?7mZW;;2J^u?xa6U!RG(gxUzHBTF8_!E1b?NjSGJw#PQ zo{j%G~W=RtlmkN2nbUaxnz!q!->{@Ri!7{}7ix+ShB`omZ57M1Q42GIRK^4Q&=+$Rq87g!Aou*9Rfl}`;FGkixx7OJ`A<)0Hu3uQRuVe- zN3Cvc{blV&oj61b|N7HC%&u1b+biEam#P>NUKR0}`p`EkB=S^9AOez2(4a ze%`sT!^h8fXJI^Rf?w*M^DTOKa!S*1KDkJ@(EU!G39QeeLgz&kxlVUqG-iVpHPY&k z&6lYTaiz^61^d>%nqD4OTy#LB{&_AIaThE`b;;Nh3$B`~!mjf@oe zJa(5kjpb$kzmc|7vG79=rl}JGK_(MNe4ghGZ9I#q>Q2#$0=<2YVXFOO0_6*@#t5%u=$V z{I5pmfXKXj(g`?3xBt71oQ<`xjXZiY-~HBZiI{n|@QQBWr(jTu92{?M3;3fOGjXwC zYE*tD)fuOXKy4bXv6a^Z%E94{+;{OGYLU};-j2#BixIOQ?K9o60p?FDx466S$|jAI zg51qLhAD^7n8hl#=L0jm-VqQ~{fM;yR`!oXu5y^(@6>95Zn&ehY;RDTnGAW1plUr? zEurNqGo%ZaHk^rYd!oBZ<*xfXgej>JWXT97YfT|}0@KvJ4EKmg(|BF@?rvNx;d@5n zwcFFxw&mr^`K8;>Vr{Xnt>Wj6hzQT0y7<;dI^T_T?o7A3mPR+y$whFtgevW=0kEkDCT~huZE>=H1Pm3+zmCjFoTTG9& zBRBUemcc$9Y%@JuJD<;&kkv2G`rh5u;+KzI+ZXr*ath_wwYxoetns}$eeT+ni*Bc{ zMXUVZ51V(7laYVFyNL6AKfPR>7i`_V!XXD283=Qgy&M*Kc%63K5zB5ni|AhICam;R z-ybwP-Cs9X4;e%@yuBydD@@gqCd>`jzP+IX& z=jIY@e|=wVUnYLLaejU^2rFdT--l7w_<7D!2TT0$5jGFKIdsyk? zi_7Q7mB3RchXBDoFfaK==hX=P;V_h_QW7Er8{w_%QbA zL=f0PK?Mwm?gBYSnSdC~b`%6)5h%bhw|F(->2mC74sM?5&IN2;Ul5_Pg=nFKiQ;?Pd3B> zjLM-I-dF9g$lYZ=8!c3n?8C$11?~xc;hIpohS5pk^-OFI`8X*e`9r9RhIr25m3ftk zASt=+SbZ`>w&6bM%D2?0M)6djvc%sy_(5_rIxa*?e&Z-9lpghNc7h~I(Ss#~(oTN^zb6cU$NZQ*}x`V55Q4i zeoNR_R2EkrzkcXoPxPrS=8vZoLK7st`dX+sjsKQO*0B%{CSGi{Q%+&y7nU~C1bvA_ z4qMfcSMCMYXKD}^PurJAR;+%$tndBSI<3ho?ifW5mm(^zX}xx#LM7!Z6<;8d)R`g- zcrE33FXewhfQ9*~(o<0D2M41cRafv|Sm3A3($(2sN6+loEY<=(7g@{4om`223XnOF z_9DJ3>S+iQ3GQ;^Y@IrGjp{t)d43=H(n||)nJ8jI{E~2%rrw{nxiic8u4YLZm-mCIBe%Y6L@s4(xvDw`k zM}M1|He3j8)xYy9+%#)NUgwfpjVg5Dlqv&vysj#ytILbINl;u&4lq|nWkDQrIFMNU zpp4D3DCyRbV=-rLw8>j`Tg8dTN1f?vv;juSDb5g@rdc*HlzbaIkEVME)}S8ysrLkW zrZAl%rX?g@NC?f)TJV5a&cjvE2ot7f@7bE;p@R4BIG$ECJSqmdbCj@7?)}Dh6FAaF z16aqJ%-*{vo`-`<$^g-5 z10c7ci3%W_FVGHRm*-h2?~yCefjz)S7~udNKDEbj?JKfFpH$FGMnKIadwoWUH+6za zI}CJiiUjeq-Dg<2i-N`^rcMOPhMoJ2)38oLd@$OM|6O%@Odz-2lwo9ASm zqvjaG`<`AO(D?rb8;(GCFOj?uzK%?3|HuIf(ld#TOGY?(jWygRYs~fV2DX;i_>*Tm zA8~sYa+x5>LE*<2O&=>Q=c4LOl@Z;o|3Cj+>V-&nujMxIHaeff3#ZHoQsf)QKHIch)nBaKw>!$H)tuiQ<8r zJw@ZqXK^?yKwT*lE#EEGk*%F*=g8c5q1?ePOF#SLoj%i{Id&r~-;TJ7-Ac6S%(KmJ zWxjdTm@rxCCSJjnYmqFFeL+|NJ%?;h1I(IRXHD1A3 z?{r=VBK(C6;4@PIo+AhgO+9`7w_loSInr7Fbh=AEyFHf;GhGkZd`f44_QXeX>zq04 z@OGijCy4{qQa)w(1~j!-`E=}4K2gRsOjdLuKmG5a?hyEllbg<1IqgonBSHDd!AuO5 ze9T3xRn4Lipt)#6w9DBjOxY-m&r*KiK=0#{bhWDZ3S6 zdG*kYY_|_-X?&V12?j;2!V-liK=45^pnB&399jHi|cheqG|uoPj{w_^_pN$dtSE?^yVCoqS*B~Atcoa(ZV{> zaSakW%??==Nm7ZQis4%wXCE|TNJGDM@1hC5!qYNZU>%Jd?&JED5-dXury6r-`oPGg zEq{WzVZ=>(byKh+9@{!g(moVMph}n;leEw0V2raMb&N_ILW-^Bh!)l za#nGq<_7r0ts(}9qE}-T&oA3tEU322IJMs6vM%BG9{flnh!iRl#jhsWU3`eaGtiyF z4YgDjtjUd)(`$fl{U9?`z|V4BPZ6kI0L??gE8HBYl3|bW+t7%qUp_z>1jE5xL&I`TO{RX7Zm^O47mtK zGM-7tWHvA+zmsvNm=8btClC?n0hDxF-okyy^J`s}&LO_|{+c?gSSMM~E(cqdl@J!C z{Jmz%ka*g{S^IeUgJ@`(a)qU(+Uz_}VZ--pGE6X<5{`i+M4Vjt^2LpDDY;dKoo;U~ zf?kuWmy-K9TjbKc0O#yyeCWt17Ud!l^Uw@08}x>v7!HB{9K1!Qio=*=jmM!X%C*+{cY?msQt5tvRZubgwN%=`0+z(iXlQDYQo<0!2e3k3I+W|`(0 z@kxAfG(Vb<#p@GW2p%bc=bNrULJH^Jryo1(u$;TY`Fb#3`<|@`v;7}8Z`)Lr*{Wj< zx;u1y6`iG$FGromFkEycI!t_>*`?)aY*6*c}4Au?{eZK|tDd6e9{A7A}7j9c0 zKj^;v@?948;2#gbU#q4)7J57i)tvNyTq*Sip!s_nzxyi*hVAw}DEAtMr@G4!Ze|kC zTvM?_s-qdbtE?7ybF|XbSl#p(dCifOiZ%&tw{PGNy zb=9IAFBIuSI4fwFqol63F4ffZnqUL zrdEiwI>}=vS3GmqH^kA;W5Nk9W9(IsdwtS?r8RRM4ehK@Jz;#23tVSe zMt3XZttq4xO^t%?Qt|#an;!4$NGm><(t;!Kk1%Xz-l|lI2MY`$)oO{jC(YDV_#^Ij znYNBHT?G{Sr%%wI-AZDZmiRx!?T3>t+ol~iARV)MXx$Z-hlqNG^6ULS>$fEy>Q06w@n+S?z_A2cBNSCG7r7^g(vWKP2+AEk^eQz zN3V4!Sv4uu$DO%lQ;&G=FU$me4;18+yL{8h<1?BslA=kSt9(g)%*?mjvu#Vw%y-*} z*sE{M_uGg}wCdtLABEub8H5nz$vN2XHzw?}o+-xvaAi|<5QiXREyJm5+R^#g{vMQ- zvSTrhD#`2pA$5kY3!*h48n5f1B$zAAq*%&^QqN#eipcgUo8j0OzT6d_=Wk!SUTn}q$P`D7XfP&YA^^53U>fQGvY-9YmX6^&%vE@p=uzFtk7{}?r2&?pQd{ogs3HyZ>8#*HMoJA#MAS!jI|NFZk=?I|RYEfDSC!Y^JAMgBtKlvV@Ay zW6a2s46%=2O(li0EY$k^52uNhS!$l2YNS)DgS?Wsv_J7ak@n5uavc*h^tk@?T$A2z z1m(=DA4VWP!q~fC!=|NRCV{T*Ssr?{#Wd0Yy49QIyKA#_E%-yHLXb(-B(z2x=wg-E9qAT8R^??+Z(e`dOr_x=Y|Sn8%~q-Rkj zPR2*#$K!C5vxcbYT*ULv;D!$ermzty6f}~$?)9D4Q+Jcs9)_K%kVZ=r3Kk9kyc$%c zBZiqQ-_kp6H0_7AwDjE|_bN`%%^II62ad`{oZs}m0;JiEeDLB3(<4o8jKxoeUIY>WS#!56ux_Hg&bv{2C*k{l5UU1b)d}noKIvM)8G1A)B@RF@)IaXVZ)Wm z-_lb2*k;6|R_v!B8Isdqr=2>V71vIxVMyJ{q@3C1G-=q3tmFlTEA!-K;@C{AD%CMq zIEG{$R^_!!R{roLve6+{x7*K$aiYB^Bw7;eePI_e?bjD z4TMX1qh{O3vmZ5pxDDf>!t}uD+L!@t*x73W!X$rjwKgk8SEVxOnO|Bxdr>R1?zpg1 zhB<_32|y_FilaB5G7o4EN4xnH%{x_VM{0B~Q`|;}k!HEUv)qmgwkNf|H6YJ{#11A_a z|KP;Al4F1f_5TYecZdHCC-G6JDn<6JV3skpNoZY-AZ~c0F0yaE6p>V|eZY%(ZgdhZ zi)sTD#&Kwj2kcIc)q0tg*5lE+#-Etq~+mk)};U@a3%ZUy+(8*eAs% zr(YBPEB!V-%&iPGrbtpp-2G^0Lf5G}#o12Hj}06$mG{3UY<~x4=pRE!sZeuB&N>7i zbx@~hla|^~)XooX$y^9Uc~D?EI=R;K1RqiP(wGp8c0F z>H1#yKKKZSjG8yICQw-sp*|)5#aLH%a&}l5%o4{!`WQ_AlSIA24e zN=zUW#P8p&TMXWm8e7%?tJ?C>F=JS-I#W|&oy!GYvQ?k(z1d2yZU%K1cSe9Sa(3++ z$|3lR*f)D&{8L6h@R~Df{9`4Ji@eQbbO1u{Gc`SxjZT0BNy7WyuZcL`7%*fYn1=5L z-O=TD_isM9GTO5sS2o^cb#)*kw%S&J^&sIPD^L6_Z*eC?4tfUa;4H-^EZGw9m zmtYa|{Cmh{mTZ-rt(uL&ErH%*UnHRnhs<*Epe^LZE*^(e}=6o?S<;1f{v9eW3ERpB%Jkz790{4V5C zT4mH+XE#T;`Kuf<41QAf)bgB^q?iCBnEIIs==n;PIJ#q53U=hW(C|8hF}Lhn<|sl_ zYTVXy2T7auvZ#;K3GKqJ=V7SPsDWBXrY2}m!u$ytG-o*It=}||3%;t6L+FQm*!MAX zDZfa$rhemH1S1R0 zeJJ0wi=e5GI2~;9cR0h4tvfhSD^|6sg1AoUoo3O5dgrz_*=faN(JLU~Cag?iPh!=lkCr&p{4k;W5Ey=r0|79l zCEJ5col@0Wy|2+E4b)iq`qS8HlqEGRC>mxl<;{Q46jjQT@g zI47)V;li@LsQm&F3cv^!HkQ;!gQ5E~-h=v!b$5Tnw&g$9E|nLD2&XePr2~bYa6`@j z`%pOUam-W0`C!;MK_{;rLh`pV2h7%@yy{@wLAyh$n`mWKh`Y|zR#a>#i}JRjlwUy| zDoly0ie~nbC<8ko@J-dk4XJLRgN2VmkO?`-P5~#QVyo3viX$=EPBIF9nDEoGQ9&9M z4ODIskWx?Uj9-UX7@WcwRLIrU(gd9WWvVHiEf!xPYZgmKpiU`-vRyRED)>_@4i+dZ zSCt=?)w#UbEF%sX5*1mHZ~F3UA6hVz7#kUrYK3|*L;*J%9#r#-E$_)74hB4}W#P|| zQ{#Sm*Hv8Q64e=`YmwBEAN*^e{nI0?iHF!aGH=hdU&7k#=)&4Y5_*0eXjvrtQ$|?# z45n^cMYg3yR4YhCw$U4!FaSHsarw;zHdD-Bb2O@AnbWQPb4=Z6d^3Q>VZ|-7nI6_W z^s_vn9Q|@tF&25d=^l*xOS8C63EqWYY1QrE55D0ibYUI6548~!Fy2KR=$N%E z7wHI)A;yG-4)`vF4M`#qnx`?iCPihjeuI!J7Tf@yDgX|s`dC;3B(kNpP$cNN#FttW zx`9|P32`Hd7)S+tevPzVH6NL1`gy#|-UR~%|J9-iZ33Y%5lsnK(a^ec) zj{{CD4dSc2@V+T=`%^ky2ee*jMhzG84EOp|B*Ed%T(e}4>k;BEhy(nM62jmi3_xTv512$8!A z`gT+l1ix59XEJBL1eQND(F?AZoB=+zHqcbwQwXsm8(Mz2l*(o+a}O|_dKUO)Ha8QL z!673W&;$GYn9~(EcfaxyFLVWrOsf2*uJ0`X0mHjvCul4+}gjc^m%hXnJX76-l+3Ft^ z$$`(sUjccH5A^^QRd5_pBs0~}d#gD`V(lw;Pr1fjQ(dsG;z1=(RK*>zDRiP@jy!`{ zhIuJ60_ob-Ol!BZstrbJYkPw^Vh=WWjsQye{)s0)nr^c zMO%HjrCq$CNxh-Zo^?(ixg2iD;yx+3Lmh8|bIGBz#b!bld;&od4)mKP>zq3#5Hur zBIQ)Xn;M@>DyXgc612eSvqp;RLM7Ocm+bWFQA;iJrhb>z4u#5C~$4|L76( z{#og^w-Ia%lB`{&H!TAn%urYjt~%(B-zV|ci-45U&EO8{nvlRHILUyV7Q@6C4Fe+4 z1VRkoS1GuR7Lx<>zV!`;<7DR&yF{Uz23a{YU8nqn@X%Hli|UDYS%Iip9TawF+G=Nf z@F&e>mY0j5tz~Qtq-7bXHN6~yAO_0~$i#?Wg7^qkqg_)&D|?^-bM2M{`c>DsAJKFg z#}`1ZfWUPU%f(N!Bo4Yq?DpwtiQ@Y@T!cL-rOH6UTv|SlUooLe*D*n3hh0!!jt33^k8@QHBJ{LVSH-sI?h zrtA@%ky~s9K7~1057UwxrC&*{2-K71ZhryL8c-+qIbKv6P?y14Q+lN~)Y+;AM;AOs>&S^cdZ4e>EeEMQ}_`{(0-meg#&up4wTJFPv5Z8}Gp30&Nd)=rv3kBe|#0)D&k0cq4dSs48L1T#4; zI#?FWO^WZl$GM61N!nL4xjAy&K%)2FReA?^FNwctbKacp9mgtbmXJ3< z-tH42H|UuZ7fx>L4dDnXQJ8ad1HCack_|^0V~4c~x~1_x7$zOKlpVWGBWDoL220sQ z@E3A~cn}e9q|ny&Nw=|%;YZI!rd)lcP}mVvm}!vlk8gmkcjs~lnI`$slOl1w+QCYf zt9^pH8+{QvWACq{)P?6SLA|$UU%P7ZBUNENethzymXN@1&ZE?(eSe(_wWuKimMS&| zZ3U#|jS0b)i6L1Tk(M1qh+EoGvxmFw)PR>}@YbTJmw+Y~J|G9{%lQE9TgYG>PPXQc zM&?VNn5>XS=LqiiL~9GeYcGJ=O*N@(FOLLU+^xE&YX76^_Io#{Op0uQtlh}EMoJ+aMq2;uDPvn?i9IE(PO z876ldd4HchkeWlFGHrNJ+>gFbtcv$2!R9L$W*P%Or8(Nt>kr`Rhil4z)9hRg*t8F~^qdVesJG)B?T^rbqoD;A zh|d((Mwbnr5L|6;U}371ds^1kr#UlU{@Uw^2VhN8WuHJXE*CN&w}lYi?WEkmSUCT7 zioS-+iJ18ff0@39C0th{fu+j;a2HxL=trTvl3ID8^3jk#{>ME7A5=jOjbEENg$XZa zCIxT*zBJfm-!o=RMUXZjYhu3o%i=Udkg4TtFl-2{m_#R3G|%U-fk>1>bO0_hXL&x* zJcC>pDR^ToXF?iClZ#@gkJYt^)Mz8kux|(^FQ&{izHwhWgV@8I>k~3Co@q?tNNg4? zng!Wlk%Q-O1)a&7=#xq%>{b>;u`;d$Fsw060ctb|>aL58oRTDiYzWl>V}Vy<6X=l3 zIJo=Z4LOPCQM2)sD@yJL5LRwJQM0sqj!Mb_3h>YOV9L!yq(g_o~GVc>obHUhBY+hA{WmB}+8JoeW#B zDH`X6nzk@u`4jKfW!{v6(Bb<{T#iWzC!z$ zL4VFYcPpHXPsrpbh+i~=Atd<@){5NF2WmpSvo`8c=a6_wZ$gcT=gzgbz<1`y zX_fb2d_Fr3ve+%k!EorYY395*f$<)jrO#=5#k+Dy4t~wQ|5uarmw_6?5v6MQG5ix| zsoC?7=u9KjY%TVU%E0+t%4U!1_RRHgZM)xI_u$*it8KUb6OE4G;G#tAutrVG^N^WH z!QfWWGd9d+GgwnsaYWT$LuD}quF6&{IRC4TtBi^=>ee73T>}zBD=p2?Eg><0(mex6 zcaC(YDBUrHNOyxUq=YmBQc8CSN)E_{d;fjwe((8l_FikRbJjZRdCz`#ybskJp+F_m zQmYT`se!xSx`441rv9=+2BZEH=H@J9oOO|5)=w#BlDcFV`TjC6IwO|>g7uvjkGZ5V zK!(wxE4O*c9+x?du~1VPmzf6&;U!|`@$L6D(Xxy+U=W-YeZ0Z!m&b!?TGUG247D~Q zKy8RYiuZ%EmO4wzv9U=~HgGr86k0sxoC57^%vd-m9Z@k?!_pPjiU~`LeL0AsXNQ1j z-=vhh(o&1a*Ku{h{-N#mm0F$Gv5U=}u9ONse|TvIG?T~7XtR0}fs+CA9$3J>F}q8G zr2r*$z8bHovDHjTzkmv#dqs4DXB`_C&m-@PVvRznoxxgPnEZ9_rim%(QNd#s3b0BU zMOv6^Q_j*4D-7mbothZB(C_kB>5*&(MgFm@*ur3Yq6%ld(2&|ZjK(5i<||*$NlrbA zz%gmt?Tm-;!(pfe_suq6Yle6vy}_p3^Jq88;%z<|4eyhTA5+{^m&4ahi%kS!IKTxY zqlJ@fzxa{_3-bkxr0%Gn4t4RB$zrKOzJEHyoSan64i!jJ=Wi_Ef+Qp5BIVEjNJeVh zi!h@l+@5X*OZ$EFIFws8t4C{glDu;y@fcD*kgbXHcY^De^5cKvV5zU;XTNnl zt)t2qWyuV7%g{&tpllqFs9Gnlw~s&qai09SyV}+rM@5`jqE8u7SaE~dq=;p# zLIOyPp}>nelMon9;Dlp~&1ySukR9p|m34x5_35sDxWbrysO%bBhHriQ@fu3q!O5b2cBNBj0&}HYaLbiWuWW zeFqGJ>BTX!KtIXf58;d*bm!-4ORjE@i`@g3yFWi*`L}V`*U|?pcc=c&e!y}!_am0O zw?xAwc@W%+?w_b#v(u~Ca9WS7(VRP2R-0cUjzB$GSj|sOf6<&T^g6$3ddNR4UgSVq zR+KS1uEnb|4DekM{Ixi*6}jm?-avF3JWurc>Z@-I!ql<3C(^yt!Lr$CV0&T78a261 z>bjm4t$5~^rP5cK>#cd0Tc~DxH5F45OTj@#olGG%jP%#n+^4Uac>*IB0C~yxN*{iZ z1OIZp*_B~wp~Wi-uu3wP-cZLsVfnOv-nL3auSzt~_o@dd`S%L&X@CLmEoaukr7`AI ztkUN5o`^C(<|N{xNcX7z)vFv^duxJRBJ2Z)54LG#H7WwZF}5EGSWpr3LtL}CeR=EP z<}EX%3$sbUUBAU`Jouk?^l(^nx$X9~*p%?3bnnG)!Oale(VG)f3-^a#Qc7HabL z5jq`z7XRV_7sZb?$hwWbFQ=PDlxQ({FF>{}v01SSePeR#pMKO)hnn|!(R)+%yy}e^ z%*^>+oC+uGAPN`jwN;3EZW-ITM~bG4q5LKX%hW_NTEI`$q{kfIAJL9 z{$vA7q0!5lNY~Sq1H^sHg{sN<>1ugLOjPfRU!SgzendqHe>Zz|)`PQJEx#rLJ~*pt zqxJF!XI0()_>opEe!}s`Eu@NL-Tyn};KBX-)>25%wNzHH7cUZo6tR@Hla%_7*Ogfs zUuHsglesRxgys8YZFUiHOpdrtCyb+c@gr;(Yj=HT5Cjr&AL24|X@Jn|T`>*OF(XTn zXAS3?Dgzgnu}Z!#o^H408J!DV{JT z9dGv-YO-R=V$+d0l~eb7K|PhfU{VkWdnn!O-O2*YN^EYhshsBEI~zAx-bwVWr3|WN zdjHz~TBAlsMn^}=25=ke7b-<~xR9fGTUOtBxOnfccHXkDzuS7(;JZhd^?cpVtJTbavvF-Cl>T8$L?I>U zGl`f7&QEjExI31Puy0Y@!fqPjaeSROJN3Odu$f+kOZ?f(XyT^YrXyjJfGNioz|Y1O zBV3m!?$$<2c1Nt8i_VS7W=-OLXWH>DL4l|XoAdhKtJ#FJY^^?sy!ZMuYyoC-Ju~6* z^nc>(((RQW?sVm;4L!Y+Nv9Z^=5^`p$3J*C?R+YGPqBX*Z1oY9WFe}hq=QR42(65} zq~Rx+dbgf>s8nda+GTERy@i{+*DX01U=i(eg4D~L(JzmXrkcb#H}y6h*<7wOY4`o< zeio!-o_DiCee;iCFZu9a0&CN>(6BM==VF@RDbfxn z=(-&2$%%bMi$43cNU)1Fj(a8`e|_v&OFCosB+OH6To>)jSw zMq8z9hd_^+(9E3rhBHyEf0Z8xVioXqWmQS1nmWf_|4%zV*VmL~&xK@`N;PtehUuE) zRSLDjwXQJpwe#0nd(Mqp0K#?s47LPE({h!`hQfIC;^HS2AbtZ`wS}Sf_uM=i%g9rp zB$9Tp>XfbAV}a;OUdO^~@$*SRS>vkMHns>W(8%M3U;%o>WHe!0r(+_*?BTtuXrFpL zL+HbHGRR4)KM~N@W zIXgP~jq^+wT8ZA0N@yt!$w21{fkB1Vb%^<`HvEt?hz%=@(fi=D;RY@&r0q^+Vd@to z$7;B#|5u$MHHxeb%U4UHZ1c`B8lx>e0D>x)*EXh50b|vbZCOVuBrc2@kIxSBzEToV z1@^A9y3xicO_ikEdCfSH#^Y>TO19;5C+HNJsF}e@lRBdc$PBM-!^Vzt;w(dvkQ~)c zk-vhEt=LC>E_^8+rwlns*|mk?*CUgu$NX7CX}zM`s=_kWtdOzNYb!~5CoLkjKkA&S zW@}fTy=#uWxGLd-I%=iKi->EKhqV$eWV{l z&>M{3VXd8VS#mwKNC~>?w|6fmyJ_+!QOo|mFw5n^qctZ<8xmDF=!_u94f;)?J633s zU?D?bXkLiWx!7JfgbP9k1pu`*Kvoji(5X0b$_J9@VPFk6#M*J<{2l@NfqQm;8o}G#n6Amzo$}evwbA5dHc-7nwiz0#~!FMcy<8 z<$8oPx*lFU0B+ZvMvJrAA=Ri93Xm0eJ_vH2(;f2)>7mV#seweX_iJgl1Am8&UXJKMi07frZd$jWXGf~jQ zdW_QrVMVvt;Oh7q&GYMRvEIYo`ZMm)_t=$3NrXigU*Owvm#gKjzEyrjbNKh#(E{vCwi?5S{+8ZScZ1So{Y3W*lgv-D+4J%CY8EeH-K=LP&AZv9v>$cr2s_yTiVadG4b|cOCcuc15 zj_`H-Wp_*wj}f2hi`;Jekk6}Su#84k;^eLO8Li$E6T>|(ie*0i>amK|TEuR!mVo79X8^WgI*l|9S1HyM)J3;-W@Bc9CT ze%5-j(uVVwsrx%>`We#{C7ux+rcl*Kahqu=F1?!XGne`vo_2e0%6}KXCyC^#$d66Rg1>(u$Yx>nOqw9GVrRqA5AulyB(T7tGC6%5PqhRvOT zhHqhCl%;<>f!m=WQG30jr8S^+4SZ!f;+4L=I*5rwP&8znUVPC#r>yvY{;KB+u3QpoGlCtKEGyO~gMiR4^SZ4Lx zHFWmKC|(ZL?v~GT@^NY=hx32X{Dz_P*#nxBT%cYcUoI~7A{Tf2i-wu5m}aed=*?Rx zh5b`q&IF-6Us28!AH@!gvDaNLoUn<_w@0|T)uKJ18P#-pIW;hJ{h!9O2X30fxr?=2 zZdDP~^jMc?1MkYA+M^gqB!Z&;5e)nPIXGHQH#wp?Zmp?D) zi*vUeWoCRX_Z=7xe(8MYX!P3Yx==A^4>(dYM@`Su4?Gbe4rBCj=dwhT%%wuB>WD#~ zG_ofMp1b8oE7Zk*`NoG91L0wm&xVQU{CKNEe-f)>?LYUBr@W=fOqueseqXJg(?D35UB+3%%rr|z!|d)P=w9ZpoTqXr+6`$@A{ZN$-j=T63|P;l zlUl0>n+x3|?QiuRT9yL-m{)xrBUT_Grmed%=&tN^&u2W{{RY0}R9K5ZNDsndZ(c+S z`9~aBHt*5lNwNM&wOh$>S9vfb3u3xv^G82Ba93BnF=dh&Nl=DSfUO54@>1&fsup=COsb^2*<0-@N6*4mf zoV+W)#YZE$To=QPCJ?x#0@c27hO023cY#VQ5F}1kMvQ~ zVsHB}Q8B4dJ1W@MJFq>_$S^zJ*Y%LlDq5%Be2?r%=%yQCXLD5I<<{g~OH=f0pb!*~ zz_pN)RH~CM*=F@HJrs^;$`N-q4U?`3&MEh=O6-ck$%b~wP-|jYA{V3P_s!>2YD+Voq{jPNkCJkJY|r;4y1jX9sM*+Ko(B$+XgzR%)*DYse$_YamHGWQbT{#OpbAkTnLZjG!oAdOgzf7*9&njdI9?b7$#!fRSJQ|ale;hcUqd<16#e_BZ z(@~}1jsIQmbZoG8$q3Y89CP?AqjEhv9_r(OJZuImaP#R8hyeSX{g8*=WDlC|qjo&< zrTj?2yM+?!Baydqq}$MuJajzHe0bd7(9vM;?E_d?F%4f?fqDDvScyy_tkyn|dSIfN zqwhr!*>PN^@$6+@ZeZ982C}-W5WO?8Z}nTQ28lOW@3N9OZn#tYJrRFy^BybccmB;< zD@ND=)uwC`fpk+#5dV~RI zS|1xx`dVW*>SE>osg@f06LO6I&f)q%(DJ`eameHJUl?8gbCLg~c=^92(9nuQSV{gL W!WS(yEbK>!ihbxZQHhO+cqY4GO=yjp4hpgJGRYlo_)VRyIb4+<8*hOI$h1W zy80YN8E^iQt8;y%96e$d?c-W|Uf@?@AMM<=_5#w<$FLm9YMVTss zct!K_b=B>NIIK$TJ^m92UA- zsxaru4T-~Ys|t)U%z2tADmVjqHzr`s4#+KptRhZ2_G>#zJKcoQ-%{JQr|8Lm)l>5oL0QDc}1&9X<4ynJRw z2@$72Tv3-p-+#*-R>rMWnA0J3?FUfjxIt*I<~7Zsbg zHK>8SOdmH6kE~2IN@oK* zrP1C#(9wRB5zl_7?`BZahz#DmY3$!z{2L`hMp!*n4a|oaRVuVxX2^gCs+?A-EW1<} z>&^(Q?_t6-KBB@rH?#22Zi85i5Z1aq3m%xxOuh#!FH;3Q*`zv|&H(W``rVRo?c_%+ zvp+o4=bsBmTDF=ULS|ohfWl*EFTrCi?KjE(Jcc^gg%u<3@*q~qz&Ti22?JfLH9QTp zzZt1^53J&_tC^%qD-dfNJQ-VR=>)GzgA0$qMH@~U^3kZ~qcR+>Si9hMf2q(EQJ4q0 z#9(=xtA(@mMWiD8?j-pBn*2uCj9fFDyT2AISmZi&f_tEpN`?HiNN-#>Q%Zz~Xq!1V zs|cbp1z~XMgZh;^k!u1zVsW<|_31BGZjqsAd&-YcA-EBY3n#GniSX1|mJJ8OW|AA~ zod%9y8UbiwgrRRsHYcjblU6 zxccT4=-lIgvDgq{)0Zk*_aIzMkHRiDb{^O`aa#(KhtVpu+;r|2#H;(@dsMU)!I?~H z+VTegz zrzgTCtQj>zXts;)Qi0F<*Jq^(wOqxTMJZlS!eDgH^Ck%u6_YDrfJ)M$=PaNkZBAoVxec3 zDe2G(9Wl2dh4v@oB|KMFBMUulIYV}Zj=)igZ=B*LBN2(1`a9c{0C6c;H1T0Q$FfBz zui4}2iq&`FyvrIpb3HKpyImu;*VA=n$WRrg?+2j7R2c}p1%o#nl~fdhc(%i?$d4I@ zv+DO0QZ!UFbPFuoAL@d>ojU21kl=W?|8%#{Qp!!1BG9HLSlDolYSGL$KkI>^Y?gEq z(^i*=z#O0=ay)=caV*Lxh^qDTwj#5rH^d6;2=jRdlb5 z7V;xFgc=J9+OPlD@B~?Me12&(aKJ+MyS>g0T!ErlTqi&#B}QVd8vxhANiZPj^%p!( zSTiC7hX-05vQm6mgqSNV)^5|c1700Lly7j}^ zgfSbK`dwA}VC(@IwzeInJ`%%Wxw~~RctklQg%D(R=Blt+zwP=%a`%_2Ftuzs9fy({ zo|@mn+Tv6zFs0x1bbi|v4_|08!{wL~G*d}98)=-{&GVCKpQ=%%Lz zSh=ooS@bi|>)@<_^+u2~y0s}twT&ZIVo@-XWE!l!-%~sA4Okap&M6wH!nHZq2m0%& z*_plddktPrqXJ-T4#0H68}#)p2VAB(MqOBFtwK7+CbpD^?WeJXPd!kpuE}uxPA8}f z#!&bqN(>qjw>ar?%0BKLW|DY6)hD3KOnc_`)u$YW7~cU`sY5k0Mcgw!ysnf~zY+~JmaIn0K^|n;%?6waO73LZ zZYD(YC{{x(12HVd=r)NSc?kvHkJEOu3&EDRQ<(r>3WA7X-W#1Bmo73`BK8`W=GmX7 zdP&|xe{FYITf-ih;!&PXh-ctzWD=*u7#!p{yQm$)V2WD)epp#o%f&)V32!_B^`SW_ zy?Ix)fS8VxWen6QH!`2^KngE-Zwo3Y8w_jI-O&ucDX(Q|m+V5<>Sbh2TDiuQdk(;5 z05PBVg9qJn~xyD>yxIli>{Pqi0hRmba zaAH+MIC??DqX_JyRIy?%!5XcUHh;I-93j$?@$`kTq`N+aCgK~vwKmV(=U$iEPw6Rq z9}pevbgWOyZ9skgv5RkUFesU8+jpMr8!C_@++{9?<;nqQ+q;|V;!s2qxfPGN672Q? zV&-rlh0u>YtprOwU8wE!*0ULGdAS`6DE@E!s3S7nl}Zf)B2Wej^79`*+8J5dtD4z4 z+Ww=+{~<@eS7!ic>+`qQkCZD}FDtvW8e0zeRAN9wC*7DGP0n-$tE@1J?e7ERd^4}o zsw)v+g&V#Nza$-KT~TESOu954^HS@LqGs?8BW|Wa`flDiP+vlGSq7gH6*q z8@A)DGnjKvL2=PzHD3pv;z}%%XAX6nB@cbIGoREqc{#FDbJ={|_!wCP7v&!F@GpZB z=H@yqZVK-25X~4IZr3K2nxXIPJc&>fbeEIwe?177huZP8(o&Y=hF2yg4vy_1DG4=m z2G3xjbuhAKsfb8yUgA6=z&$c9X9TkavjZ-RY8F^RzfgKI*9J)jqeeJM1S2Fow9A*_Nhtz0^odu+q9NHP80vFrwi+Dg9mJGR(k9C zA~mDow*gR8XstUBQsfchY{i$DI~%zcV3neNw+G4o#Tr~GAACjF3w3%pjsl1s8e6XY z(dJ_FJjAcSGm%k)Pc08n|M@10yo^nu8K(a87s|u&TD^Tn6uR0#d2 zo6^t%X-FopV30vD@Ov1s7)#NhKK4l!q4muh|DCmsw_N6vUCl$2Bx#Mb2LHP^VQvDh zJ%3|(MVYaUP?fNP+XPJrw+dtIzb7uPLay>|g#)3?gLW+b@I;w>BP7hT-P4l%a~T?E zJ?wKJI8NbQ$N3q4=(gP7o~(ag$XVDXIYR90?A0o?#c|n)P+!9_ZK0Lep65QMFEDh_ z46I1&(?4$xwsQKFT`6cKCLvo7x2$_Q_t9_sugjTmot7P1w)p>8EmYi1m)X0RyiJAA z)|~#~@+bq;98dd8Y;9tB$yWF?Mv-9^tqhNHNsk2(LvA z!&UF{BgcJ>cN98)I4l3+fG$>W`%!!miiKw=Rt|YB8;YICws&ceB$UEIU8%fI3E=)r zm&424g1_N_p1{=sO(R_`3ZF3#$9kbVxBPTh=a!zAv2>Mpn%-JKa$9DlRE3#uRDz?I zP=~wz7}yEy*S7SDPRm^v5<_2Pwy%(;RliU7T*d@?;uIl_O}acuIfRnm{v{@{jz2=(3*ds7XZ9SQQ2G47hN;G<6AGzi5P?a=nvi z&h!|z!@a-Wr)&6h`Lz^2;4;wElX)DEN{$vl=^uoo{%sAP{$5T=56{aZDvRPhIK%zPK_= z(^G2fTRq{>U(VA=Nk8x^X{cJEF}*HPt`Klol4`1D&*E{*yttyH@6!6ujE_yymTky5 zeV5O9`j)K7Bwg1u+Paij7Jr{rzTS;Q<8P)< z*t@_sW)-~uZ3O71Uzp>Fy06>ugRM_97o)8|cKwcXhyya&T>-rmc;&&P%#=3kt~!H3 zxk+5-RKA7sA8`Co!E=Qox}t817}RMUb=kpGekAkp||oNZy!0F$D$CoWaoj? zFR|2K)+h-;X6cP9-psK{1cFF+(UF55jfPSG# zSp)@h&~LanH*_6qm{%m_Tij(xeCUDNkVEl|v)ua7(S|pReaf1kl6$&nTPQ8o`>S`2 zleRe)>6fx~zQK3QgAr!T%gu6s&;DB;`6A^2^tSyMHjK3w;bD0xlct=%9cnL+4!R9? zfEZj7Z={PqbYxmVLLJVQpOS#^?Xr+H<9^i1mTDTBwLZTRC3e!4l%<#8KTM4I@QgQX z_(0H#2EYJ=2gzk$9V5x?^Q6UCS!`XhhmS_ymF;1#{Ozrm9v7p%g8QED&U0Vi+2#^%8KzI{%mDDfwXyB}u(!*_ zc`M8xcWXlbzIb(=r{jy6v*&8#7ya2@SNwCtzP(S~I+rDae4W5XSu@)e9^S^Ujhcyw zR!g`q471dCLBHd!o|%F4_vCZX%WeU)fBkId>3w+D5j3And~VAeek$R&?SSscZ?#@` z^#pBwF7*iXtaW+3p*6Sr3F&a?`aX9|b*+NGr*OOGK7jRo&iC+i5Wsh?M##mlEN)-B zYg|JCU>DXe=FSdy&uDV#?Oe zSQGDCkH7TuSx(5+R>afxmb2-*IkIl_wl3rtUT)uvovFdyXy4-HXD7MVZ(vr_^*2hu zCec1IaWNWAm$p919e9C6aZ4ascrj zKZre1rHhGmpD{}%(p*H^bOb`NWy%R26^0Vfqq>25WBZHp?;NGbmlIyY4i5P#g7a57$>@P0-kO>ma2St?e5*@m?z^7rRT2GPZ7f;{I5 zet8|X3DaC-ed5rc@YXhP*Y=vjo z;w8io6NHz=GH_A*FrbNUSsb$v)bM?=_2D=E`c?w=#kk<&9oG`SfUOtpM$CSql*4#&YtV4Gz_`!0IK@S&(*1 zB#@LmE=+-|kvi;NX-Zep2h?1xn{e)I%e=gP8J&5 zjh47oT9Xx`B>vG0!A?oE-(NoF9pHXbjUz|S15sRK6({?gSH{@{k#{u=jp^P#h=bmi zsA3r;ND#|0pvzU8HadZMn)pgRqcF#+zv}<2ttYG<3a+%a>FmMqgVJ>c3u3jkB_bzK za~F30CJ(yzrS#X%{RZ67dZ9=a$G#FL9n)j1A(KRu$ zrCwo&1c`62XXC!zk&i6D$ai-*y~~&+NNY781${4PXD5B!`3aRIhW`X*{0vB2(SC6; zMbvnFJIrj0a6LNd`tqHjr@A)gsq)6W*Bf;1Q_NiZ!;fC~$*23&+ctjicV=2TciciB z(F|$Ze*h4spS2~PJ(0*q6YjfhS_>Le4Xn>q4tKOMbG}O^8|5^Wj04m zvm=Znf}2NQ_lBC!#Z}M@7o>0P)&1@a4d+|Gb1!GSO9X$>oL@i43xK}7w~ZLQm-eHW zG9q+}0dwg_-w=2i7n&X928Yi&6fiZtGQUm2*q5aOe6)+*oCH$haBfh5VAR@)l7Y^r z@5%lyST7pZu>0=pH`TSn}|LOfj73;1mhw zp1+NxrYQtn8P|U8#~1bLiD9JW&VTz8HD}Vi7g5qbw`8W6fFJ(X^NdCQ@c==d1*$j9 z{VUl3gPf?IV+>7-x12AHSz|U+crH47-f43H@0Zo6CcEwFIb=4z6u>)Ngv7s)Y?dg| zD#bl3(WKG7-9z#Fi6ID6Ho*DlQaR86{ugPjuk`_y+m2U77=ww-FYi8cno|M}eWPa+ zTd&*6@TWvhJX5k^z%R6dZ~_6G;{u0YTs}6onMlTCBJbntZ^l#oO-K9-6P&$}2%Kh? z)-M_p?*+3g&kE^qNg5lCG6vX|lp-37@}xGkrfZwcuVD%26?~TRw>f$dJLy%JWdP-2 zs|lXR7;Wbtri1vn7itw#oi$b+Ehamoe7DYj7O}|G?Bv?~VJGW+^JYt%k6IXRB5yRN zwP7U}(^;&d?v)9jBH)J<7Njsvc%%gMPuulmq95Tm*Zo$PUaf)&y7!DIPWrHYa&_XH zU<8E%rOu!|!Ejfr-jd>TRmR|(I9>04al=->N5%bnAcs}WYCrL`59tP4ZFE8*LExR) z?0<%pfV{6Z-{UhP_B64%eM7gj$F}5#z57*GM@7QDOH=svu;Ojcv#B_;XERpP+R+o- z@cLq`ishdsoxWgyWBBD|(GlN<=D2x$6R5nh?o1h3`OgL%bbVD#Zq|MmU>0kAfAb;@ zvfP3m*k9^2R8QTio;O?yXCEXA7A@8jxb&QlgmpZEWAYME99+zd(YSHv^3V$vCOakh z1Qe<`$y@BHQtSH)rPbC2Tcw zftm(F{E1eQlFy4zlZlegcHwp~ciZ@TQQg7$?96FmZfwqfJoO=;&P3v$_HaO=L7tRN z`Rxs(>R&RGxSm6teg;n^wvd;0zfZjfR>s9cf1rYKy~!D`V&ZQx3PCZt8*N>^Xx!Ok zTp#6mJd9R0jAk($mV+DuLC%RknS)@FvM`bZmwu(1F_o!!*O}W+A<%<-OupRCF(C!Qn+LzumTQbZ{TSQ*vT zD7G-cMn%G(CnX6*f6w3(S!b0ynIrh)DQrqrDPz5f=l3Os_|+PUgZ7G2Ox>HGY&jD4 zzC%BDj2WA>pzmiaP2p(FE%u|>&DU{+SQW{CxJhxDMB0i?@WKk?U>{Lv`W`0j7r|h| z+)wc%t6d@Rv6!Ja8abVT<)I`QhejkY**xG$p_(G4mlOatjff#-ZtFM$5dTx-tH`4LxSf9a}gAYuC?5| zox{X3Z(p+QnebPXCT7e<9?Kzf2`KS3tzMfOA!HGy4%5oU6>F}XvVqp}s1vE-FM_MH z{KB1tK&)0Yy`fNCSJecNM-d);bDjq)8SQ`K~>&#}wr*GqrU) z*04gormJ>Q@Xf{%0K@ROP4zZhM?r&jh#n%3YLA59#|d8J#{vomDbj zo#_1lIH>3&S0}`UlKo=k&XgrjwQnn|?AhO6EtYTk@9UXU33+)n!#p;+nQ0T7p2Wa96rtS26>R8R#0>N=t*8y z6Q5RyoGhUM73dfZL(GDZBc&!gMsTZPeqxb9FE zrU?FX)dP)T=oh_`IJz&N82)`>I}X5SotMwAC8?0MxhM9Q%4yF9dk1Km4uS6u6b7TH zS%>NX$3lq)9nNi7PFtq?{E85O3#qrB1r+d(ID`*DdV#k{HmWF{wO4TYC2@pEZ{awh zECZM8cYPkr`%;AkG=4ud84wvqZ4jeTa!3aG+*RrDImiOz@>`=35o>vc*HF z@==Vv2mM@C!`x^d;M#|RcpRh#twh(|_?N)Wd-`jyr2#T|kX8(txO9+Fsfem{vaUPH zDu^-?eM=E`53z2pw7z2rrWZ1Yl#%Ns&G=@8%p--Qys@RF=TJ6FZP&`H1(1@Py?-UN0N0FjpDb#1g#@+U3m+GxCeIxXtjDAFjC`Hl~s?xz2Plah(W{6uHM zKKGV-jB`18S#27t*|l1ZQ|-vJ<4)~^>JRF2v&nDL?Oxvk7lyh;jd3x0{r~7hq)}vA&B3iOliYTJZ_>BxZQHz7641TmPvP@GZuOo{DiMU{o<@WJe!<* z)8s>PBklR?Q<(S^Y(qlW3dxV|PN4t!W-Nu4VrYopxfyVZB!6ey9FG_4@&;~Ny(Ynp z4LxbiNGzg~xa2(iE9<>Xrk%7zeEqT)z9+T7FKRd)J}Zm84_TSbEo_2{x8Z#a1(I|6 z{SU@J+$c!LR`>Lw~IB zJyx%Tvms%GKsYCK!-&CdGBCT?!p=t#G~7?oCrLU&yCIQCf>82D>Gi|ui+3rcRBhXH zDsdon$i&gM{sUmQtWLPskjaQ%_$<5T|hje_|u|1 zl0|hGTig9#wRcWr6jM5mhBYK_8ehV)_eJP_1VI`c^nMI_5*L%wftS$B-NNH_VNP0?QG*E_4>FhT)RB&Kl3ZzW^P}jz zBmsNrzma)le5sReaqv!xU*3@tHL0T<69lWK6}Pg*NJ_fho5)_>3E1fFw=aY;@o-5r z1d+p>WKIhH-W^N2`j}ix1R97f8D#aISItYqHv)DjEsVzdWw7z{A~MvcCOZ4u*Ch@+ zuFU=pDsC+HB6rg+LVqN)6YWKd=)+j@ED8mQXJv>aa=&>}Ypc0N_bUESu@$~W3Ce<2 z$P{~hShlC9jPpuM9U)8Q9-=CLz$I%c{FnuGzX_rCIsbWNdyXy{TGCqvJw3A4~lhnmv8nF>MCF8y@ z%_ENH2DR8al689JM?m$bXr*+gOK{_hc;u@||2R8?)q=T)K3>?QEuQfqYv>^Emf~lg%IELV2I&F$Na}1=?ZgKPJ)qA10}^!8)_0zi;&$S#hix1`1Ciy$LoL_?jzA3o9KRL4#UaP z!Xy(It`!5_POTa%RQ9%K4C9fc<#rdK)_~%4D|p(}3UhYpHw8ooub)N?y&3hbZ=!C? zSRN1bVS4RU3@}H^5`%=OI61Q9$3 zCfGWuQcA{ZXvs#4D3^-ynEHM|N~uz#6;qTGcGUg|vqsCv5@z%6uAEniyD_3e-XkG6 zn#MjKVwv&$!NgAxeJW>2xfVN$@M9kO_O7F;K@GwG+|A1f1$f(`f;E1?mJsze;T3T~ zh21v8t*HcKBD~K;Tky@L9c29T=Jy*AQg-Z*WK`$}$QcZOmpsxHDUpWp&&a!(ov8~l zJ+!Ugps=nUf%zM$H@Q5|`)3z}If>b6;IcR(Ev z28BtG{H@q)dFlJU|2DLn`x0Gp8SDuWK}PS6b&*KGDeA#s+ESTtlZv&;JYjrig!8Lz zz~ZR0%^jE5nk89`*6NF=m$|$jQop`wM5EjHQQ~$otRh@PE&#RDPt&%@`PGN--$9!? zZ+0qjZgTx1@m}oWxzR;yCkKEXofZHctIl^QeZf%WpngbQx0Y|a(W(C<6O*ebHY^jb zo^PGsK3w7|ZOLlMuL%4^kS_~DeoJ@qP_U4X8)lQy7(U2;obl$Fu5G@-ZYXz|lA@e| zfhoN?qjigtzErZM0%t-FVmoeZamiEpXHeU27KVT}WV5Js3#4zu0b9I9@TPvW zi91;SgyQEfS*1L}VRnet#?q7}@*~~TeA)8dR{91_f;d#_xAk+LiGjnxs z`w3#^yD(A;i} z5h2}zCZ_v-oQ7XX6TFe2Idxw#xN+64mZTdCja-;>7D7Ayqh+AIV{lT=GsJq-POGtXaR3e8+G>Ny5sDRBiYyw7d>-&;AQA^F#MB`Raer&r;g#);JP*^MDdw?uW=}8u z8fZoaW&e74k!H((vZEvz5SGOUk+E)CDlU>Q+&Q_UuugIgkRlW#8I(c zexRh&-(7sn(yentjjVVnaxrV%1k|21I09h@b^;nS$Rl zBN-4!l?(m`yY;FX@Ka#bwaffQk^|hvswCX2l@QS!pu9NXFz=?#dkU8#}RywY@K1V)^>iq zY|4T1d%*3wX!TQc<8LNN85QEBaMA?`G@#F5NPMxx0`0cTVKs@lgQyZzOj$dN*;cY$PF zB5oT+5NeKhC1H3tReIuR6n7((XB0qRDW78I`;3)EEOo)D_Ld%9?suJ4=c8Z;#}K2@ zx_WM6L!2hU{t0I@Pd)rj^a~l6y!S#yHTUU(6JApxX8lk%zi}}@0Xd!vgPQF&fnhmt9__A&-H_v=;18FKFSix(5u=MV}CB! z*TW`%6)UZlLv*5p2S2kE*7_{i@i5=y!MM2rRenap?kalV3+kIpV$Z+9G%1SjGrzDK z;zvOgjO);h&q<+w2C-ER8Y3T-%TIySA*QqUG@xJ1h4= z3OBo^3pX1}=%q}{IX`Pgq2&RjF59*AH^R{Z%8L3M|J!$gX2s9 z`TJrnhN^|g(4NW9*H74xO(i8)8Yt2m3y6-a zg-_mpQww##U)TmQlb8tFf1xFTg_16E>y;5={jNT0((j1hWY|)Q5(~nPC<999fRBIx zs4S$<&8G>b9fJCV(1OAwO`lJl{`d10#JGownuejw8teuTC10b{;U4spr)Aa4kz^7t zHH)Dn;UaR-JRArwJ;w_sicF2mF6qtHgg=f5@O_z%9dUH@bj~2b5V2m{bRJLex+U!V zOAuQxb{+MM{=wrhEO-zr_%10Znx%7S_%p&9)S=Lib&%r#n#7mIUDb(=$J&^$QrsF} zDf(7q{1IfiycauoX~;~$F`u$G8Z9TS`%h@XOO<7nSu;y^W*f4@_EnMjkB#`JvWL*b zZ$p_wLXoncp;}ywH+Rhix~*$tT5=8Xiv(bCr-qu{5NHgbE|B9VF|yS+Wc(oAlmsBz z0bwQXLH1+5^mCWGC+-jSOG;Tt;r1MuB5nU2hOHSl3vJlWoXgs?R_%NMWas6gQa(UN zb6rNT?YOgSHppd4{D5QrZ>-Dlf0xC^Zzp@Cz#QnKT+EcTf;Le5>HJDq9k-`tLez;& zgdWok4IDEOWBkyn;Y-&6md}iMR?6p5-}i8*sX1r|1q6BhzKV{#xlD0JaM2ot{#>QC zh$jR_GYFAIngwXfxU(1&xe$k44~Ji#pQYx#Jxe>1%jelnU1hAid+jiTw#ec1Ps_{?+dt&{pid&jm(0Rrs?e$g}au7j# zf?#HL?jO)vxIw6m(~RvX_6#xP=ww>S5!r2gQ1=y{kvIVjx-hJSI|BKp6d?ebna(tN zuud&kDv*b!=akT9XFWAOtQ<$=_gi@E)F<>kC5)+vK7tx$7gPi~6(;^X&=qS#K<(a_>EjgNBcrN5@bI<^F@!ihi=TRNu>nF3^Ul z6Y{-UzO&vu2szgIn{HVPRiy*c=WwnB&Pb;OPv}my`4tKUr?1A#$}81yfP?LJUme}g z6hiWW8&bkH zeOTESp3dB=AoW{PGqyr)Fa!}~Vl`oewUuH**<=Sfvw+t;fFa}#{?ua}?Wl}AW|T`o>s@Mw`~tbg2GR!ZjUc(hr2kDrPDD+J`oT@XNmwe&Bpb~BdAMJzZu=>pirWXrvFKw#V|z zqH(I1b)nX8Y!d4(@rM3uc6tdrkaooz0Hhf@Vrpb?fBDY0m3y;Dv!-;Key2Elu$7%F zK~2G}*f=*pDTE}^fZ`628&N9To|^|@l7X!s9^9`iGF*$%wC8cw8&YGDj|uvz$W(dZ z?x-bfQh5jbGCLJp?Czp1AObyH{{CQJtP%G&NeUvr!)cfkVm3{Hr5;E42~k(ykRZs7 zhHOjxb(ZIRsL*l7;5&kZaS3IBek^~-H&nugP}MMFL}n66BjesWtSWGhw(XmyGbI9SB%bzToZ)m~ECwG#L{4ZvPW5hTL>+9|MW^@qqN&VC0Y3DbOGXV`7Tv<2PUZ?d^ehC1L1z0Mr;@Nuu~}3?7bIjj)bpSqv#GQ( zy63J$J6?D;n2v1dsB{{iWyB(Y@BN<-D<~Km`2RZ!>YpR)|4PL{|B3%cOw|8rww?Z4Xp2a6`kvH$=8 diff --git a/Solutions/Tailscale (CCF)/Package/createUiDefinition.json b/Solutions/Tailscale (CCF)/Package/createUiDefinition.json index 98f301d0624..a1a2a8ab424 100644 --- a/Solutions/Tailscale (CCF)/Package/createUiDefinition.json +++ b/Solutions/Tailscale (CCF)/Package/createUiDefinition.json @@ -6,7 +6,7 @@ "config": { "isWizard": false, "basics": { - "description": "\n\n**Note:** Please refer to the following before installing the solution: \n\n• Review the solution [Release Notes](https://github.com/Azure/Azure-Sentinel/tree/master/Solutions/Tailscale%20%28CCF%29/ReleaseNotes.md)\n\n • There may be [known issues](https://aka.ms/sentinelsolutionsknownissues) pertaining to this Solution, please refer to them before installing.\n\nThe [Tailscale](https://tailscale.com/) solution for Microsoft Sentinel ingests Tailscale telemetry via OAuth2-secured APIs.\n\n**Data connectors in this solution (install one based on your Tailscale plan):**\n- **Tailscale Standard (CCF)** — Configuration audit logs (`/logging/configuration`). Use on **Personal (Free) and Standard** tailnets. Included in this release.\n- **Tailscale Premium (CCF)** — Configuration audit + network flow logs (`/logging/configuration` + `/logging/network`). Use on **Premium and Enterprise** tailnets. *Planned for a future release.*\n\n**Underlying technology:** Microsoft Sentinel Codeless Connector Framework (CCF) with OAuth2 client-credentials auth.\n\n**Pre-requisites:**\n1. Sign in to [Tailscale OAuth settings](https://login.tailscale.com/admin/settings/oauth)\n2. Create an OAuth client with the **Audit Logs - Read** scope (Standard connector). For the Premium connector also tick **Network Logs - Read**.\n3. Copy the client ID and client secret (secret shown once)\n4. Note your tailnet name from the [Keys page](https://login.tailscale.com/admin/settings/keys)\n\n**Data Connectors:** 2, **Analytic Rules:** 5\n\n[Learn more about Microsoft Sentinel](https://aka.ms/azuresentinel) | [Learn more about Solutions](https://aka.ms/azuresentinelsolutionsdoc)", + "description": "\n\n**Note:** Please refer to the following before installing the solution: \n\n• Review the solution [Release Notes](https://github.com/Azure/Azure-Sentinel/tree/master/Solutions/Tailscale%20%28CCF%29/ReleaseNotes.md)\n\n • There may be [known issues](https://aka.ms/sentinelsolutionsknownissues) pertaining to this Solution, please refer to them before installing.\n\nThe [Tailscale](https://tailscale.com/) solution for Microsoft Sentinel ingests Tailscale telemetry via OAuth2-secured APIs.\n\n**Data connectors in this solution (install one based on your Tailscale plan):**\n- **Tailscale Standard (CCF)** — Configuration audit logs (`/logging/configuration`). Use on **Personal (Free) and Standard** tailnets. Included in this release.\n- **Tailscale Premium (CCF)** — Configuration audit + network flow logs (`/logging/configuration` + `/logging/network`). Use on **Premium and Enterprise** tailnets. *Planned for a future release.*\n\n**Underlying technology:** Microsoft Sentinel Codeless Connector Framework (CCF) with OAuth2 client-credentials auth.\n\n**Pre-requisites:**\n1. Sign in to [Tailscale OAuth settings](https://login.tailscale.com/admin/settings/oauth)\n2. Create an OAuth client with the **Audit Logs - Read** scope (Standard connector). For the Premium connector also tick **Network Logs - Read**.\n3. Copy the client ID and client secret (secret shown once)\n4. Note your tailnet name from the [Keys page](https://login.tailscale.com/admin/settings/keys)\n\n**Data Connectors:** 2, **Workbooks:** 2, **Analytic Rules:** 5, **Hunting Queries:** 8\n\n[Learn more about Microsoft Sentinel](https://aka.ms/azuresentinel) | [Learn more about Solutions](https://aka.ms/azuresentinelsolutionsdoc)", "subscription": { "resourceProviders": [ "Microsoft.OperationsManagement/solutions", @@ -92,6 +92,62 @@ } ] }, + { + "name": "workbooks", + "label": "Workbooks", + "subLabel": { + "preValidation": "Configure the workbooks", + "postValidation": "Done" + }, + "bladeTitle": "Workbooks", + "elements": [ + { + "name": "workbooks-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "This solution installs workbook(s) to help you gain insights into the telemetry collected in Microsoft Sentinel. After installing the solution, start using the workbook in Manage solution view." + } + }, + { + "name": "workbooks-link", + "type": "Microsoft.Common.TextBlock", + "options": { + "link": { + "label": "Learn more", + "uri": "https://docs.microsoft.com/azure/sentinel/tutorial-monitor-your-data" + } + } + }, + { + "name": "workbook1", + "type": "Microsoft.Common.Section", + "label": "Tailscale Operations (Standard)", + "elements": [ + { + "name": "workbook1-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "Tailscale Operations workbook for Standard tier — configuration audit, identity activity, ACL change history, OAuth/API token lifecycle, and security signals." + } + } + ] + }, + { + "name": "workbook2", + "type": "Microsoft.Common.Section", + "label": "Tailscale Operations (Premium)", + "elements": [ + { + "name": "workbook2-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "Tailscale Operations workbook for Premium / Enterprise tier — configuration audit plus network flow telemetry (top talkers, exit-node usage, subnet router throughput)." + } + } + ] + } + ] + }, { "name": "analytics", "label": "Analytics", @@ -189,6 +245,142 @@ ] } ] + }, + { + "name": "huntingqueries", + "label": "Hunting Queries", + "bladeTitle": "Hunting Queries", + "elements": [ + { + "name": "huntingqueries-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "This solution installs the following hunting queries. After installing the solution, run these hunting queries to hunt for threats in Manage solution view. " + } + }, + { + "name": "huntingqueries-link", + "type": "Microsoft.Common.TextBlock", + "options": { + "link": { + "label": "Learn more", + "uri": "https://docs.microsoft.com/azure/sentinel/hunting" + } + } + }, + { + "name": "huntingquery1", + "type": "Microsoft.Common.Section", + "label": "Tailscale: First-seen actor making configuration changes", + "elements": [ + { + "name": "huntingquery1-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "Surfaces actors making their first configuration change in the lookback window. New legitimate admins look identical to compromised credentials — review whether each surfaced actor is expected to have admin rights. This hunting query depends on TailscaleCCF data connector (Tailscale_Configuration_CL Parser or Table)" + } + } + ] + }, + { + "name": "huntingquery2", + "type": "Microsoft.Common.Section", + "label": "Tailscale: ACL policy churn", + "elements": [ + { + "name": "huntingquery2-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "Identifies short windows where the tailnet ACL policy file was rewritten multiple times. Iterative policy edits during an incident can indicate rule-hunting by an attacker or a misconfiguration spiral; legitimate ACL refactors usually land as a single change. This hunting query depends on TailscaleCCF data connector (Tailscale_Configuration_CL Parser or Table)" + } + } + ] + }, + { + "name": "huntingquery3", + "type": "Microsoft.Common.Section", + "label": "Tailscale: Off-hours configuration changes", + "elements": [ + { + "name": "huntingquery3-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "Lists configuration audit events that occurred outside typical business hours (defined as Monday-Friday 08:00-18:00 UTC). Adjust the time window to match your operating region. Useful for spotting impromptu maintenance, account compromise, or insider activity. This hunting query depends on TailscaleCCF data connector (Tailscale_Configuration_CL Parser or Table)" + } + } + ] + }, + { + "name": "huntingquery4", + "type": "Microsoft.Common.Section", + "label": "Tailscale: Auth key sprawl", + "elements": [ + { + "name": "huntingquery4-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "Actors creating multiple auth keys in a short window. A single admin creating many keys for unattended enrollment is normal during a rollout; the same pattern can also indicate token-spraying for persistence. Cross-reference with the Tailscale: Auth key created alert to filter context. This hunting query depends on TailscaleCCF data connector (Tailscale_Configuration_CL Parser or Table)" + } + } + ] + }, + { + "name": "huntingquery5", + "type": "Microsoft.Common.Section", + "label": "Tailscale: New src->dst node pairs (lateral movement candidates)", + "elements": [ + { + "name": "huntingquery5-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "Lists tailnet src->dst pairs observed in the last 24h that were NOT observed in the prior 7-day baseline. Useful for spotting lateral movement to nodes that don't usually talk to each other. Requires Tailscale Premium or Enterprise (network flow logs). This hunting query depends on TailscalePremiumCCF data connector (Tailscale_Network_CL Parser or Table)" + } + } + ] + }, + { + "name": "huntingquery6", + "type": "Microsoft.Common.Section", + "label": "Tailscale: Top talkers by bytes (virtual traffic)", + "elements": [ + { + "name": "huntingquery6-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "Ranks tailnet src->dst pairs by total bytes transferred over the last 24h. Useful for capacity planning, identifying data-heavy flows, and spotting unexpected volume that could indicate data staging. Requires Tailscale Premium or Enterprise. This hunting query depends on TailscalePremiumCCF data connector (Tailscale_Network_CL Parser or Table)" + } + } + ] + }, + { + "name": "huntingquery7", + "type": "Microsoft.Common.Section", + "label": "Tailscale: Exit-node usage patterns", + "elements": [ + { + "name": "huntingquery7-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "Summarises traffic leaving the tailnet via exit nodes. Exit node use is typically intentional (regional egress, privacy routing) but unexpected exit-node traffic from a node should be investigated as a potential pivot point or unsanctioned egress. Requires Tailscale Premium or Enterprise. This hunting query depends on TailscalePremiumCCF data connector (Tailscale_Network_CL Parser or Table)" + } + } + ] + }, + { + "name": "huntingquery8", + "type": "Microsoft.Common.Section", + "label": "Tailscale: Beaconing candidates (regular periodic flows)", + "elements": [ + { + "name": "huntingquery8-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "Detects flows that recur at a highly regular interval, which is the signature of C2 beaconing or scheduled exfiltration jobs. Looks for src->dst pairs where 80%+ of inter-flow gaps cluster around the same delta. Requires Tailscale Premium or Enterprise. This hunting query depends on TailscalePremiumCCF data connector (Tailscale_Network_CL Parser or Table)" + } + } + ] + } + ] } ], "outputs": { diff --git a/Solutions/Tailscale (CCF)/Package/mainTemplate.json b/Solutions/Tailscale (CCF)/Package/mainTemplate.json index 41c52cc1c5b..f473b5f8236 100644 --- a/Solutions/Tailscale (CCF)/Package/mainTemplate.json +++ b/Solutions/Tailscale (CCF)/Package/mainTemplate.json @@ -41,6 +41,22 @@ "metadata": { "description": "subscription id where Microsoft Sentinel is setup" } + }, + "workbook1-name": { + "type": "string", + "defaultValue": "Tailscale Operations (Standard)", + "minLength": 1, + "metadata": { + "description": "Name for the workbook" + } + }, + "workbook2-name": { + "type": "string", + "defaultValue": "Tailscale Operations (Premium)", + "minLength": 1, + "metadata": { + "description": "Name for the workbook" + } } }, "variables": { @@ -95,6 +111,58 @@ "analyticRuleTemplateSpecName5": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('f817e2fa-6fa0-fc25-5369-cef9b58771af')))]", "_analyticRulecontentProductId5": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','f817e2fa-6fa0-fc25-5369-cef9b58771af','-', '1.0.0')))]" }, + "huntingQueryObject1": { + "huntingQueryVersion1": "1.0.0", + "_huntingQuerycontentId1": "a91f4d3c-1b7e-4f2a-9c1d-0e3b5f7c8d9a", + "huntingQueryTemplateSpecName1": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-hq-',uniquestring('a91f4d3c-1b7e-4f2a-9c1d-0e3b5f7c8d9a')))]" + }, + "huntingQueryObject2": { + "huntingQueryVersion2": "1.0.0", + "_huntingQuerycontentId2": "b82e5d4d-2c8f-5e3b-ad2e-1f4c6e8d9eab", + "huntingQueryTemplateSpecName2": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-hq-',uniquestring('b82e5d4d-2c8f-5e3b-ad2e-1f4c6e8d9eab')))]" + }, + "huntingQueryObject3": { + "huntingQueryVersion3": "1.0.0", + "_huntingQuerycontentId3": "c73f6e5e-3d9a-6f4c-be3f-2a5d7f9eafbc", + "huntingQueryTemplateSpecName3": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-hq-',uniquestring('c73f6e5e-3d9a-6f4c-be3f-2a5d7f9eafbc')))]" + }, + "huntingQueryObject4": { + "huntingQueryVersion4": "1.0.0", + "_huntingQuerycontentId4": "d64e7f6f-4eab-7a5d-cf4a-3b6e8aafbcad", + "huntingQueryTemplateSpecName4": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-hq-',uniquestring('d64e7f6f-4eab-7a5d-cf4a-3b6e8aafbcad')))]" + }, + "huntingQueryObject5": { + "huntingQueryVersion5": "1.0.0", + "_huntingQuerycontentId5": "e55f8aaf-5fbc-8b6e-d05b-4c7faabcadbe", + "huntingQueryTemplateSpecName5": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-hq-',uniquestring('e55f8aaf-5fbc-8b6e-d05b-4c7faabcadbe')))]" + }, + "huntingQueryObject6": { + "huntingQueryVersion6": "1.0.0", + "_huntingQuerycontentId6": "f46a9bb0-6acd-9c7f-e16c-5d8abbcdbeca", + "huntingQueryTemplateSpecName6": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-hq-',uniquestring('f46a9bb0-6acd-9c7f-e16c-5d8abbcdbeca')))]" + }, + "huntingQueryObject7": { + "huntingQueryVersion7": "1.0.0", + "_huntingQuerycontentId7": "a37bacc1-7bde-ad8a-f27d-6e9bcdcecadb", + "huntingQueryTemplateSpecName7": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-hq-',uniquestring('a37bacc1-7bde-ad8a-f27d-6e9bcdcecadb')))]" + }, + "huntingQueryObject8": { + "huntingQueryVersion8": "1.0.0", + "_huntingQuerycontentId8": "b28cbdd2-8cef-be9b-a38e-7faceedfdbec", + "huntingQueryTemplateSpecName8": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-hq-',uniquestring('b28cbdd2-8cef-be9b-a38e-7faceedfdbec')))]" + }, + "workbookVersion1": "1.0.0", + "workbookContentId1": "TailscaleStandardOperationsWorkbook", + "workbookId1": "[resourceId('Microsoft.Insights/workbooks', variables('workbookContentId1'))]", + "workbookTemplateSpecName1": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-wb-',uniquestring(variables('_workbookContentId1'))))]", + "_workbookContentId1": "[variables('workbookContentId1')]", + "_workbookcontentProductId1": "[concat(take(variables('_solutionId'),50),'-','wb','-', uniqueString(concat(variables('_solutionId'),'-','Workbook','-',variables('_workbookContentId1'),'-', variables('workbookVersion1'))))]", + "workbookVersion2": "1.0.0", + "workbookContentId2": "TailscalePremiumOperationsWorkbook", + "workbookId2": "[resourceId('Microsoft.Insights/workbooks', variables('workbookContentId2'))]", + "workbookTemplateSpecName2": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-wb-',uniquestring(variables('_workbookContentId2'))))]", + "_workbookContentId2": "[variables('workbookContentId2')]", + "_workbookcontentProductId2": "[concat(take(variables('_solutionId'),50),'-','wb','-', uniqueString(concat(variables('_solutionId'),'-','Workbook','-',variables('_workbookContentId2'),'-', variables('workbookVersion2'))))]", "_solutioncontentProductId": "[concat(take(variables('_solutionId'),50),'-','sl','-', uniqueString(concat(variables('_solutionId'),'-','Solution','-',variables('_solutionId'),'-', variables('_solutionVersion'))))]" }, "resources": [ @@ -1542,24 +1610,24 @@ ], "entityMappings": [ { + "entityType": "Account", "fieldMappings": [ { - "columnName": "ActorLogin", - "identifier": "FullName" + "identifier": "FullName", + "columnName": "ActorLogin" } - ], - "entityType": "Account" + ] } ], "incidentConfiguration": { - "createIncident": true, "groupingConfiguration": { - "lookbackDuration": "PT5H", - "matchingMethod": "AllEntities", - "reopenClosedIncident": false, + "enabled": true, "groupByEntities": [], - "enabled": true - } + "reopenClosedIncident": false, + "lookbackDuration": "PT5H", + "matchingMethod": "AllEntities" + }, + "createIncident": true } } }, @@ -1655,24 +1723,24 @@ ], "entityMappings": [ { + "entityType": "Account", "fieldMappings": [ { - "columnName": "ActorLogin", - "identifier": "FullName" + "identifier": "FullName", + "columnName": "ActorLogin" } - ], - "entityType": "Account" + ] } ], "incidentConfiguration": { - "createIncident": true, "groupingConfiguration": { - "lookbackDuration": "PT5H", - "matchingMethod": "AllEntities", - "reopenClosedIncident": false, + "enabled": true, "groupByEntities": [], - "enabled": true - } + "reopenClosedIncident": false, + "lookbackDuration": "PT5H", + "matchingMethod": "AllEntities" + }, + "createIncident": true } } }, @@ -1767,24 +1835,24 @@ ], "entityMappings": [ { + "entityType": "Account", "fieldMappings": [ { - "columnName": "ActorLogin", - "identifier": "FullName" + "identifier": "FullName", + "columnName": "ActorLogin" } - ], - "entityType": "Account" + ] } ], "incidentConfiguration": { - "createIncident": true, "groupingConfiguration": { - "lookbackDuration": "PT5H", - "matchingMethod": "AllEntities", - "reopenClosedIncident": false, + "enabled": true, "groupByEntities": [], - "enabled": true - } + "reopenClosedIncident": false, + "lookbackDuration": "PT5H", + "matchingMethod": "AllEntities" + }, + "createIncident": true } } }, @@ -1880,33 +1948,33 @@ ], "entityMappings": [ { + "entityType": "Account", "fieldMappings": [ { - "columnName": "ActorLogin", - "identifier": "FullName" + "identifier": "FullName", + "columnName": "ActorLogin" } - ], - "entityType": "Account" + ] }, { + "entityType": "Host", "fieldMappings": [ { - "columnName": "NodeName", - "identifier": "HostName" + "identifier": "HostName", + "columnName": "NodeName" } - ], - "entityType": "Host" + ] } ], "incidentConfiguration": { - "createIncident": true, "groupingConfiguration": { - "lookbackDuration": "PT5H", - "matchingMethod": "AllEntities", - "reopenClosedIncident": false, + "enabled": true, "groupByEntities": [], - "enabled": true - } + "reopenClosedIncident": false, + "lookbackDuration": "PT5H", + "matchingMethod": "AllEntities" + }, + "createIncident": true } } }, @@ -2002,24 +2070,24 @@ ], "entityMappings": [ { + "entityType": "Account", "fieldMappings": [ { - "columnName": "ActorLogin", - "identifier": "FullName" + "identifier": "FullName", + "columnName": "ActorLogin" } - ], - "entityType": "Account" + ] } ], "incidentConfiguration": { - "createIncident": true, "groupingConfiguration": { - "lookbackDuration": "PT5H", - "matchingMethod": "AllEntities", - "reopenClosedIncident": false, + "enabled": true, "groupByEntities": [], - "enabled": true - } + "reopenClosedIncident": false, + "lookbackDuration": "PT5H", + "matchingMethod": "AllEntities" + }, + "createIncident": true } } }, @@ -2064,72 +2132,962 @@ } }, { - "type": "Microsoft.OperationalInsights/workspaces/providers/contentPackages", + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", "apiVersion": "2023-04-01-preview", + "name": "[variables('huntingQueryObject1').huntingQueryTemplateSpecName1]", "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], "properties": { - "version": "3.0.1", - "kind": "Solution", - "contentSchemaVersion": "3.0.0", - "displayName": "Tailscale (CCF)", - "publisherDisplayName": "Community", - "descriptionHtml": "

Note: Please refer to the following before installing the solution:

\n

• Review the solution Release Notes

\n

• There may be known issues pertaining to this Solution, please refer to them before installing.

\n

The Tailscale solution for Microsoft Sentinel ingests Tailscale telemetry via OAuth2-secured APIs.

\n

Data connectors in this solution (install one based on your Tailscale plan):

\n
    \n
  • Tailscale Standard (CCF) — Configuration audit logs (/logging/configuration). Use on Personal (Free) and Standard tailnets. Included in this release.
  • \n
  • Tailscale Premium (CCF) — Configuration audit + network flow logs (/logging/configuration + /logging/network). Use on Premium and Enterprise tailnets. Planned for a future release.
  • \n
\n

Underlying technology: Microsoft Sentinel Codeless Connector Framework (CCF) with OAuth2 client-credentials auth.

\n

Pre-requisites:

\n
    \n
  1. Sign in to Tailscale OAuth settings
  2. \n
  3. Create an OAuth client with the Audit Logs - Read scope (Standard connector). For the Premium connector also tick Network Logs - Read.
  4. \n
  5. Copy the client ID and client secret (secret shown once)
  6. \n
  7. Note your tailnet name from the Keys page
  8. \n
\n

Data Connectors: 2, Analytic Rules: 5

\n

Learn more about Microsoft Sentinel | Learn more about Solutions

\n", - "contentKind": "Solution", - "contentProductId": "[variables('_solutioncontentProductId')]", - "id": "[variables('_solutioncontentProductId')]", - "icon": "", - "contentId": "[variables('_solutionId')]", - "parentId": "[variables('_solutionId')]", - "source": { - "kind": "Solution", - "name": "Tailscale (CCF)", - "sourceId": "[variables('_solutionId')]" - }, - "author": { - "name": "noodlemctwoodle" - }, - "support": { - "name": "Community", - "tier": "Community", - "link": "https://github.com/Azure/Azure-Sentinel/issues" - }, - "dependencies": { - "operator": "AND", - "criteria": [ - { - "kind": "DataConnector", - "contentId": "[variables('_dataConnectorContentIdConnections1')]", - "version": "[variables('dataConnectorCCPVersion')]" - }, + "description": "TailscaleFirstSeenActor_HuntingQueries Hunting Query with template version 3.0.1", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('huntingQueryObject1').huntingQueryVersion1]", + "parameters": {}, + "variables": {}, + "resources": [ { - "kind": "DataConnector", - "contentId": "[variables('_dataConnectorContentIdConnections2')]", - "version": "[variables('dataConnectorCCPVersion')]" + "type": "Microsoft.OperationalInsights/savedSearches", + "apiVersion": "2025-07-01", + "name": "Tailscale_(CCF)_Hunting_Query_1", + "location": "[parameters('workspace-location')]", + "properties": { + "eTag": "*", + "displayName": "Tailscale: First-seen actor making configuration changes", + "category": "Hunting Queries", + "query": "let lookback = 14d;\nlet baselineWindow = 14d;\nTailscale_Configuration_CL\n| where TimeGenerated > ago(lookback)\n| extend ActorLogin = tostring(Actor.loginName)\n| where isnotempty(ActorLogin)\n| summarize FirstSeen = min(TimeGenerated), Actions = make_set(Action), Targets = make_set(tostring(Target.type)), TotalEvents = count() by ActorLogin\n| join kind=leftanti (\n Tailscale_Configuration_CL\n | where TimeGenerated between (ago(lookback + baselineWindow) .. ago(lookback))\n | extend ActorLogin = tostring(Actor.loginName)\n | distinct ActorLogin\n) on ActorLogin\n| order by FirstSeen asc\n", + "version": 2, + "tags": [ + { + "name": "description", + "value": "Surfaces actors making their first configuration change in the lookback window. New legitimate admins look identical to compromised credentials — review whether each surfaced actor is expected to have admin rights." + }, + { + "name": "tactics", + "value": "InitialAccess,Persistence" + }, + { + "name": "techniques", + "value": "T1078" + } + ] + } }, { - "kind": "AnalyticsRule", - "contentId": "[variables('analyticRuleObject1')._analyticRulecontentId1]", - "version": "[variables('analyticRuleObject1').analyticRuleVersion1]" - }, + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject1')._huntingQuerycontentId1),'/'))))]", + "properties": { + "description": "Tailscale (CCF) Hunting Query 1", + "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject1')._huntingQuerycontentId1)]", + "contentId": "[variables('huntingQueryObject1')._huntingQuerycontentId1]", + "kind": "HuntingQuery", + "version": "[variables('huntingQueryObject1').huntingQueryVersion1]", + "source": { + "kind": "Solution", + "name": "Tailscale (CCF)", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "noodlemctwoodle" + }, + "support": { + "name": "Community", + "tier": "Community", + "link": "https://github.com/Azure/Azure-Sentinel/issues" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('huntingQueryObject1')._huntingQuerycontentId1]", + "contentKind": "HuntingQuery", + "displayName": "Tailscale: First-seen actor making configuration changes", + "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject1')._huntingQuerycontentId1,'-', '1.0.0')))]", + "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject1')._huntingQuerycontentId1,'-', '1.0.0')))]", + "version": "1.0.0" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('huntingQueryObject2').huntingQueryTemplateSpecName2]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "TailscaleACLPolicyChurn_HuntingQueries Hunting Query with template version 3.0.1", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('huntingQueryObject2').huntingQueryVersion2]", + "parameters": {}, + "variables": {}, + "resources": [ { - "kind": "AnalyticsRule", - "contentId": "[variables('analyticRuleObject2')._analyticRulecontentId2]", - "version": "[variables('analyticRuleObject2').analyticRuleVersion2]" + "type": "Microsoft.OperationalInsights/savedSearches", + "apiVersion": "2025-07-01", + "name": "Tailscale_(CCF)_Hunting_Query_2", + "location": "[parameters('workspace-location')]", + "properties": { + "eTag": "*", + "displayName": "Tailscale: ACL policy churn", + "category": "Hunting Queries", + "query": "let bucket = 30m;\nlet churnThreshold = 3;\nTailscale_Configuration_CL\n| where Action == \"WRITE\"\n| where tostring(Target.type) == \"ACL\"\n| extend ActorLogin = tostring(Actor.loginName)\n| summarize EditCount = count(), Editors = make_set(ActorLogin), FirstEdit = min(TimeGenerated), LastEdit = max(TimeGenerated)\n by bin(TimeGenerated, bucket)\n| where EditCount >= churnThreshold\n| order by EditCount desc\n", + "version": 2, + "tags": [ + { + "name": "description", + "value": "Identifies short windows where the tailnet ACL policy file was rewritten multiple times. Iterative policy edits during an incident can indicate rule-hunting by an attacker or a misconfiguration spiral; legitimate ACL refactors usually land as a single change." + }, + { + "name": "tactics", + "value": "DefenseEvasion,PrivilegeEscalation" + }, + { + "name": "techniques", + "value": "T1098,T1556" + } + ] + } }, { - "kind": "AnalyticsRule", - "contentId": "[variables('analyticRuleObject3')._analyticRulecontentId3]", - "version": "[variables('analyticRuleObject3').analyticRuleVersion3]" - }, + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject2')._huntingQuerycontentId2),'/'))))]", + "properties": { + "description": "Tailscale (CCF) Hunting Query 2", + "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject2')._huntingQuerycontentId2)]", + "contentId": "[variables('huntingQueryObject2')._huntingQuerycontentId2]", + "kind": "HuntingQuery", + "version": "[variables('huntingQueryObject2').huntingQueryVersion2]", + "source": { + "kind": "Solution", + "name": "Tailscale (CCF)", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "noodlemctwoodle" + }, + "support": { + "name": "Community", + "tier": "Community", + "link": "https://github.com/Azure/Azure-Sentinel/issues" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('huntingQueryObject2')._huntingQuerycontentId2]", + "contentKind": "HuntingQuery", + "displayName": "Tailscale: ACL policy churn", + "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject2')._huntingQuerycontentId2,'-', '1.0.0')))]", + "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject2')._huntingQuerycontentId2,'-', '1.0.0')))]", + "version": "1.0.0" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('huntingQueryObject3').huntingQueryTemplateSpecName3]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "TailscaleOffHoursConfigChanges_HuntingQueries Hunting Query with template version 3.0.1", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('huntingQueryObject3').huntingQueryVersion3]", + "parameters": {}, + "variables": {}, + "resources": [ { - "kind": "AnalyticsRule", - "contentId": "[variables('analyticRuleObject4')._analyticRulecontentId4]", - "version": "[variables('analyticRuleObject4').analyticRuleVersion4]" + "type": "Microsoft.OperationalInsights/savedSearches", + "apiVersion": "2025-07-01", + "name": "Tailscale_(CCF)_Hunting_Query_3", + "location": "[parameters('workspace-location')]", + "properties": { + "eTag": "*", + "displayName": "Tailscale: Off-hours configuration changes", + "category": "Hunting Queries", + "query": "let businessStartUtc = 8;\nlet businessEndUtc = 18;\nTailscale_Configuration_CL\n| extend HourOfDay = datetime_part(\"hour\", TimeGenerated), DayOfWeek = dayofweek(TimeGenerated)\n| where DayOfWeek in (0d, 6d) // Sunday and Saturday\n or HourOfDay < businessStartUtc\n or HourOfDay >= businessEndUtc\n| extend ActorLogin = tostring(Actor.loginName)\n| extend TargetType = tostring(Target.type)\n| project TimeGenerated, ActorLogin, Action, TargetType, Target, Origin\n| order by TimeGenerated desc\n", + "version": 2, + "tags": [ + { + "name": "description", + "value": "Lists configuration audit events that occurred outside typical business hours (defined as Monday-Friday 08:00-18:00 UTC). Adjust the time window to match your operating region. Useful for spotting impromptu maintenance, account compromise, or insider activity." + }, + { + "name": "tactics", + "value": "InitialAccess,Persistence" + }, + { + "name": "techniques", + "value": "T1078" + } + ] + } }, { - "kind": "AnalyticsRule", - "contentId": "[variables('analyticRuleObject5')._analyticRulecontentId5]", - "version": "[variables('analyticRuleObject5').analyticRuleVersion5]" + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject3')._huntingQuerycontentId3),'/'))))]", + "properties": { + "description": "Tailscale (CCF) Hunting Query 3", + "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject3')._huntingQuerycontentId3)]", + "contentId": "[variables('huntingQueryObject3')._huntingQuerycontentId3]", + "kind": "HuntingQuery", + "version": "[variables('huntingQueryObject3').huntingQueryVersion3]", + "source": { + "kind": "Solution", + "name": "Tailscale (CCF)", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "noodlemctwoodle" + }, + "support": { + "name": "Community", + "tier": "Community", + "link": "https://github.com/Azure/Azure-Sentinel/issues" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('huntingQueryObject3')._huntingQuerycontentId3]", + "contentKind": "HuntingQuery", + "displayName": "Tailscale: Off-hours configuration changes", + "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject3')._huntingQuerycontentId3,'-', '1.0.0')))]", + "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject3')._huntingQuerycontentId3,'-', '1.0.0')))]", + "version": "1.0.0" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('huntingQueryObject4').huntingQueryTemplateSpecName4]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "TailscaleAuthKeySprawl_HuntingQueries Hunting Query with template version 3.0.1", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('huntingQueryObject4').huntingQueryVersion4]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.OperationalInsights/savedSearches", + "apiVersion": "2025-07-01", + "name": "Tailscale_(CCF)_Hunting_Query_4", + "location": "[parameters('workspace-location')]", + "properties": { + "eTag": "*", + "displayName": "Tailscale: Auth key sprawl", + "category": "Hunting Queries", + "query": "let bucket = 1h;\nlet sprawlThreshold = 5;\nTailscale_Configuration_CL\n| where Action == \"CREATE\"\n| where tostring(Target.type) == \"AUTH_KEY\"\n| extend ActorLogin = tostring(Actor.loginName)\n| summarize KeysCreated = count(), KeyIDs = make_set(tostring(Target.id)), Reusable = make_set(tostring(New.reusable)), Ephemeral = make_set(tostring(New.ephemeral))\n by bin(TimeGenerated, bucket), ActorLogin\n| where KeysCreated >= sprawlThreshold\n| order by KeysCreated desc\n", + "version": 2, + "tags": [ + { + "name": "description", + "value": "Actors creating multiple auth keys in a short window. A single admin creating many keys for unattended enrollment is normal during a rollout; the same pattern can also indicate token-spraying for persistence. Cross-reference with the Tailscale: Auth key created alert to filter context." + }, + { + "name": "tactics", + "value": "Persistence,CredentialAccess" + }, + { + "name": "techniques", + "value": "T1098,T1136" + } + ] + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject4')._huntingQuerycontentId4),'/'))))]", + "properties": { + "description": "Tailscale (CCF) Hunting Query 4", + "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject4')._huntingQuerycontentId4)]", + "contentId": "[variables('huntingQueryObject4')._huntingQuerycontentId4]", + "kind": "HuntingQuery", + "version": "[variables('huntingQueryObject4').huntingQueryVersion4]", + "source": { + "kind": "Solution", + "name": "Tailscale (CCF)", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "noodlemctwoodle" + }, + "support": { + "name": "Community", + "tier": "Community", + "link": "https://github.com/Azure/Azure-Sentinel/issues" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('huntingQueryObject4')._huntingQuerycontentId4]", + "contentKind": "HuntingQuery", + "displayName": "Tailscale: Auth key sprawl", + "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject4')._huntingQuerycontentId4,'-', '1.0.0')))]", + "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject4')._huntingQuerycontentId4,'-', '1.0.0')))]", + "version": "1.0.0" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('huntingQueryObject5').huntingQueryTemplateSpecName5]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "TailscaleNewNodePairs_HuntingQueries Hunting Query with template version 3.0.1", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('huntingQueryObject5').huntingQueryVersion5]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.OperationalInsights/savedSearches", + "apiVersion": "2025-07-01", + "name": "Tailscale_(CCF)_Hunting_Query_5", + "location": "[parameters('workspace-location')]", + "properties": { + "eTag": "*", + "displayName": "Tailscale: New src->dst node pairs (lateral movement candidates)", + "category": "Hunting Queries", + "query": "let recent = 1d;\nlet baseline = 7d;\nlet recentPairs =\n Tailscale_Network_CL\n | where TimeGenerated > ago(recent)\n | mv-expand t = VirtualTraffic\n | extend Src = tostring(t.src), Dst = tostring(t.dst), Proto = toint(t.proto)\n | summarize FirstSeen = min(TimeGenerated), TxBytes = sum(tolong(t.txBytes)), RxBytes = sum(tolong(t.rxBytes)), FlowCount = count() by Src, Dst, Proto;\nlet baselinePairs =\n Tailscale_Network_CL\n | where TimeGenerated between (ago(baseline + recent) .. ago(recent))\n | mv-expand t = VirtualTraffic\n | extend Src = tostring(t.src), Dst = tostring(t.dst), Proto = toint(t.proto)\n | distinct Src, Dst, Proto;\nrecentPairs\n| join kind=leftanti baselinePairs on Src, Dst, Proto\n| order by FirstSeen asc\n", + "version": 2, + "tags": [ + { + "name": "description", + "value": "Lists tailnet src->dst pairs observed in the last 24h that were NOT observed in the prior 7-day baseline. Useful for spotting lateral movement to nodes that don't usually talk to each other. Requires Tailscale Premium or Enterprise (network flow logs)." + }, + { + "name": "tactics", + "value": "LateralMovement,Discovery" + }, + { + "name": "techniques", + "value": "T1021,T1018" + } + ] + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject5')._huntingQuerycontentId5),'/'))))]", + "properties": { + "description": "Tailscale (CCF) Hunting Query 5", + "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject5')._huntingQuerycontentId5)]", + "contentId": "[variables('huntingQueryObject5')._huntingQuerycontentId5]", + "kind": "HuntingQuery", + "version": "[variables('huntingQueryObject5').huntingQueryVersion5]", + "source": { + "kind": "Solution", + "name": "Tailscale (CCF)", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "noodlemctwoodle" + }, + "support": { + "name": "Community", + "tier": "Community", + "link": "https://github.com/Azure/Azure-Sentinel/issues" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('huntingQueryObject5')._huntingQuerycontentId5]", + "contentKind": "HuntingQuery", + "displayName": "Tailscale: New src->dst node pairs (lateral movement candidates)", + "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject5')._huntingQuerycontentId5,'-', '1.0.0')))]", + "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject5')._huntingQuerycontentId5,'-', '1.0.0')))]", + "version": "1.0.0" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('huntingQueryObject6').huntingQueryTemplateSpecName6]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "TailscaleTopTalkers_HuntingQueries Hunting Query with template version 3.0.1", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('huntingQueryObject6').huntingQueryVersion6]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.OperationalInsights/savedSearches", + "apiVersion": "2025-07-01", + "name": "Tailscale_(CCF)_Hunting_Query_6", + "location": "[parameters('workspace-location')]", + "properties": { + "eTag": "*", + "displayName": "Tailscale: Top talkers by bytes (virtual traffic)", + "category": "Hunting Queries", + "query": "Tailscale_Network_CL\n| where TimeGenerated > ago(1d)\n| mv-expand t = VirtualTraffic\n| extend Src = tostring(t.src), Dst = tostring(t.dst), Proto = toint(t.proto), TxBytes = tolong(t.txBytes), RxBytes = tolong(t.rxBytes)\n| summarize TotalBytes = sum(TxBytes + RxBytes), TotalPackets = sum(tolong(t.txPkts) + tolong(t.rxPkts)), FlowCount = count() by Src, Dst, Proto\n| extend TotalMB = round(TotalBytes / 1024.0 / 1024.0, 2)\n| top 50 by TotalBytes\n", + "version": 2, + "tags": [ + { + "name": "description", + "value": "Ranks tailnet src->dst pairs by total bytes transferred over the last 24h. Useful for capacity planning, identifying data-heavy flows, and spotting unexpected volume that could indicate data staging. Requires Tailscale Premium or Enterprise." + }, + { + "name": "tactics", + "value": "Exfiltration,Collection" + }, + { + "name": "techniques", + "value": "T1041,T1567" + } + ] + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject6')._huntingQuerycontentId6),'/'))))]", + "properties": { + "description": "Tailscale (CCF) Hunting Query 6", + "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject6')._huntingQuerycontentId6)]", + "contentId": "[variables('huntingQueryObject6')._huntingQuerycontentId6]", + "kind": "HuntingQuery", + "version": "[variables('huntingQueryObject6').huntingQueryVersion6]", + "source": { + "kind": "Solution", + "name": "Tailscale (CCF)", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "noodlemctwoodle" + }, + "support": { + "name": "Community", + "tier": "Community", + "link": "https://github.com/Azure/Azure-Sentinel/issues" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('huntingQueryObject6')._huntingQuerycontentId6]", + "contentKind": "HuntingQuery", + "displayName": "Tailscale: Top talkers by bytes (virtual traffic)", + "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject6')._huntingQuerycontentId6,'-', '1.0.0')))]", + "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject6')._huntingQuerycontentId6,'-', '1.0.0')))]", + "version": "1.0.0" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('huntingQueryObject7').huntingQueryTemplateSpecName7]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "TailscaleExitNodeUsage_HuntingQueries Hunting Query with template version 3.0.1", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('huntingQueryObject7').huntingQueryVersion7]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.OperationalInsights/savedSearches", + "apiVersion": "2025-07-01", + "name": "Tailscale_(CCF)_Hunting_Query_7", + "location": "[parameters('workspace-location')]", + "properties": { + "eTag": "*", + "displayName": "Tailscale: Exit-node usage patterns", + "category": "Hunting Queries", + "query": "Tailscale_Network_CL\n| where TimeGenerated > ago(1d)\n| where array_length(ExitTraffic) > 0\n| mv-expand t = ExitTraffic\n| extend Src = tostring(t.src), ExitDst = tostring(t.dst), Proto = toint(t.proto), TxBytes = tolong(t.txBytes), RxBytes = tolong(t.rxBytes)\n| summarize TotalBytes = sum(TxBytes + RxBytes), FlowCount = count(), ExitDestinations = make_set(ExitDst, 25)\n by NodeId, SrcNodeName = tostring(SrcNode.name), Src\n| extend TotalMB = round(TotalBytes / 1024.0 / 1024.0, 2)\n| order by TotalBytes desc\n| take 100\n", + "version": 2, + "tags": [ + { + "name": "description", + "value": "Summarises traffic leaving the tailnet via exit nodes. Exit node use is typically intentional (regional egress, privacy routing) but unexpected exit-node traffic from a node should be investigated as a potential pivot point or unsanctioned egress. Requires Tailscale Premium or Enterprise." + }, + { + "name": "tactics", + "value": "CommandAndControl,Exfiltration" + }, + { + "name": "techniques", + "value": "T1090,T1041" + } + ] + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject7')._huntingQuerycontentId7),'/'))))]", + "properties": { + "description": "Tailscale (CCF) Hunting Query 7", + "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject7')._huntingQuerycontentId7)]", + "contentId": "[variables('huntingQueryObject7')._huntingQuerycontentId7]", + "kind": "HuntingQuery", + "version": "[variables('huntingQueryObject7').huntingQueryVersion7]", + "source": { + "kind": "Solution", + "name": "Tailscale (CCF)", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "noodlemctwoodle" + }, + "support": { + "name": "Community", + "tier": "Community", + "link": "https://github.com/Azure/Azure-Sentinel/issues" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('huntingQueryObject7')._huntingQuerycontentId7]", + "contentKind": "HuntingQuery", + "displayName": "Tailscale: Exit-node usage patterns", + "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject7')._huntingQuerycontentId7,'-', '1.0.0')))]", + "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject7')._huntingQuerycontentId7,'-', '1.0.0')))]", + "version": "1.0.0" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('huntingQueryObject8').huntingQueryTemplateSpecName8]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "TailscaleBeaconingCandidates_HuntingQueries Hunting Query with template version 3.0.1", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('huntingQueryObject8').huntingQueryVersion8]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.OperationalInsights/savedSearches", + "apiVersion": "2025-07-01", + "name": "Tailscale_(CCF)_Hunting_Query_8", + "location": "[parameters('workspace-location')]", + "properties": { + "eTag": "*", + "displayName": "Tailscale: Beaconing candidates (regular periodic flows)", + "category": "Hunting Queries", + "query": "let lookback = 2d;\nlet minFlows = 10;\nlet beaconPercentThreshold = 80.0;\nTailscale_Network_CL\n| where TimeGenerated > ago(lookback)\n| mv-expand t = VirtualTraffic\n| extend Src = tostring(t.src), Dst = tostring(t.dst), Proto = toint(t.proto)\n| project TimeGenerated, Src, Dst, Proto\n| sort by Src asc, Dst asc, Proto asc, TimeGenerated asc\n| serialize\n| extend NextTime = next(TimeGenerated), NextSrc = next(Src), NextDst = next(Dst), NextProto = next(Proto)\n| where Src == NextSrc and Dst == NextDst and Proto == NextProto\n| extend DeltaSec = datetime_diff('second', NextTime, TimeGenerated)\n| where DeltaSec > 5\n| summarize DeltaCount = count() by Src, Dst, Proto, DeltaSec\n| summarize (MostFrequentDeltaCount, MostFrequentDeltaSec) = arg_max(DeltaCount, DeltaSec), TotalFlows = sum(DeltaCount) by Src, Dst, Proto\n| where TotalFlows >= minFlows\n| extend BeaconPercent = round(MostFrequentDeltaCount * 100.0 / TotalFlows, 1)\n| where BeaconPercent >= beaconPercentThreshold\n| order by BeaconPercent desc\n", + "version": 2, + "tags": [ + { + "name": "description", + "value": "Detects flows that recur at a highly regular interval, which is the signature of C2 beaconing or scheduled exfiltration jobs. Looks for src->dst pairs where 80%+ of inter-flow gaps cluster around the same delta. Requires Tailscale Premium or Enterprise." + }, + { + "name": "tactics", + "value": "CommandAndControl,Exfiltration" + }, + { + "name": "techniques", + "value": "T1071,T1095,T1029" + } + ] + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject8')._huntingQuerycontentId8),'/'))))]", + "properties": { + "description": "Tailscale (CCF) Hunting Query 8", + "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject8')._huntingQuerycontentId8)]", + "contentId": "[variables('huntingQueryObject8')._huntingQuerycontentId8]", + "kind": "HuntingQuery", + "version": "[variables('huntingQueryObject8').huntingQueryVersion8]", + "source": { + "kind": "Solution", + "name": "Tailscale (CCF)", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "noodlemctwoodle" + }, + "support": { + "name": "Community", + "tier": "Community", + "link": "https://github.com/Azure/Azure-Sentinel/issues" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('huntingQueryObject8')._huntingQuerycontentId8]", + "contentKind": "HuntingQuery", + "displayName": "Tailscale: Beaconing candidates (regular periodic flows)", + "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject8')._huntingQuerycontentId8,'-', '1.0.0')))]", + "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject8')._huntingQuerycontentId8,'-', '1.0.0')))]", + "version": "1.0.0" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('workbookTemplateSpecName1')]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "TailscaleStandardOperations Workbook with template version 3.0.1", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('workbookVersion1')]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.Insights/workbooks", + "name": "[variables('workbookContentId1')]", + "location": "[parameters('workspace-location')]", + "kind": "shared", + "apiVersion": "2021-08-01", + "metadata": { + "description": "Tailscale Operations workbook for Standard tier — configuration audit, identity activity, ACL change history, OAuth/API token lifecycle, and security signals." + }, + "properties": { + "displayName": "[parameters('workbook1-name')]", + "serializedData": "{\"version\":\"Notebook/1.0\",\"items\":[{\"type\":1,\"content\":{\"json\":\"# Tailscale Operations (Standard)\\n\\nSecurity and operational view across the **Tailscale Standard (CCF)** data connector. Surfaces configuration audit data (`Tailscale_Configuration_CL`).\\n\\nFor network flow visibility, upgrade your tailnet to Premium / Enterprise and install **Tailscale Premium (CCF)** + use the **Tailscale Operations (Premium)** workbook.\"},\"name\":\"header\"},{\"type\":9,\"content\":{\"version\":\"KqlParameterItem/1.0\",\"parameters\":[{\"id\":\"timerange\",\"version\":\"KqlParameterItem/1.0\",\"name\":\"TimeRange\",\"type\":4,\"isRequired\":true,\"value\":{\"durationMs\":86400000},\"typeSettings\":{\"selectableValues\":[{\"durationMs\":3600000},{\"durationMs\":14400000},{\"durationMs\":43200000},{\"durationMs\":86400000},{\"durationMs\":172800000},{\"durationMs\":604800000},{\"durationMs\":2592000000}]}}],\"style\":\"pills\",\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\"},\"name\":\"parameters\"},{\"type\":11,\"content\":{\"version\":\"LinkItem/1.0\",\"style\":\"tabs\",\"links\":[{\"id\":\"overview\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Overview\",\"subTarget\":\"overview\",\"style\":\"link\"},{\"id\":\"config\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Identity & Configuration\",\"subTarget\":\"config\",\"style\":\"link\"},{\"id\":\"signals\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Security Signals\",\"subTarget\":\"signals\",\"style\":\"link\"}]},\"name\":\"tabs\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":1,\"content\":{\"json\":\"## Overview\"},\"name\":\"overview-header\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Configuration_CL\\n| where TimeGenerated {TimeRange}\\n| summarize TotalEvents = count(), UniqueActors = dcount(tostring(Actor.loginName)), ACLChanges = countif(Action == \\\"WRITE\\\" and tostring(Target.type) == \\\"ACL\\\"), AuthKeysCreated = countif(Action == \\\"CREATE\\\" and tostring(Target.type) == \\\"AUTH_KEY\\\")\",\"size\":4,\"title\":\"Configuration audit summary\",\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"tiles\",\"tileSettings\":{\"titleContent\":{\"columnMatch\":\"TotalEvents\",\"formatter\":12},\"showBorder\":true}},\"customWidth\":\"100\",\"name\":\"overview-tiles\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Configuration_CL\\n| where TimeGenerated {TimeRange}\\n| summarize EventCount = count() by bin(TimeGenerated, 1h), Action\\n| render timechart\",\"size\":0,\"title\":\"Configuration activity over time, by action\",\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\"},\"customWidth\":\"100\",\"name\":\"overview-timeseries\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Configuration_CL\\n| where TimeGenerated {TimeRange}\\n| extend ActorLogin = tostring(Actor.loginName)\\n| where isnotempty(ActorLogin)\\n| summarize EventCount = count() by ActorLogin\\n| top 10 by EventCount\\n| render barchart\",\"size\":0,\"title\":\"Top 10 actors\",\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\"},\"customWidth\":\"50\",\"name\":\"overview-top-actors\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Configuration_CL\\n| where TimeGenerated {TimeRange}\\n| summarize EventCount = count() by tostring(Target.type)\\n| top 10 by EventCount\\n| render piechart\",\"size\":0,\"title\":\"Target type distribution\",\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\"},\"customWidth\":\"50\",\"name\":\"overview-target-types\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"overview\"},\"name\":\"overview-group\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":1,\"content\":{\"json\":\"## Identity & Configuration\"},\"name\":\"config-header\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Configuration_CL\\n| where TimeGenerated {TimeRange}\\n| extend ActorLogin = tostring(Actor.loginName), TargetType = tostring(Target.type)\\n| summarize EventCount = count() by ActorLogin, Action, TargetType\\n| order by EventCount desc\",\"size\":0,\"title\":\"Actor / Action / Target heatmap\",\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\"},\"customWidth\":\"100\",\"name\":\"config-heatmap\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Configuration_CL\\n| where TimeGenerated {TimeRange}\\n| where tostring(Target.type) == \\\"ACL\\\"\\n| extend ActorLogin = tostring(Actor.loginName)\\n| project TimeGenerated, ActorLogin, Action, OldPolicy = Old, NewPolicy = New\\n| order by TimeGenerated desc\",\"size\":0,\"title\":\"ACL policy change history\",\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\"},\"customWidth\":\"100\",\"name\":\"config-acl-history\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Configuration_CL\\n| where TimeGenerated {TimeRange}\\n| where tostring(Target.type) == \\\"AUTH_KEY\\\"\\n| extend ActorLogin = tostring(Actor.loginName), Reusable = tostring(New.reusable), Ephemeral = tostring(New.ephemeral)\\n| project TimeGenerated, Action, ActorLogin, KeyId = tostring(Target.id), Reusable, Ephemeral, Description = tostring(New.description)\\n| order by TimeGenerated desc\",\"size\":0,\"title\":\"Auth key lifecycle\",\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\"},\"customWidth\":\"100\",\"name\":\"config-authkey-lifecycle\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Configuration_CL\\n| where TimeGenerated {TimeRange}\\n| where tostring(Target.type) in (\\\"API_KEY\\\", \\\"OAUTH_CLIENT\\\")\\n| extend ActorLogin = tostring(Actor.loginName)\\n| project TimeGenerated, Action, ActorLogin, Type = tostring(Target.type), TargetId = tostring(Target.id), Description = tostring(New.description)\\n| order by TimeGenerated desc\",\"size\":0,\"title\":\"API token and OAuth client activity\",\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\"},\"customWidth\":\"100\",\"name\":\"config-oauth-api\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"config\"},\"name\":\"config-group\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":1,\"content\":{\"json\":\"## Security Signals\\n\\nIncidents and alerts raised by Tailscale-related analytic rules.\"},\"name\":\"signals-header\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"SecurityAlert\\n| where TimeGenerated {TimeRange}\\n| where AlertName startswith \\\"Tailscale\\\"\\n| summarize Count = count() by AlertName, AlertSeverity\\n| order by Count desc\",\"size\":0,\"title\":\"Tailscale alerts by rule\",\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\"},\"customWidth\":\"50\",\"name\":\"signals-by-rule\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"SecurityAlert\\n| where TimeGenerated {TimeRange}\\n| where AlertName startswith \\\"Tailscale\\\"\\n| summarize Count = count() by AlertSeverity\\n| render piechart\",\"size\":0,\"title\":\"Severity distribution\",\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\"},\"customWidth\":\"50\",\"name\":\"signals-severity\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"SecurityAlert\\n| where TimeGenerated {TimeRange}\\n| where AlertName startswith \\\"Tailscale\\\"\\n| project TimeGenerated, AlertName, AlertSeverity, Description, Entities\\n| order by TimeGenerated desc\",\"size\":0,\"title\":\"Recent Tailscale alerts\",\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\"},\"customWidth\":\"100\",\"name\":\"signals-recent\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"signals\"},\"name\":\"signals-group\"}],\"fromTemplateId\":\"sentinel-TailscaleStandardOperations\",\"$schema\":\"https://github.com/Microsoft/Application-Insights-Workbooks/blob/master/schema/workbook.json\"}\n", + "version": "1.0", + "sourceId": "[variables('workspaceResourceId')]", + "category": "sentinel" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('Workbook-', last(split(variables('workbookId1'),'/'))))]", + "properties": { + "description": "@{workbookKey=TailscaleStandardOperationsWorkbook; logoFileName=Tailscale.svg; description=Tailscale Operations workbook for Standard tier — configuration audit, identity activity, ACL change history, OAuth/API token lifecycle, and security signals.; dataTypesDependencies=System.Object[]; dataConnectorsDependencies=System.Object[]; previewImagesFileNames=System.Object[]; version=1.0.0; title=Tailscale Operations (Standard); templateRelativePath=TailscaleStandardOperations.json; subtitle=; provider=noodlemctwoodle}.description", + "parentId": "[variables('workbookId1')]", + "contentId": "[variables('_workbookContentId1')]", + "kind": "Workbook", + "version": "[variables('workbookVersion1')]", + "source": { + "kind": "Solution", + "name": "Tailscale (CCF)", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "noodlemctwoodle" + }, + "support": { + "name": "Community", + "tier": "Community", + "link": "https://github.com/Azure/Azure-Sentinel/issues" + }, + "dependencies": { + "operator": "AND", + "criteria": [ + { + "contentId": "Tailscale_Configuration_CL", + "kind": "DataType" + }, + { + "contentId": "TailscaleCCF", + "kind": "DataConnector" + } + ] + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('_workbookContentId1')]", + "contentKind": "Workbook", + "displayName": "[parameters('workbook1-name')]", + "contentProductId": "[variables('_workbookcontentProductId1')]", + "id": "[variables('_workbookcontentProductId1')]", + "version": "[variables('workbookVersion1')]" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('workbookTemplateSpecName2')]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "TailscalePremiumOperations Workbook with template version 3.0.1", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('workbookVersion2')]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.Insights/workbooks", + "name": "[variables('workbookContentId2')]", + "location": "[parameters('workspace-location')]", + "kind": "shared", + "apiVersion": "2021-08-01", + "metadata": { + "description": "Tailscale Operations workbook for Premium / Enterprise tier — configuration audit plus network flow telemetry (top talkers, exit-node usage, subnet router throughput)." + }, + "properties": { + "displayName": "[parameters('workbook2-name')]", + "serializedData": "{\"version\":\"Notebook/1.0\",\"items\":[{\"type\":1,\"content\":{\"json\":\"# Tailscale Operations (Premium)\\n\\nFull security and operational view across the **Tailscale Premium (CCF)** data connector — covers configuration audit (`Tailscale_Configuration_CL`) **and** network flow (`Tailscale_Network_CL`) telemetry.\\n\\nFor Personal (Free) / Standard tier tailnets, use the **Tailscale Operations (Standard)** workbook instead.\"},\"name\":\"header\"},{\"type\":9,\"content\":{\"version\":\"KqlParameterItem/1.0\",\"parameters\":[{\"id\":\"timerange\",\"version\":\"KqlParameterItem/1.0\",\"name\":\"TimeRange\",\"type\":4,\"isRequired\":true,\"value\":{\"durationMs\":86400000},\"typeSettings\":{\"selectableValues\":[{\"durationMs\":3600000},{\"durationMs\":14400000},{\"durationMs\":43200000},{\"durationMs\":86400000},{\"durationMs\":172800000},{\"durationMs\":604800000},{\"durationMs\":2592000000}]}}],\"style\":\"pills\",\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\"},\"name\":\"parameters\"},{\"type\":11,\"content\":{\"version\":\"LinkItem/1.0\",\"style\":\"tabs\",\"links\":[{\"id\":\"overview\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Overview\",\"subTarget\":\"overview\",\"style\":\"link\"},{\"id\":\"config\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Identity & Configuration\",\"subTarget\":\"config\",\"style\":\"link\"},{\"id\":\"network\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Network Flows (Premium)\",\"subTarget\":\"network\",\"style\":\"link\"},{\"id\":\"signals\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Security Signals\",\"subTarget\":\"signals\",\"style\":\"link\"}]},\"name\":\"tabs\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":1,\"content\":{\"json\":\"## Overview\"},\"name\":\"overview-header\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Configuration_CL\\n| where TimeGenerated {TimeRange}\\n| summarize TotalEvents = count(), UniqueActors = dcount(tostring(Actor.loginName)), ACLChanges = countif(Action == \\\"WRITE\\\" and tostring(Target.type) == \\\"ACL\\\"), AuthKeysCreated = countif(Action == \\\"CREATE\\\" and tostring(Target.type) == \\\"AUTH_KEY\\\")\",\"size\":4,\"title\":\"Configuration audit summary\",\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"tiles\",\"tileSettings\":{\"titleContent\":{\"columnMatch\":\"TotalEvents\",\"formatter\":12},\"showBorder\":true}},\"customWidth\":\"100\",\"name\":\"overview-tiles\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Configuration_CL\\n| where TimeGenerated {TimeRange}\\n| summarize EventCount = count() by bin(TimeGenerated, 1h), Action\\n| render timechart\",\"size\":0,\"title\":\"Configuration activity over time, by action\",\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\"},\"customWidth\":\"100\",\"name\":\"overview-timeseries\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Configuration_CL\\n| where TimeGenerated {TimeRange}\\n| extend ActorLogin = tostring(Actor.loginName)\\n| where isnotempty(ActorLogin)\\n| summarize EventCount = count() by ActorLogin\\n| top 10 by EventCount\\n| render barchart\",\"size\":0,\"title\":\"Top 10 actors\",\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\"},\"customWidth\":\"50\",\"name\":\"overview-top-actors\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Configuration_CL\\n| where TimeGenerated {TimeRange}\\n| summarize EventCount = count() by tostring(Target.type)\\n| top 10 by EventCount\\n| render piechart\",\"size\":0,\"title\":\"Target type distribution\",\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\"},\"customWidth\":\"50\",\"name\":\"overview-target-types\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"overview\"},\"name\":\"overview-group\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":1,\"content\":{\"json\":\"## Identity & Configuration\"},\"name\":\"config-header\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Configuration_CL\\n| where TimeGenerated {TimeRange}\\n| extend ActorLogin = tostring(Actor.loginName), TargetType = tostring(Target.type)\\n| summarize EventCount = count() by ActorLogin, Action, TargetType\\n| order by EventCount desc\",\"size\":0,\"title\":\"Actor / Action / Target heatmap\",\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\"},\"customWidth\":\"100\",\"name\":\"config-heatmap\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Configuration_CL\\n| where TimeGenerated {TimeRange}\\n| where tostring(Target.type) == \\\"ACL\\\"\\n| extend ActorLogin = tostring(Actor.loginName)\\n| project TimeGenerated, ActorLogin, Action, OldPolicy = Old, NewPolicy = New\\n| order by TimeGenerated desc\",\"size\":0,\"title\":\"ACL policy change history\",\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\"},\"customWidth\":\"100\",\"name\":\"config-acl-history\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Configuration_CL\\n| where TimeGenerated {TimeRange}\\n| where tostring(Target.type) == \\\"AUTH_KEY\\\"\\n| extend ActorLogin = tostring(Actor.loginName), Reusable = tostring(New.reusable), Ephemeral = tostring(New.ephemeral)\\n| project TimeGenerated, Action, ActorLogin, KeyId = tostring(Target.id), Reusable, Ephemeral, Description = tostring(New.description)\\n| order by TimeGenerated desc\",\"size\":0,\"title\":\"Auth key lifecycle\",\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\"},\"customWidth\":\"100\",\"name\":\"config-authkey-lifecycle\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Configuration_CL\\n| where TimeGenerated {TimeRange}\\n| where tostring(Target.type) in (\\\"API_KEY\\\", \\\"OAUTH_CLIENT\\\")\\n| extend ActorLogin = tostring(Actor.loginName)\\n| project TimeGenerated, Action, ActorLogin, Type = tostring(Target.type), TargetId = tostring(Target.id), Description = tostring(New.description)\\n| order by TimeGenerated desc\",\"size\":0,\"title\":\"API token and OAuth client activity\",\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\"},\"customWidth\":\"100\",\"name\":\"config-oauth-api\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"config\"},\"name\":\"config-group\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":1,\"content\":{\"json\":\"## Network Flows (Premium)\\n\\nThese visualisations require the `Tailscale Premium (CCF)` data connector. If no data is shown, install the Premium connector or confirm your tailnet is on the Premium or Enterprise tier.\"},\"name\":\"network-header\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Network_CL\\n| where TimeGenerated {TimeRange}\\n| summarize Messages = count(), UniqueNodes = dcount(NodeId)\",\"size\":4,\"title\":\"Network flow summary\",\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"tiles\"},\"customWidth\":\"100\",\"name\":\"network-tiles\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Network_CL\\n| where TimeGenerated {TimeRange}\\n| mv-expand t = VirtualTraffic\\n| extend Src = tostring(t.src), Dst = tostring(t.dst), TxBytes = tolong(t.txBytes), RxBytes = tolong(t.rxBytes)\\n| summarize TotalBytes = sum(TxBytes + RxBytes) by Src, Dst\\n| top 20 by TotalBytes\\n| render barchart\",\"size\":0,\"title\":\"Top 20 talkers (virtual traffic, bytes)\",\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\"},\"customWidth\":\"100\",\"name\":\"network-top-talkers\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Network_CL\\n| where TimeGenerated {TimeRange}\\n| where array_length(ExitTraffic) > 0\\n| mv-expand t = ExitTraffic\\n| extend ExitDst = tostring(t.dst), Bytes = tolong(t.txBytes) + tolong(t.rxBytes)\\n| summarize TotalBytes = sum(Bytes), Flows = count() by SrcNodeName = tostring(SrcNode.name), ExitDst\\n| top 50 by TotalBytes\",\"size\":0,\"title\":\"Exit-node traffic\",\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\"},\"customWidth\":\"50\",\"name\":\"network-exit-traffic\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Network_CL\\n| where TimeGenerated {TimeRange}\\n| where array_length(SubnetTraffic) > 0\\n| mv-expand t = SubnetTraffic\\n| extend Bytes = tolong(t.txBytes) + tolong(t.rxBytes)\\n| summarize TotalBytes = sum(Bytes), Flows = count() by SrcNodeName = tostring(SrcNode.name)\\n| top 25 by TotalBytes\",\"size\":0,\"title\":\"Subnet router throughput by source node\",\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\"},\"customWidth\":\"50\",\"name\":\"network-subnet-traffic\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Network_CL\\n| where TimeGenerated {TimeRange}\\n| mv-expand t = VirtualTraffic\\n| extend Bytes = tolong(t.txBytes) + tolong(t.rxBytes)\\n| summarize TotalBytes = sum(Bytes) by bin(TimeGenerated, 1h)\\n| render timechart\",\"size\":0,\"title\":\"Virtual-traffic bytes over time\",\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\"},\"customWidth\":\"100\",\"name\":\"network-bytes-timechart\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"network\"},\"name\":\"network-group\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":1,\"content\":{\"json\":\"## Security Signals\\n\\nIncidents and alerts raised by Tailscale-related analytic rules.\"},\"name\":\"signals-header\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"SecurityAlert\\n| where TimeGenerated {TimeRange}\\n| where AlertName startswith \\\"Tailscale\\\"\\n| summarize Count = count() by AlertName, AlertSeverity\\n| order by Count desc\",\"size\":0,\"title\":\"Tailscale alerts by rule\",\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\"},\"customWidth\":\"50\",\"name\":\"signals-by-rule\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"SecurityAlert\\n| where TimeGenerated {TimeRange}\\n| where AlertName startswith \\\"Tailscale\\\"\\n| summarize Count = count() by AlertSeverity\\n| render piechart\",\"size\":0,\"title\":\"Severity distribution\",\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\"},\"customWidth\":\"50\",\"name\":\"signals-severity\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"SecurityAlert\\n| where TimeGenerated {TimeRange}\\n| where AlertName startswith \\\"Tailscale\\\"\\n| project TimeGenerated, AlertName, AlertSeverity, Description, Entities\\n| order by TimeGenerated desc\",\"size\":0,\"title\":\"Recent Tailscale alerts\",\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\"},\"customWidth\":\"100\",\"name\":\"signals-recent\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"signals\"},\"name\":\"signals-group\"}],\"fromTemplateId\":\"sentinel-TailscalePremiumOperations\",\"$schema\":\"https://github.com/Microsoft/Application-Insights-Workbooks/blob/master/schema/workbook.json\"}\n", + "version": "1.0", + "sourceId": "[variables('workspaceResourceId')]", + "category": "sentinel" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('Workbook-', last(split(variables('workbookId2'),'/'))))]", + "properties": { + "description": "@{workbookKey=TailscalePremiumOperationsWorkbook; logoFileName=Tailscale.svg; description=Tailscale Operations workbook for Premium / Enterprise tier — configuration audit plus network flow telemetry (top talkers, exit-node usage, subnet router throughput).; dataTypesDependencies=System.Object[]; dataConnectorsDependencies=System.Object[]; previewImagesFileNames=System.Object[]; version=1.0.0; title=Tailscale Operations (Premium); templateRelativePath=TailscalePremiumOperations.json; subtitle=; provider=noodlemctwoodle}.description", + "parentId": "[variables('workbookId2')]", + "contentId": "[variables('_workbookContentId2')]", + "kind": "Workbook", + "version": "[variables('workbookVersion2')]", + "source": { + "kind": "Solution", + "name": "Tailscale (CCF)", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "noodlemctwoodle" + }, + "support": { + "name": "Community", + "tier": "Community", + "link": "https://github.com/Azure/Azure-Sentinel/issues" + }, + "dependencies": { + "operator": "AND", + "criteria": [ + { + "contentId": "Tailscale_Configuration_CL", + "kind": "DataType" + }, + { + "contentId": "Tailscale_Network_CL", + "kind": "DataType" + }, + { + "contentId": "TailscalePremiumCCF", + "kind": "DataConnector" + } + ] + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('_workbookContentId2')]", + "contentKind": "Workbook", + "displayName": "[parameters('workbook2-name')]", + "contentProductId": "[variables('_workbookcontentProductId2')]", + "id": "[variables('_workbookcontentProductId2')]", + "version": "[variables('workbookVersion2')]" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentPackages", + "apiVersion": "2023-04-01-preview", + "location": "[parameters('workspace-location')]", + "properties": { + "version": "3.0.1", + "kind": "Solution", + "contentSchemaVersion": "3.0.0", + "displayName": "Tailscale (CCF)", + "publisherDisplayName": "Community", + "descriptionHtml": "

Note: Please refer to the following before installing the solution:

\n

• Review the solution Release Notes

\n

• There may be known issues pertaining to this Solution, please refer to them before installing.

\n

The Tailscale solution for Microsoft Sentinel ingests Tailscale telemetry via OAuth2-secured APIs.

\n

Data connectors in this solution (install one based on your Tailscale plan):

\n
    \n
  • Tailscale Standard (CCF) — Configuration audit logs (/logging/configuration). Use on Personal (Free) and Standard tailnets. Included in this release.
  • \n
  • Tailscale Premium (CCF) — Configuration audit + network flow logs (/logging/configuration + /logging/network). Use on Premium and Enterprise tailnets. Planned for a future release.
  • \n
\n

Underlying technology: Microsoft Sentinel Codeless Connector Framework (CCF) with OAuth2 client-credentials auth.

\n

Pre-requisites:

\n
    \n
  1. Sign in to Tailscale OAuth settings
  2. \n
  3. Create an OAuth client with the Audit Logs - Read scope (Standard connector). For the Premium connector also tick Network Logs - Read.
  4. \n
  5. Copy the client ID and client secret (secret shown once)
  6. \n
  7. Note your tailnet name from the Keys page
  8. \n
\n

Data Connectors: 2, Workbooks: 2, Analytic Rules: 5, Hunting Queries: 8

\n

Learn more about Microsoft Sentinel | Learn more about Solutions

\n", + "contentKind": "Solution", + "contentProductId": "[variables('_solutioncontentProductId')]", + "id": "[variables('_solutioncontentProductId')]", + "icon": "", + "contentId": "[variables('_solutionId')]", + "parentId": "[variables('_solutionId')]", + "source": { + "kind": "Solution", + "name": "Tailscale (CCF)", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "noodlemctwoodle" + }, + "support": { + "name": "Community", + "tier": "Community", + "link": "https://github.com/Azure/Azure-Sentinel/issues" + }, + "dependencies": { + "operator": "AND", + "criteria": [ + { + "kind": "DataConnector", + "contentId": "[variables('_dataConnectorContentIdConnections1')]", + "version": "[variables('dataConnectorCCPVersion')]" + }, + { + "kind": "DataConnector", + "contentId": "[variables('_dataConnectorContentIdConnections2')]", + "version": "[variables('dataConnectorCCPVersion')]" + }, + { + "kind": "AnalyticsRule", + "contentId": "[variables('analyticRuleObject1')._analyticRulecontentId1]", + "version": "[variables('analyticRuleObject1').analyticRuleVersion1]" + }, + { + "kind": "AnalyticsRule", + "contentId": "[variables('analyticRuleObject2')._analyticRulecontentId2]", + "version": "[variables('analyticRuleObject2').analyticRuleVersion2]" + }, + { + "kind": "AnalyticsRule", + "contentId": "[variables('analyticRuleObject3')._analyticRulecontentId3]", + "version": "[variables('analyticRuleObject3').analyticRuleVersion3]" + }, + { + "kind": "AnalyticsRule", + "contentId": "[variables('analyticRuleObject4')._analyticRulecontentId4]", + "version": "[variables('analyticRuleObject4').analyticRuleVersion4]" + }, + { + "kind": "AnalyticsRule", + "contentId": "[variables('analyticRuleObject5')._analyticRulecontentId5]", + "version": "[variables('analyticRuleObject5').analyticRuleVersion5]" + }, + { + "kind": "HuntingQuery", + "contentId": "[variables('huntingQueryObject1')._huntingQuerycontentId1]", + "version": "[variables('huntingQueryObject1').huntingQueryVersion1]" + }, + { + "kind": "HuntingQuery", + "contentId": "[variables('huntingQueryObject2')._huntingQuerycontentId2]", + "version": "[variables('huntingQueryObject2').huntingQueryVersion2]" + }, + { + "kind": "HuntingQuery", + "contentId": "[variables('huntingQueryObject3')._huntingQuerycontentId3]", + "version": "[variables('huntingQueryObject3').huntingQueryVersion3]" + }, + { + "kind": "HuntingQuery", + "contentId": "[variables('huntingQueryObject4')._huntingQuerycontentId4]", + "version": "[variables('huntingQueryObject4').huntingQueryVersion4]" + }, + { + "kind": "HuntingQuery", + "contentId": "[variables('huntingQueryObject5')._huntingQuerycontentId5]", + "version": "[variables('huntingQueryObject5').huntingQueryVersion5]" + }, + { + "kind": "HuntingQuery", + "contentId": "[variables('huntingQueryObject6')._huntingQuerycontentId6]", + "version": "[variables('huntingQueryObject6').huntingQueryVersion6]" + }, + { + "kind": "HuntingQuery", + "contentId": "[variables('huntingQueryObject7')._huntingQuerycontentId7]", + "version": "[variables('huntingQueryObject7').huntingQueryVersion7]" + }, + { + "kind": "HuntingQuery", + "contentId": "[variables('huntingQueryObject8')._huntingQuerycontentId8]", + "version": "[variables('huntingQueryObject8').huntingQueryVersion8]" + }, + { + "kind": "Workbook", + "contentId": "[variables('_workbookContentId1')]", + "version": "[variables('workbookVersion1')]" + }, + { + "kind": "Workbook", + "contentId": "[variables('_workbookContentId2')]", + "version": "[variables('workbookVersion2')]" } ] }, diff --git a/Solutions/Tailscale (CCF)/Package/testParameters.json b/Solutions/Tailscale (CCF)/Package/testParameters.json index 554801e41b7..8e1e27a3c33 100644 --- a/Solutions/Tailscale (CCF)/Package/testParameters.json +++ b/Solutions/Tailscale (CCF)/Package/testParameters.json @@ -34,5 +34,21 @@ "metadata": { "description": "subscription id where Microsoft Sentinel is setup" } + }, + "workbook1-name": { + "type": "string", + "defaultValue": "Tailscale Operations (Standard)", + "minLength": 1, + "metadata": { + "description": "Name for the workbook" + } + }, + "workbook2-name": { + "type": "string", + "defaultValue": "Tailscale Operations (Premium)", + "minLength": 1, + "metadata": { + "description": "Name for the workbook" + } } } diff --git a/Solutions/Tailscale (CCF)/Workbooks/TailscalePremiumOperations.json b/Solutions/Tailscale (CCF)/Workbooks/TailscalePremiumOperations.json new file mode 100644 index 00000000000..4edfc21850c --- /dev/null +++ b/Solutions/Tailscale (CCF)/Workbooks/TailscalePremiumOperations.json @@ -0,0 +1,410 @@ +{ + "version": "Notebook/1.0", + "items": [ + { + "type": 1, + "content": { + "json": "# Tailscale Operations (Premium)\n\nFull security and operational view across the **Tailscale Premium (CCF)** data connector \u2014 covers configuration audit (`Tailscale_Configuration_CL`) **and** network flow (`Tailscale_Network_CL`) telemetry.\n\nFor Personal (Free) / Standard tier tailnets, use the **Tailscale Operations (Standard)** workbook instead." + }, + "name": "header" + }, + { + "type": 9, + "content": { + "version": "KqlParameterItem/1.0", + "parameters": [ + { + "id": "timerange", + "version": "KqlParameterItem/1.0", + "name": "TimeRange", + "type": 4, + "isRequired": true, + "value": { + "durationMs": 86400000 + }, + "typeSettings": { + "selectableValues": [ + { + "durationMs": 3600000 + }, + { + "durationMs": 14400000 + }, + { + "durationMs": 43200000 + }, + { + "durationMs": 86400000 + }, + { + "durationMs": 172800000 + }, + { + "durationMs": 604800000 + }, + { + "durationMs": 2592000000 + } + ] + } + } + ], + "style": "pills", + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces" + }, + "name": "parameters" + }, + { + "type": 11, + "content": { + "version": "LinkItem/1.0", + "style": "tabs", + "links": [ + { + "id": "overview", + "cellValue": "selectedTab", + "linkTarget": "parameter", + "linkLabel": "Overview", + "subTarget": "overview", + "style": "link" + }, + { + "id": "config", + "cellValue": "selectedTab", + "linkTarget": "parameter", + "linkLabel": "Identity & Configuration", + "subTarget": "config", + "style": "link" + }, + { + "id": "network", + "cellValue": "selectedTab", + "linkTarget": "parameter", + "linkLabel": "Network Flows (Premium)", + "subTarget": "network", + "style": "link" + }, + { + "id": "signals", + "cellValue": "selectedTab", + "linkTarget": "parameter", + "linkLabel": "Security Signals", + "subTarget": "signals", + "style": "link" + } + ] + }, + "name": "tabs" + }, + { + "type": 12, + "content": { + "version": "NotebookGroup/1.0", + "groupType": "editable", + "items": [ + { + "type": 1, + "content": { + "json": "## Overview" + }, + "name": "overview-header" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_Configuration_CL\n| where TimeGenerated {TimeRange}\n| summarize TotalEvents = count(), UniqueActors = dcount(tostring(Actor.loginName)), ACLChanges = countif(Action == \"WRITE\" and tostring(Target.type) == \"ACL\"), AuthKeysCreated = countif(Action == \"CREATE\" and tostring(Target.type) == \"AUTH_KEY\")", + "size": 4, + "title": "Configuration audit summary", + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "tiles", + "tileSettings": { + "titleContent": { + "columnMatch": "TotalEvents", + "formatter": 12 + }, + "showBorder": true + } + }, + "customWidth": "100", + "name": "overview-tiles" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_Configuration_CL\n| where TimeGenerated {TimeRange}\n| summarize EventCount = count() by bin(TimeGenerated, 1h), Action\n| render timechart", + "size": 0, + "title": "Configuration activity over time, by action", + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces" + }, + "customWidth": "100", + "name": "overview-timeseries" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_Configuration_CL\n| where TimeGenerated {TimeRange}\n| extend ActorLogin = tostring(Actor.loginName)\n| where isnotempty(ActorLogin)\n| summarize EventCount = count() by ActorLogin\n| top 10 by EventCount\n| render barchart", + "size": 0, + "title": "Top 10 actors", + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces" + }, + "customWidth": "50", + "name": "overview-top-actors" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_Configuration_CL\n| where TimeGenerated {TimeRange}\n| summarize EventCount = count() by tostring(Target.type)\n| top 10 by EventCount\n| render piechart", + "size": 0, + "title": "Target type distribution", + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces" + }, + "customWidth": "50", + "name": "overview-target-types" + } + ] + }, + "conditionalVisibility": { + "parameterName": "selectedTab", + "comparison": "isEqualTo", + "value": "overview" + }, + "name": "overview-group" + }, + { + "type": 12, + "content": { + "version": "NotebookGroup/1.0", + "groupType": "editable", + "items": [ + { + "type": 1, + "content": { + "json": "## Identity & Configuration" + }, + "name": "config-header" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_Configuration_CL\n| where TimeGenerated {TimeRange}\n| extend ActorLogin = tostring(Actor.loginName), TargetType = tostring(Target.type)\n| summarize EventCount = count() by ActorLogin, Action, TargetType\n| order by EventCount desc", + "size": 0, + "title": "Actor / Action / Target heatmap", + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "table" + }, + "customWidth": "100", + "name": "config-heatmap" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_Configuration_CL\n| where TimeGenerated {TimeRange}\n| where tostring(Target.type) == \"ACL\"\n| extend ActorLogin = tostring(Actor.loginName)\n| project TimeGenerated, ActorLogin, Action, OldPolicy = Old, NewPolicy = New\n| order by TimeGenerated desc", + "size": 0, + "title": "ACL policy change history", + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces" + }, + "customWidth": "100", + "name": "config-acl-history" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_Configuration_CL\n| where TimeGenerated {TimeRange}\n| where tostring(Target.type) == \"AUTH_KEY\"\n| extend ActorLogin = tostring(Actor.loginName), Reusable = tostring(New.reusable), Ephemeral = tostring(New.ephemeral)\n| project TimeGenerated, Action, ActorLogin, KeyId = tostring(Target.id), Reusable, Ephemeral, Description = tostring(New.description)\n| order by TimeGenerated desc", + "size": 0, + "title": "Auth key lifecycle", + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces" + }, + "customWidth": "100", + "name": "config-authkey-lifecycle" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_Configuration_CL\n| where TimeGenerated {TimeRange}\n| where tostring(Target.type) in (\"API_KEY\", \"OAUTH_CLIENT\")\n| extend ActorLogin = tostring(Actor.loginName)\n| project TimeGenerated, Action, ActorLogin, Type = tostring(Target.type), TargetId = tostring(Target.id), Description = tostring(New.description)\n| order by TimeGenerated desc", + "size": 0, + "title": "API token and OAuth client activity", + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces" + }, + "customWidth": "100", + "name": "config-oauth-api" + } + ] + }, + "conditionalVisibility": { + "parameterName": "selectedTab", + "comparison": "isEqualTo", + "value": "config" + }, + "name": "config-group" + }, + { + "type": 12, + "content": { + "version": "NotebookGroup/1.0", + "groupType": "editable", + "items": [ + { + "type": 1, + "content": { + "json": "## Network Flows (Premium)\n\nThese visualisations require the `Tailscale Premium (CCF)` data connector. If no data is shown, install the Premium connector or confirm your tailnet is on the Premium or Enterprise tier." + }, + "name": "network-header" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_Network_CL\n| where TimeGenerated {TimeRange}\n| summarize Messages = count(), UniqueNodes = dcount(NodeId)", + "size": 4, + "title": "Network flow summary", + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "tiles" + }, + "customWidth": "100", + "name": "network-tiles" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_Network_CL\n| where TimeGenerated {TimeRange}\n| mv-expand t = VirtualTraffic\n| extend Src = tostring(t.src), Dst = tostring(t.dst), TxBytes = tolong(t.txBytes), RxBytes = tolong(t.rxBytes)\n| summarize TotalBytes = sum(TxBytes + RxBytes) by Src, Dst\n| top 20 by TotalBytes\n| render barchart", + "size": 0, + "title": "Top 20 talkers (virtual traffic, bytes)", + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces" + }, + "customWidth": "100", + "name": "network-top-talkers" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_Network_CL\n| where TimeGenerated {TimeRange}\n| where array_length(ExitTraffic) > 0\n| mv-expand t = ExitTraffic\n| extend ExitDst = tostring(t.dst), Bytes = tolong(t.txBytes) + tolong(t.rxBytes)\n| summarize TotalBytes = sum(Bytes), Flows = count() by SrcNodeName = tostring(SrcNode.name), ExitDst\n| top 50 by TotalBytes", + "size": 0, + "title": "Exit-node traffic", + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces" + }, + "customWidth": "50", + "name": "network-exit-traffic" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_Network_CL\n| where TimeGenerated {TimeRange}\n| where array_length(SubnetTraffic) > 0\n| mv-expand t = SubnetTraffic\n| extend Bytes = tolong(t.txBytes) + tolong(t.rxBytes)\n| summarize TotalBytes = sum(Bytes), Flows = count() by SrcNodeName = tostring(SrcNode.name)\n| top 25 by TotalBytes", + "size": 0, + "title": "Subnet router throughput by source node", + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces" + }, + "customWidth": "50", + "name": "network-subnet-traffic" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_Network_CL\n| where TimeGenerated {TimeRange}\n| mv-expand t = VirtualTraffic\n| extend Bytes = tolong(t.txBytes) + tolong(t.rxBytes)\n| summarize TotalBytes = sum(Bytes) by bin(TimeGenerated, 1h)\n| render timechart", + "size": 0, + "title": "Virtual-traffic bytes over time", + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces" + }, + "customWidth": "100", + "name": "network-bytes-timechart" + } + ] + }, + "conditionalVisibility": { + "parameterName": "selectedTab", + "comparison": "isEqualTo", + "value": "network" + }, + "name": "network-group" + }, + { + "type": 12, + "content": { + "version": "NotebookGroup/1.0", + "groupType": "editable", + "items": [ + { + "type": 1, + "content": { + "json": "## Security Signals\n\nIncidents and alerts raised by Tailscale-related analytic rules." + }, + "name": "signals-header" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "SecurityAlert\n| where TimeGenerated {TimeRange}\n| where AlertName startswith \"Tailscale\"\n| summarize Count = count() by AlertName, AlertSeverity\n| order by Count desc", + "size": 0, + "title": "Tailscale alerts by rule", + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces" + }, + "customWidth": "50", + "name": "signals-by-rule" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "SecurityAlert\n| where TimeGenerated {TimeRange}\n| where AlertName startswith \"Tailscale\"\n| summarize Count = count() by AlertSeverity\n| render piechart", + "size": 0, + "title": "Severity distribution", + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces" + }, + "customWidth": "50", + "name": "signals-severity" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "SecurityAlert\n| where TimeGenerated {TimeRange}\n| where AlertName startswith \"Tailscale\"\n| project TimeGenerated, AlertName, AlertSeverity, Description, Entities\n| order by TimeGenerated desc", + "size": 0, + "title": "Recent Tailscale alerts", + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces" + }, + "customWidth": "100", + "name": "signals-recent" + } + ] + }, + "conditionalVisibility": { + "parameterName": "selectedTab", + "comparison": "isEqualTo", + "value": "signals" + }, + "name": "signals-group" + } + ], + "fallbackResourceIds": [], + "fromTemplateId": "sentinel-TailscalePremiumOperations", + "$schema": "https://github.com/Microsoft/Application-Insights-Workbooks/blob/master/schema/workbook.json" +} \ No newline at end of file diff --git a/Solutions/Tailscale (CCF)/Workbooks/TailscaleStandardOperations.json b/Solutions/Tailscale (CCF)/Workbooks/TailscaleStandardOperations.json new file mode 100644 index 00000000000..519a9ba2cf8 --- /dev/null +++ b/Solutions/Tailscale (CCF)/Workbooks/TailscaleStandardOperations.json @@ -0,0 +1,314 @@ +{ + "version": "Notebook/1.0", + "items": [ + { + "type": 1, + "content": { + "json": "# Tailscale Operations (Standard)\n\nSecurity and operational view across the **Tailscale Standard (CCF)** data connector. Surfaces configuration audit data (`Tailscale_Configuration_CL`).\n\nFor network flow visibility, upgrade your tailnet to Premium / Enterprise and install **Tailscale Premium (CCF)** + use the **Tailscale Operations (Premium)** workbook." + }, + "name": "header" + }, + { + "type": 9, + "content": { + "version": "KqlParameterItem/1.0", + "parameters": [ + { + "id": "timerange", + "version": "KqlParameterItem/1.0", + "name": "TimeRange", + "type": 4, + "isRequired": true, + "value": { + "durationMs": 86400000 + }, + "typeSettings": { + "selectableValues": [ + { + "durationMs": 3600000 + }, + { + "durationMs": 14400000 + }, + { + "durationMs": 43200000 + }, + { + "durationMs": 86400000 + }, + { + "durationMs": 172800000 + }, + { + "durationMs": 604800000 + }, + { + "durationMs": 2592000000 + } + ] + } + } + ], + "style": "pills", + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces" + }, + "name": "parameters" + }, + { + "type": 11, + "content": { + "version": "LinkItem/1.0", + "style": "tabs", + "links": [ + { + "id": "overview", + "cellValue": "selectedTab", + "linkTarget": "parameter", + "linkLabel": "Overview", + "subTarget": "overview", + "style": "link" + }, + { + "id": "config", + "cellValue": "selectedTab", + "linkTarget": "parameter", + "linkLabel": "Identity & Configuration", + "subTarget": "config", + "style": "link" + }, + { + "id": "signals", + "cellValue": "selectedTab", + "linkTarget": "parameter", + "linkLabel": "Security Signals", + "subTarget": "signals", + "style": "link" + } + ] + }, + "name": "tabs" + }, + { + "type": 12, + "content": { + "version": "NotebookGroup/1.0", + "groupType": "editable", + "items": [ + { + "type": 1, + "content": { + "json": "## Overview" + }, + "name": "overview-header" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_Configuration_CL\n| where TimeGenerated {TimeRange}\n| summarize TotalEvents = count(), UniqueActors = dcount(tostring(Actor.loginName)), ACLChanges = countif(Action == \"WRITE\" and tostring(Target.type) == \"ACL\"), AuthKeysCreated = countif(Action == \"CREATE\" and tostring(Target.type) == \"AUTH_KEY\")", + "size": 4, + "title": "Configuration audit summary", + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "tiles", + "tileSettings": { + "titleContent": { + "columnMatch": "TotalEvents", + "formatter": 12 + }, + "showBorder": true + } + }, + "customWidth": "100", + "name": "overview-tiles" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_Configuration_CL\n| where TimeGenerated {TimeRange}\n| summarize EventCount = count() by bin(TimeGenerated, 1h), Action\n| render timechart", + "size": 0, + "title": "Configuration activity over time, by action", + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces" + }, + "customWidth": "100", + "name": "overview-timeseries" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_Configuration_CL\n| where TimeGenerated {TimeRange}\n| extend ActorLogin = tostring(Actor.loginName)\n| where isnotempty(ActorLogin)\n| summarize EventCount = count() by ActorLogin\n| top 10 by EventCount\n| render barchart", + "size": 0, + "title": "Top 10 actors", + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces" + }, + "customWidth": "50", + "name": "overview-top-actors" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_Configuration_CL\n| where TimeGenerated {TimeRange}\n| summarize EventCount = count() by tostring(Target.type)\n| top 10 by EventCount\n| render piechart", + "size": 0, + "title": "Target type distribution", + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces" + }, + "customWidth": "50", + "name": "overview-target-types" + } + ] + }, + "conditionalVisibility": { + "parameterName": "selectedTab", + "comparison": "isEqualTo", + "value": "overview" + }, + "name": "overview-group" + }, + { + "type": 12, + "content": { + "version": "NotebookGroup/1.0", + "groupType": "editable", + "items": [ + { + "type": 1, + "content": { + "json": "## Identity & Configuration" + }, + "name": "config-header" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_Configuration_CL\n| where TimeGenerated {TimeRange}\n| extend ActorLogin = tostring(Actor.loginName), TargetType = tostring(Target.type)\n| summarize EventCount = count() by ActorLogin, Action, TargetType\n| order by EventCount desc", + "size": 0, + "title": "Actor / Action / Target heatmap", + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "table" + }, + "customWidth": "100", + "name": "config-heatmap" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_Configuration_CL\n| where TimeGenerated {TimeRange}\n| where tostring(Target.type) == \"ACL\"\n| extend ActorLogin = tostring(Actor.loginName)\n| project TimeGenerated, ActorLogin, Action, OldPolicy = Old, NewPolicy = New\n| order by TimeGenerated desc", + "size": 0, + "title": "ACL policy change history", + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces" + }, + "customWidth": "100", + "name": "config-acl-history" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_Configuration_CL\n| where TimeGenerated {TimeRange}\n| where tostring(Target.type) == \"AUTH_KEY\"\n| extend ActorLogin = tostring(Actor.loginName), Reusable = tostring(New.reusable), Ephemeral = tostring(New.ephemeral)\n| project TimeGenerated, Action, ActorLogin, KeyId = tostring(Target.id), Reusable, Ephemeral, Description = tostring(New.description)\n| order by TimeGenerated desc", + "size": 0, + "title": "Auth key lifecycle", + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces" + }, + "customWidth": "100", + "name": "config-authkey-lifecycle" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_Configuration_CL\n| where TimeGenerated {TimeRange}\n| where tostring(Target.type) in (\"API_KEY\", \"OAUTH_CLIENT\")\n| extend ActorLogin = tostring(Actor.loginName)\n| project TimeGenerated, Action, ActorLogin, Type = tostring(Target.type), TargetId = tostring(Target.id), Description = tostring(New.description)\n| order by TimeGenerated desc", + "size": 0, + "title": "API token and OAuth client activity", + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces" + }, + "customWidth": "100", + "name": "config-oauth-api" + } + ] + }, + "conditionalVisibility": { + "parameterName": "selectedTab", + "comparison": "isEqualTo", + "value": "config" + }, + "name": "config-group" + }, + { + "type": 12, + "content": { + "version": "NotebookGroup/1.0", + "groupType": "editable", + "items": [ + { + "type": 1, + "content": { + "json": "## Security Signals\n\nIncidents and alerts raised by Tailscale-related analytic rules." + }, + "name": "signals-header" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "SecurityAlert\n| where TimeGenerated {TimeRange}\n| where AlertName startswith \"Tailscale\"\n| summarize Count = count() by AlertName, AlertSeverity\n| order by Count desc", + "size": 0, + "title": "Tailscale alerts by rule", + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces" + }, + "customWidth": "50", + "name": "signals-by-rule" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "SecurityAlert\n| where TimeGenerated {TimeRange}\n| where AlertName startswith \"Tailscale\"\n| summarize Count = count() by AlertSeverity\n| render piechart", + "size": 0, + "title": "Severity distribution", + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces" + }, + "customWidth": "50", + "name": "signals-severity" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "SecurityAlert\n| where TimeGenerated {TimeRange}\n| where AlertName startswith \"Tailscale\"\n| project TimeGenerated, AlertName, AlertSeverity, Description, Entities\n| order by TimeGenerated desc", + "size": 0, + "title": "Recent Tailscale alerts", + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces" + }, + "customWidth": "100", + "name": "signals-recent" + } + ] + }, + "conditionalVisibility": { + "parameterName": "selectedTab", + "comparison": "isEqualTo", + "value": "signals" + }, + "name": "signals-group" + } + ], + "fallbackResourceIds": [], + "fromTemplateId": "sentinel-TailscaleStandardOperations", + "$schema": "https://github.com/Microsoft/Application-Insights-Workbooks/blob/master/schema/workbook.json" +} \ No newline at end of file diff --git a/Workbooks/WorkbooksMetadata.json b/Workbooks/WorkbooksMetadata.json index 1db435f6159..50c09195a9f 100644 --- a/Workbooks/WorkbooksMetadata.json +++ b/Workbooks/WorkbooksMetadata.json @@ -10596,5 +10596,40 @@ "templateRelativePath": "NucleusCyber_NCProtect_Workbook.json", "subtitle": "", "provider": "archTIS" + }, + { + "workbookKey": "TailscaleStandardOperationsWorkbook", + "logoFileName": "Tailscale.svg", + "description": "Tailscale Operations workbook for Standard tier - configuration audit, identity activity, ACL change history, OAuth/API token lifecycle, and security signals.", + "dataTypesDependencies": [ + "Tailscale_Configuration_CL" + ], + "dataConnectorsDependencies": [ + "TailscaleCCF" + ], + "previewImagesFileNames": [], + "version": "1.0.0", + "title": "Tailscale Operations (Standard)", + "templateRelativePath": "TailscaleStandardOperations.json", + "subtitle": "", + "provider": "noodlemctwoodle" + }, + { + "workbookKey": "TailscalePremiumOperationsWorkbook", + "logoFileName": "Tailscale.svg", + "description": "Tailscale Operations workbook for Premium / Enterprise tier - configuration audit plus network flow telemetry (top talkers, exit-node usage, subnet router throughput).", + "dataTypesDependencies": [ + "Tailscale_Configuration_CL", + "Tailscale_Network_CL" + ], + "dataConnectorsDependencies": [ + "TailscalePremiumCCF" + ], + "previewImagesFileNames": [], + "version": "1.0.0", + "title": "Tailscale Operations (Premium)", + "templateRelativePath": "TailscalePremiumOperations.json", + "subtitle": "", + "provider": "noodlemctwoodle" } ] From a97e95fa684eee60ec1fa1c0fa7d2c08b22dfa09 Mon Sep 17 00:00:00 2001 From: noodlemctwoodle Date: Tue, 12 May 2026 08:33:14 +0100 Subject: [PATCH 03/27] Tailscale (CCF): pre-emptive CI fixes (mirrors UniFi PR fix-up) Applies the same five CI-failure classes that hit PR #14253 to the Tailscale branch up-front so PR #2 doesn't need a fix-up round: 1. ValidConnectorIds.json allowlist Appended TailscaleCCF + TailscalePremiumCCF, CRLF-preserving surgical edit so the diff is two new lines. 2. Duration short form Converted PT15M -> 15m, PT5H -> 5h, PT1H -> 1h on all 5 analytic rules. ScheduledTemplateTimeSpanConverter rejects ISO-8601. 3. CustomTables JSON schemas Added Tailscale_Configuration_CL.json + Tailscale_Network_CL.json to .script/tests/KqlvalidationsTests/CustomTables/. Required for KqlValidationTests to recognise the custom _CL table names in any detection / hunting / exploration query referencing them. 4. SVG sanitisation Tailscale.svg verified clean (no style= or \ No newline at end of file + diff --git a/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleAuthkeycreated.yaml b/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleAuthkeycreated.yaml index 164dd75e837..8c662aea001 100644 --- a/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleAuthkeycreated.yaml +++ b/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleAuthkeycreated.yaml @@ -1,7 +1,7 @@ id: 6b052c8d-5de8-eab0-1956-69a297765a32 name: "Tailscale: Auth key created" description: | - A new auth key was generated. Auth keys allow unattended device enrollment into the tailnet - confirm it was expected and revoke if not. + Identifies when a new Tailscale auth key is generated. Auth keys allow unattended device enrollment into the tailnet - confirm it was expected and revoke if not. severity: Low status: Available requiredDataConnectors: @@ -10,7 +10,7 @@ requiredDataConnectors: - Tailscale_Audit_CL queryFrequency: 15m queryPeriod: 15m -triggerOperator: GreaterThan +triggerOperator: gt triggerThreshold: 0 tactics: - Persistence diff --git a/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleDeviceAdvertisingSubnetRoutes.yaml b/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleDeviceAdvertisingSubnetRoutes.yaml index 38acd1b2068..0854c997cb2 100644 --- a/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleDeviceAdvertisingSubnetRoutes.yaml +++ b/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleDeviceAdvertisingSubnetRoutes.yaml @@ -1,7 +1,7 @@ id: c2b3d4e5-2345-6789-01ab-cdef12345002 name: "Tailscale: Device started advertising subnet routes" description: | - A tailnet device began advertising subnet routes (subnet-router capability) that it was not advertising in the previous snapshot. Subnet routers expose adjacent networks to the tailnet; unexpected advertisement can indicate a compromised node trying to expand reachable surface area or an unsanctioned admin change. + Identifies when a tailnet device begins advertising subnet routes (subnet-router capability) not present in the previous snapshot. Unexpected advertisement may indicate a compromised node expanding reachable surface area or an unsanctioned admin change. severity: Medium status: Available requiredDataConnectors: @@ -10,7 +10,7 @@ requiredDataConnectors: - Tailscale_Devices_CL queryFrequency: 1h queryPeriod: 1d -triggerOperator: GreaterThan +triggerOperator: gt triggerThreshold: 0 tactics: - LateralMovement diff --git a/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleDeviceKeyExpiringSoon.yaml b/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleDeviceKeyExpiringSoon.yaml index 0babe6a9790..e83535000b4 100644 --- a/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleDeviceKeyExpiringSoon.yaml +++ b/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleDeviceKeyExpiringSoon.yaml @@ -1,7 +1,7 @@ id: b1a2c3d4-1234-5678-90ab-cdef12345001 name: "Tailscale: Device key expiring within 7 days" description: | - A tailnet device's machine key expires within the next 7 days and key expiry is not disabled. Expired keys force device re-authentication; surface this proactively so it can be renewed in a planned window rather than during an outage. + Identifies tailnet devices whose machine key expires within the next 7 days and where key expiry is not disabled. Surface proactively so renewal can be scheduled rather than forced during an outage. severity: Medium status: Available requiredDataConnectors: @@ -10,7 +10,7 @@ requiredDataConnectors: - Tailscale_Devices_CL queryFrequency: 6h queryPeriod: 6h -triggerOperator: GreaterThan +triggerOperator: gt triggerThreshold: 0 tactics: - InitialAccess diff --git a/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleDeviceSshNewlyEnabled.yaml b/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleDeviceSshNewlyEnabled.yaml index b34cad6a1fa..85f3e59513f 100644 --- a/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleDeviceSshNewlyEnabled.yaml +++ b/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleDeviceSshNewlyEnabled.yaml @@ -1,7 +1,7 @@ id: f0a1b2c3-4567-8901-23de-f12345670041 name: "Tailscale: Device Tailscale SSH newly enabled" description: | - Tailscale SSH was enabled on a device that previously did not have it. Tailscale SSH provides authenticated shell access to the device over the tailnet, using the existing Tailscale identity rather than SSH keys. Legitimate during initial admin setup; an unexpected enable broadens the attack surface for any device that can reach this node. Verify the change was intentional and that the SSH ACL covers it appropriately. + Identifies when Tailscale SSH is enabled on a device that previously did not have it. SSH provides authenticated shell access over the tailnet using Tailscale identity, broadening attack surface if unexpected. Verify and confirm the SSH ACL covers it. severity: Medium status: Available requiredDataConnectors: @@ -10,7 +10,7 @@ requiredDataConnectors: - Tailscale_Devices_CL queryFrequency: 1h queryPeriod: 2d -triggerOperator: GreaterThan +triggerOperator: gt triggerThreshold: 0 tactics: - Persistence diff --git a/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleDnsNameserversModified.yaml b/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleDnsNameserversModified.yaml index 9ad0bc8b2f6..d2dcdf84705 100644 --- a/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleDnsNameserversModified.yaml +++ b/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleDnsNameserversModified.yaml @@ -1,7 +1,7 @@ id: c5d6e7f8-2345-6789-01ab-cdef12345011 name: "Tailscale: DNS nameservers modified" description: | - The tailnet's global DNS nameserver list was modified. Adding an attacker-controlled resolver as a tailnet-wide nameserver enables broad DNS hijacking for every device on the tailnet that uses MagicDNS resolution. Captured via the audit log which records every change with the full before/after document and actor attribution. + Identifies when the tailnet's global DNS nameserver list is modified. Adding an attacker-controlled resolver as a tailnet-wide nameserver enables broad DNS hijacking for every device using MagicDNS resolution. severity: High status: Available requiredDataConnectors: @@ -10,7 +10,7 @@ requiredDataConnectors: - Tailscale_Audit_CL queryFrequency: 15m queryPeriod: 15m -triggerOperator: GreaterThan +triggerOperator: gt triggerThreshold: 0 tactics: - DefenseEvasion diff --git a/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleExitnodeadvertisedorapproved.yaml b/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleExitnodeadvertisedorapproved.yaml index 190f09ad7c3..1c1bf64eceb 100644 --- a/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleExitnodeadvertisedorapproved.yaml +++ b/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleExitnodeadvertisedorapproved.yaml @@ -1,7 +1,7 @@ id: f42f2906-c8e6-23d0-e48c-0620e50d5510 name: "Tailscale: Exit node advertised or approved" description: | - A device started advertising itself as an exit node, or an admin approved one. Validate the device and operator - rogue exit nodes can intercept tailnet egress. + Identifies when a device starts advertising itself as an exit node, or when an admin approves one. Validate the device and operator - rogue exit nodes can intercept tailnet egress. severity: Low status: Available requiredDataConnectors: @@ -10,7 +10,7 @@ requiredDataConnectors: - Tailscale_Audit_CL queryFrequency: 30m queryPeriod: 30m -triggerOperator: GreaterThan +triggerOperator: gt triggerThreshold: 0 tactics: - CommandAndControl diff --git a/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleExternalDeviceAdded.yaml b/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleExternalDeviceAdded.yaml index 39a1e7ab8af..ab926f2c861 100644 --- a/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleExternalDeviceAdded.yaml +++ b/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleExternalDeviceAdded.yaml @@ -1,7 +1,7 @@ id: b2c3d4e5-6789-0123-4567-890123450043 name: "Tailscale: External (shared-in) device added" description: | - A device from a different tailnet has been shared into yours (IsExternal=true) and is not present in the prior 24-hour baseline. Tailnet sharing legitimately lets external collaborators access specific tagged resources, but every shared-in device expands the trust boundary - confirm the share matches a documented agreement and that the device is restricted to the intended ACL scope. + Identifies new external (shared-in) devices joining the tailnet that were not present in the prior 24-hour baseline. Each shared-in device expands the trust boundary - confirm the share matches a documented agreement and ACL scope. severity: Medium status: Available requiredDataConnectors: @@ -10,7 +10,7 @@ requiredDataConnectors: - Tailscale_Devices_CL queryFrequency: 1h queryPeriod: 2d -triggerOperator: GreaterThan +triggerOperator: gt triggerThreshold: 0 tactics: - InitialAccess diff --git a/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleMagicDnsDisabled.yaml b/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleMagicDnsDisabled.yaml index c8a497c18df..182e161b57f 100644 --- a/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleMagicDnsDisabled.yaml +++ b/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleMagicDnsDisabled.yaml @@ -1,7 +1,7 @@ id: f8a9b0c1-4567-8901-23ab-cdef12345020 name: "Tailscale: MagicDNS disabled" description: | - MagicDNS was turned off on the tailnet. MagicDNS provides automatic hostname resolution between tailnet devices; disabling it changes DNS behaviour for every device and is occasionally a precursor to a wider DNS hijacking attempt (e.g. attacker disables MagicDNS, then changes DNS nameservers to attacker-controlled resolvers). Disabling is a legitimate but unusual admin action - verify the change was intentional. + Identifies when MagicDNS is turned off on the tailnet. Disabling MagicDNS changes DNS behaviour for every device and is occasionally a precursor to wider DNS hijacking - verify the change was intentional. severity: Medium status: Available requiredDataConnectors: @@ -10,7 +10,7 @@ requiredDataConnectors: - Tailscale_Audit_CL queryFrequency: 15m queryPeriod: 15m -triggerOperator: GreaterThan +triggerOperator: gt triggerThreshold: 0 tactics: - DefenseEvasion diff --git a/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleMasscredentialrevocationinshortwindow.yaml b/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleMasscredentialrevocationinshortwindow.yaml index 6cbec67b06c..e82b106dba2 100644 --- a/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleMasscredentialrevocationinshortwindow.yaml +++ b/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleMasscredentialrevocationinshortwindow.yaml @@ -1,7 +1,7 @@ id: f817e2fa-6fa0-fc25-5369-cef9b58771af name: "Tailscale: Mass credential revocation in short window" description: | - Five or more API keys, OAuth clients, or auth keys were revoked/deleted within an hour. May be routine rotation, but also a typical cleanup pattern after credential compromise. + Identifies when five or more API keys, OAuth clients, or auth keys are revoked or deleted within one hour. May be routine rotation, or a typical cleanup pattern after credential compromise. severity: High status: Available requiredDataConnectors: @@ -10,7 +10,7 @@ requiredDataConnectors: - Tailscale_Audit_CL queryFrequency: 1h queryPeriod: 1h -triggerOperator: GreaterThan +triggerOperator: gt triggerThreshold: 0 tactics: - DefenseEvasion diff --git a/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleNewAPIaccesstokenorOAuthclientcreated.yaml b/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleNewAPIaccesstokenorOAuthclientcreated.yaml index 7db2be31550..8bf479f5443 100644 --- a/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleNewAPIaccesstokenorOAuthclientcreated.yaml +++ b/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleNewAPIaccesstokenorOAuthclientcreated.yaml @@ -1,7 +1,7 @@ id: 668b43fd-cf28-961a-85af-957850df5027 name: "Tailscale: New API access token or OAuth client created" description: | - A new API access token or OAuth client was created in the tailnet. These grant programmatic access - verify the actor and intent. + Identifies when a new API access token or OAuth client is created in the tailnet. These grant programmatic access - verify the actor and intent. severity: Medium status: Available requiredDataConnectors: @@ -10,7 +10,7 @@ requiredDataConnectors: - Tailscale_Audit_CL queryFrequency: 15m queryPeriod: 15m -triggerOperator: GreaterThan +triggerOperator: gt triggerThreshold: 0 tactics: - Persistence diff --git a/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePolicyfileACLmodified.yaml b/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePolicyfileACLmodified.yaml index 4a3f450b5f1..155d98440b7 100644 --- a/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePolicyfileACLmodified.yaml +++ b/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePolicyfileACLmodified.yaml @@ -1,7 +1,7 @@ id: 1e7249c2-1a9d-05fd-45cb-c859eef5b8ae name: "Tailscale: Policy file (ACL) modified" description: | - The tailnet ACL/policy file was modified. Review the diff - incorrect ACLs can silently expand blast radius across the tailnet. + Identifies when the tailnet ACL/policy file is modified. Review the diff - incorrect ACLs can silently expand blast radius across the tailnet. severity: Medium status: Available requiredDataConnectors: @@ -10,7 +10,7 @@ requiredDataConnectors: - Tailscale_Audit_CL queryFrequency: 15m queryPeriod: 15m -triggerOperator: GreaterThan +triggerOperator: gt triggerThreshold: 0 tactics: - DefenseEvasion diff --git a/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumBeaconingDetected.yaml b/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumBeaconingDetected.yaml index 0fa21b2798b..422ab6163cf 100644 --- a/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumBeaconingDetected.yaml +++ b/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumBeaconingDetected.yaml @@ -1,7 +1,7 @@ id: e3f4a5b6-3c4d-5e6f-7a8b-9c0d1e2f3a4b name: "Tailscale Premium: Network flow beaconing detected" description: | - Flows between a src->dst pair recur at a regular interval (80%+ of inter-flow gaps cluster on the same delta over 10+ flows). This is the signature of C2 beaconing or scheduled exfiltration. Investigate the source node, the destination, and timing. Requires Tailscale Premium or Enterprise (network flow logs). + Identifies when flows between a src-dst pair recur at a regular interval (80%+ of inter-flow gaps cluster on the same delta over 10+ flows). Signature of C2 beaconing or scheduled exfiltration. Requires Tailscale Premium or Enterprise. severity: Medium status: Available requiredDataConnectors: @@ -10,7 +10,7 @@ requiredDataConnectors: - Tailscale_Network_CL queryFrequency: 1h queryPeriod: 2d -triggerOperator: GreaterThan +triggerOperator: gt triggerThreshold: 0 tactics: - CommandAndControl diff --git a/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumLargeOutboundTransfer.yaml b/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumLargeOutboundTransfer.yaml index d6cf85e2ea2..cc9b9f60ba5 100644 --- a/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumLargeOutboundTransfer.yaml +++ b/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumLargeOutboundTransfer.yaml @@ -1,7 +1,7 @@ id: d2e3f4a5-2b3c-4d5e-6f7a-8b9c0d1e2f3a name: "Tailscale Premium: Large outbound transfer over tailnet" description: | - A single src->dst pair transferred more than 100 MB over the tailnet within a 1-hour window. Large bursts can indicate data staging, exfiltration, or a misconfigured backup. Tune the threshold to match your normal traffic profile. Requires Tailscale Premium or Enterprise (network flow logs). + Identifies when a single src-dst pair transfers more than 100 MB over the tailnet within a 1-hour window. Large bursts can indicate data staging, exfiltration, or a misconfigured backup. Requires Tailscale Premium or Enterprise. severity: Medium status: Available requiredDataConnectors: @@ -10,7 +10,7 @@ requiredDataConnectors: - Tailscale_Network_CL queryFrequency: 1h queryPeriod: 1h -triggerOperator: GreaterThan +triggerOperator: gt triggerThreshold: 0 tactics: - Exfiltration diff --git a/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumMassFanOut.yaml b/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumMassFanOut.yaml index 9fea64a1247..b8e2815aebb 100644 --- a/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumMassFanOut.yaml +++ b/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumMassFanOut.yaml @@ -1,7 +1,7 @@ id: f4a5b6c7-4d5e-6f7a-8b9c-0d1e2f3a4b5c name: "Tailscale Premium: Mass fan-out from single node" description: | - A single node initiated flows to 25 or more unique destinations within a 15-minute window. Sudden fan-out is consistent with port scanning, lateral discovery, or worm-style propagation across the tailnet. Tune the threshold for legitimate scanner / admin workstation behaviour. Requires Tailscale Premium or Enterprise (network flow logs). + Identifies when a single node initiates flows to 25 or more unique destinations within a 15-minute window. Sudden fan-out is consistent with port scanning, lateral discovery, or worm-style propagation. Requires Tailscale Premium or Enterprise. severity: High status: Available requiredDataConnectors: @@ -10,7 +10,7 @@ requiredDataConnectors: - Tailscale_Network_CL queryFrequency: 15m queryPeriod: 15m -triggerOperator: GreaterThan +triggerOperator: gt triggerThreshold: 0 tactics: - Discovery diff --git a/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumNewPostureIntegration.yaml b/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumNewPostureIntegration.yaml index 84bc92cfeee..56777da2221 100644 --- a/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumNewPostureIntegration.yaml +++ b/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumNewPostureIntegration.yaml @@ -1,7 +1,7 @@ id: b2c3d4e5-6789-0123-45ab-cdef12345031 name: "Tailscale Premium: New posture integration added" description: | - A new device-posture integration was added to the tailnet. Posture integrations connect Tailscale to MDM/EDR systems (Jamf, Kandji, Intune, Kolide, Microsoft Defender for Endpoint, CrowdStrike Falcon, SentinelOne, etc.) for device compliance enforcement. Adding a new integration is legitimate during setup or vendor changes; an unexpected addition could indicate an attacker establishing a control plane or bypassing existing compliance gates. Requires Tailscale Premium or Enterprise. + Identifies when a new device-posture integration is added to the tailnet (Jamf, Kandji, Intune, Kolide, Defender for Endpoint, CrowdStrike, SentinelOne, etc.). Unexpected additions may indicate an attacker establishing a control plane or bypassing compliance gates. Requires Tailscale Premium or Enterprise. severity: Medium status: Available requiredDataConnectors: @@ -10,7 +10,7 @@ requiredDataConnectors: - Tailscale_Audit_CL queryFrequency: 15m queryPeriod: 15m -triggerOperator: GreaterThan +triggerOperator: gt triggerThreshold: 0 tactics: - Persistence diff --git a/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumPostureIntegrationDisabled.yaml b/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumPostureIntegrationDisabled.yaml index f088c28c546..712ca67876d 100644 --- a/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumPostureIntegrationDisabled.yaml +++ b/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumPostureIntegrationDisabled.yaml @@ -1,7 +1,7 @@ id: a1b2c3d4-5678-9012-34ab-cdef12345030 name: "Tailscale Premium: Posture integration disabled or removed" description: | - A device-posture integration (Jamf, Kandji, Intune, Kolide, Microsoft Defender for Endpoint, CrowdStrike Falcon, SentinelOne, etc.) was disabled or removed from the tailnet. Posture integrations enforce device compliance (OS up-to-date, EDR running, etc.) before allowing tailnet access; removing one disables that enforcement and increases blast radius for compromised endpoints. Requires Tailscale Premium or Enterprise (posture integrations feature). + Identifies when a device-posture integration is disabled or removed from the tailnet. Posture integrations enforce device compliance; removing one disables that enforcement and increases blast radius for compromised endpoints. Requires Tailscale Premium or Enterprise. severity: High status: Available requiredDataConnectors: @@ -10,7 +10,7 @@ requiredDataConnectors: - Tailscale_Audit_CL queryFrequency: 15m queryPeriod: 15m -triggerOperator: GreaterThan +triggerOperator: gt triggerThreshold: 0 tactics: - DefenseEvasion diff --git a/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumSubnetRouterThroughputAnomaly.yaml b/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumSubnetRouterThroughputAnomaly.yaml index b25391fdee4..d7c9815f2e3 100644 --- a/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumSubnetRouterThroughputAnomaly.yaml +++ b/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumSubnetRouterThroughputAnomaly.yaml @@ -1,7 +1,7 @@ id: a5b6c7d8-5e6f-7a8b-9c0d-1e2f3a4b5c6d name: "Tailscale Premium: Subnet router throughput anomaly" description: | - A subnet router (gateway node bridging the tailnet to an on-prem or cloud subnet) handled 3x or more its 7-day baseline traffic in the last hour. Spikes can indicate increased legitimate workload, but also data exfiltration to a non-Tailscale destination via the gateway, or scanning of an attached subnet. Requires Tailscale Premium or Enterprise (network flow logs). + Identifies when a subnet router (gateway node bridging the tailnet to an on-prem or cloud subnet) handles 3x or more its 7-day baseline traffic in the last hour. Spikes can indicate exfiltration or scanning. Requires Tailscale Premium or Enterprise. severity: Low status: Available requiredDataConnectors: @@ -10,7 +10,7 @@ requiredDataConnectors: - Tailscale_Network_CL queryFrequency: 1h queryPeriod: 8d -triggerOperator: GreaterThan +triggerOperator: gt triggerThreshold: 0 tactics: - Exfiltration diff --git a/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumUnexpectedExitNodeEgress.yaml b/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumUnexpectedExitNodeEgress.yaml index 474e7f6cdd4..e95fe6c50f1 100644 --- a/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumUnexpectedExitNodeEgress.yaml +++ b/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumUnexpectedExitNodeEgress.yaml @@ -1,7 +1,7 @@ id: c1d2e3f4-1a2b-3c4d-5e6f-7a8b9c0d1e2f name: "Tailscale Premium: Unexpected exit-node egress" description: | - A node sent traffic via an exit node that it has not used in the prior 7-day baseline. Exit-node usage is typically intentional (regional egress, privacy routing); a first-seen exit destination from a specific source can indicate unsanctioned egress, a compromised node pivoting, or a policy misconfiguration. Requires Tailscale Premium or Enterprise (network flow logs). + Identifies when a node sends traffic via an exit node not used in the prior 7-day baseline. First-seen exit destinations from a specific source may indicate unsanctioned egress, a compromised node pivoting, or a policy misconfiguration. Requires Tailscale Premium or Enterprise. severity: Medium status: Available requiredDataConnectors: @@ -10,7 +10,7 @@ requiredDataConnectors: - Tailscale_Network_CL queryFrequency: 1h queryPeriod: 1h -triggerOperator: GreaterThan +triggerOperator: gt triggerThreshold: 0 tactics: - CommandAndControl diff --git a/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleSplitDnsModified.yaml b/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleSplitDnsModified.yaml index ace173aa676..c942fa3b27c 100644 --- a/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleSplitDnsModified.yaml +++ b/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleSplitDnsModified.yaml @@ -1,7 +1,7 @@ id: b4c5d6e7-1234-5678-90ab-cdef12345010 name: "Tailscale: Split-DNS configuration modified" description: | - The tailnet split-DNS configuration was modified. Split-DNS overrides per-domain DNS resolution within the tailnet - if an attacker adds a new domain mapping or changes the resolver IP for an existing domain, they can hijack DNS for that domain (e.g., point `*.bank.example` at an attacker-controlled resolver). Captured via the audit log which records every change with the full before/after document and actor attribution. + Identifies when the tailnet split-DNS configuration is modified. Split-DNS overrides per-domain resolution within the tailnet - an attacker adding a new domain mapping or changing the resolver IP can hijack DNS for that domain. severity: High status: Available requiredDataConnectors: @@ -10,7 +10,7 @@ requiredDataConnectors: - Tailscale_Audit_CL queryFrequency: 15m queryPeriod: 15m -triggerOperator: GreaterThan +triggerOperator: gt triggerThreshold: 0 tactics: - DefenseEvasion diff --git a/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleTailnetLockValidationFailed.yaml b/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleTailnetLockValidationFailed.yaml index c99c9dba86d..cd0e551f20a 100644 --- a/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleTailnetLockValidationFailed.yaml +++ b/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleTailnetLockValidationFailed.yaml @@ -1,7 +1,7 @@ id: e9f0a1b2-3456-7890-12cd-ef1234560040 name: "Tailscale: Tailnet lock validation failed" description: | - A device has a non-empty TailnetLockError, indicating the device failed tailnet-lock cryptographic validation. Tailnet lock requires that all new node-keys be co-signed by trusted signers - any error here is the only direct signal of a node-key injection attempt or a misconfigured signer. Investigate the failing device and which signer (or absence thereof) caused the failure. + Identifies tailnet devices with a non-empty TailnetLockError, indicating the device failed tailnet-lock cryptographic validation. Tailnet lock requires new node-keys be co-signed by trusted signers; errors here are the only direct signal of a node-key injection attempt. severity: High status: Available requiredDataConnectors: @@ -10,7 +10,7 @@ requiredDataConnectors: - Tailscale_Devices_CL queryFrequency: 1h queryPeriod: 1h -triggerOperator: GreaterThan +triggerOperator: gt triggerThreshold: 0 tactics: - DefenseEvasion diff --git a/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleUnauthorizedDeviceConnected.yaml b/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleUnauthorizedDeviceConnected.yaml index 9727041b6fc..e9cc0b56206 100644 --- a/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleUnauthorizedDeviceConnected.yaml +++ b/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleUnauthorizedDeviceConnected.yaml @@ -1,7 +1,7 @@ id: a1b2c3d4-5678-9012-3456-789012340042 name: "Tailscale: Unauthorized device connected to control plane" description: | - A device is actively connected to the Tailscale control plane (ConnectedToControl=true) but has not been authorized by an admin (Authorized=false). With device approval enabled, this is the queue of devices waiting for approval - any persistent or unexpected entry should be reviewed before being admitted. Without device approval enabled, this state should be transient (auto-approve); persistence indicates a misconfig or a node-key injection attempt. + Identifies devices actively connected to the Tailscale control plane (ConnectedToControl=true) but not yet authorized by an admin (Authorized=false). With device approval enabled, persistent entries warrant review; without approval, persistence may indicate a node-key injection attempt. severity: High status: Available requiredDataConnectors: @@ -10,7 +10,7 @@ requiredDataConnectors: - Tailscale_Devices_CL queryFrequency: 1h queryPeriod: 1h -triggerOperator: GreaterThan +triggerOperator: gt triggerThreshold: 0 tactics: - InitialAccess diff --git a/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleUserRoleElevated.yaml b/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleUserRoleElevated.yaml index f2642cadb7d..9d549dee867 100644 --- a/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleUserRoleElevated.yaml +++ b/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleUserRoleElevated.yaml @@ -1,7 +1,7 @@ id: d3c4e5f6-3456-7890-12ab-cdef12345003 name: "Tailscale: User role elevated to admin or owner" description: | - A user's tailnet role changed from a lower-privilege role (member / billing-admin / IT admin) to admin, network-admin, or owner between consecutive snapshots. Privilege escalation is a high-value attacker objective and warrants prompt review. + Identifies when a user's tailnet role changes from a lower-privilege role to admin, network-admin, or owner between consecutive snapshots. Privilege escalation is a high-value attacker objective and warrants prompt review. severity: High status: Available requiredDataConnectors: @@ -10,7 +10,7 @@ requiredDataConnectors: - Tailscale_Users_CL queryFrequency: 1h queryPeriod: 2d -triggerOperator: GreaterThan +triggerOperator: gt triggerThreshold: 0 tactics: - PrivilegeEscalation diff --git a/Solutions/Tailscale (CCF)/Data/Solution_Tailscale.json b/Solutions/Tailscale (CCF)/Data/Solution_Tailscale.json index 1ce1e4254e5..099069e926f 100644 --- a/Solutions/Tailscale (CCF)/Data/Solution_Tailscale.json +++ b/Solutions/Tailscale (CCF)/Data/Solution_Tailscale.json @@ -1,11 +1,11 @@ { "Name": "Tailscale (CCF)", - "Author": "noodlemctwoodle", + "Author": "noodlemctwoodle - ccfconnectors.county118@passmail.com", "Logo": "", - "Description": "The [Tailscale](https://tailscale.com/) solution for Microsoft Sentinel ingests Tailscale telemetry via OAuth2-secured APIs.\n\n**Data connectors in this solution (install one based on your Tailscale plan):**\n- **Tailscale Standard (CCF)** - Configuration audit logs (`/logging/configuration`). Use on **Personal (Free) and Standard** tailnets. Included in this release.\n- **Tailscale Premium (CCF)** - Configuration audit + network flow logs (`/logging/configuration` + `/logging/network`). Use on **Premium and Enterprise** tailnets. *Planned for a future release.*\n\n**Underlying technology:** Microsoft Sentinel Codeless Connector Framework (CCF) with OAuth2 client-credentials auth.\n\n**Pre-requisites:**\n1. Sign in to [Tailscale OAuth settings](https://login.tailscale.com/admin/settings/oauth)\n2. Create an OAuth client with the **Audit Logs - Read** scope (Standard connector). For the Premium connector also tick **Network Logs - Read**.\n3. Copy the client ID and client secret (secret shown once)\n4. Note your tailnet name from the [Keys page](https://login.tailscale.com/admin/settings/keys)", + "Description": "The [Tailscale](https://tailscale.com/) solution for Microsoft Sentinel ingests Tailscale identity, device, configuration, audit and (Premium) network-flow telemetry via OAuth2-secured APIs. Built on the Codeless Connector Framework (CCF) - no Function App or container required.\n\n**Data connectors in this solution (install the one matching your Tailscale plan):**\n- **Tailscale Standard (CCF)** - Configuration audit, devices, users, keys, webhooks, DNS, settings. Use on **Personal (Free), Starter and Premium** tailnets.\n- **Tailscale Premium (CCF)** - Everything in Standard plus network flow logs and posture integrations. Use on **Premium and Enterprise** tailnets for full coverage.\n\n**Pre-requisites:**\n1. Sign in to [Tailscale OAuth settings](https://login.tailscale.com/admin/settings/oauth)\n2. Create an OAuth client with the scopes for your tier (see the README in this solution).\n3. Copy the client ID and client secret (secret shown once).\n4. Note your tailnet name (e.g. `tailb094d7.ts.net`) from the [Keys page](https://login.tailscale.com/admin/settings/keys).", "BasePath": "C:\\GitHub\\Azure-Sentinel\\Solutions\\Tailscale (CCF)", "Version": "3.0.3", - "TemplateSpec": true, + "TemplateSpec": false, "Is1PConnector": false, "Data Connectors": [ "Data Connectors/TailscaleAuditLogs_ccf/Tailscale_ConnectorDefinition.json", diff --git a/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleAuthKeysNoExpiry.yaml b/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleAuthKeysNoExpiry.yaml index 0bae1501be2..0be911d6674 100644 --- a/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleAuthKeysNoExpiry.yaml +++ b/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleAuthKeysNoExpiry.yaml @@ -19,4 +19,9 @@ query: | | where isnull(Expires) or Expires == datetime(null) | project KeyId, Description, UserId, Created, Capabilities | order by Created asc +entityMappings: + - entityType: Account + fieldMappings: + - identifier: FullName + columnName: UserId version: 1.0.0 diff --git a/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumBeaconingCandidates.yaml b/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumBeaconingCandidates.yaml index 616c7b710b1..5f6716d7121 100644 --- a/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumBeaconingCandidates.yaml +++ b/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumBeaconingCandidates.yaml @@ -1,7 +1,7 @@ id: b28cbdd2-8cef-be9b-a38e-7faceedfdbec name: "Tailscale Premium: Beaconing candidates (regular periodic flows)" description: | - Detects flows that recur at a highly regular interval, which is the signature of C2 beaconing or scheduled exfiltration jobs. Looks for src->dst pairs where 80%+ of inter-flow gaps cluster around the same delta. Requires Tailscale Premium or Enterprise. + Identifies flows that recur at a highly regular interval, which is the signature of C2 beaconing or scheduled exfiltration jobs. Looks for src->dst pairs where 80%+ of inter-flow gaps cluster around the same delta. Requires Tailscale Premium or Enterprise. requiredDataConnectors: - connectorId: TailscalePremiumCCF dataTypes: @@ -34,4 +34,9 @@ query: | | extend BeaconPercent = round(MostFrequentDeltaCount * 100.0 / TotalFlows, 1) | where BeaconPercent >= beaconPercentThreshold | order by BeaconPercent desc +entityMappings: + - entityType: IP + fieldMappings: + - identifier: Address + columnName: Src version: 1.0.0 diff --git a/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumExitNodeUsage.yaml b/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumExitNodeUsage.yaml index d1e712f3a99..5c2a4122934 100644 --- a/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumExitNodeUsage.yaml +++ b/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumExitNodeUsage.yaml @@ -23,4 +23,13 @@ query: | | extend TotalMB = round(TotalBytes / 1024.0 / 1024.0, 2) | order by TotalBytes desc | take 100 +entityMappings: + - entityType: Host + fieldMappings: + - identifier: HostName + columnName: SrcNodeName + - entityType: IP + fieldMappings: + - identifier: Address + columnName: Src version: 1.0.0 diff --git a/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumNewNodePairs.yaml b/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumNewNodePairs.yaml index 738c75aaf99..b8c9b6be3cf 100644 --- a/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumNewNodePairs.yaml +++ b/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumNewNodePairs.yaml @@ -30,4 +30,9 @@ query: | recentPairs | join kind=leftanti baselinePairs on Src, Dst, Proto | order by FirstSeen asc +entityMappings: + - entityType: IP + fieldMappings: + - identifier: Address + columnName: Src version: 1.0.0 diff --git a/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumPostureInventory.yaml b/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumPostureInventory.yaml index d9b3cef67a3..8181369fa0e 100644 --- a/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumPostureInventory.yaml +++ b/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumPostureInventory.yaml @@ -15,4 +15,9 @@ query: | | summarize arg_max(TimeGenerated, *) by IntegrationId | project IntegrationId, Provider, ClientId, TenantId_Provider, Status, ConfigOverwrites | order by Provider asc +entityMappings: + - entityType: CloudApplication + fieldMappings: + - identifier: Name + columnName: Provider version: 1.0.0 diff --git a/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumTopTalkers.yaml b/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumTopTalkers.yaml index 38a0b1182d9..f1ed6deff0f 100644 --- a/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumTopTalkers.yaml +++ b/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumTopTalkers.yaml @@ -20,4 +20,9 @@ query: | | summarize TotalBytes = sum(TxBytes + RxBytes), TotalPackets = sum(tolong(t.txPkts) + tolong(t.rxPkts)), FlowCount = count() by Src, Dst, Proto | extend TotalMB = round(TotalBytes / 1024.0 / 1024.0, 2) | top 50 by TotalBytes +entityMappings: + - entityType: IP + fieldMappings: + - identifier: Address + columnName: Src version: 1.0.0 diff --git a/Solutions/Tailscale (CCF)/Package/3.0.3.zip b/Solutions/Tailscale (CCF)/Package/3.0.3.zip index 628f2f8712998698cc1718847ce90b1f44b3f7b3..f7be2fcb7a9d9055bbbea06cc18da4333718acd2 100644 GIT binary patch literal 61357 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

rqah_>gK@1M&$#mjA$ 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

T5$mLug|=k8?Kc6hAd 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 MGwACIbNP?@U)3+|WdHyG literal 0 HcmV?d00001 diff --git a/Solutions/Tailscale (CCF)/Package/mainTemplate.json b/Solutions/Tailscale (CCF)/Package/mainTemplate.json index af0e18942ea..515946da2f7 100644 --- a/Solutions/Tailscale (CCF)/Package/mainTemplate.json +++ b/Solutions/Tailscale (CCF)/Package/mainTemplate.json @@ -63,11 +63,11 @@ "email": "ccfconnectors.county118@passmail.com", "_email": "[variables('email')]", "_solutionName": "Tailscale (CCF)", - "_solutionVersion": "3.0.3", + "_solutionVersion": "3.0.4", "solutionId": "noodlemctwoodle.azure-sentinel-solution-tailscale-ccf", "_solutionId": "[variables('solutionId')]", "workspaceResourceId": "[resourceId('microsoft.OperationalInsights/Workspaces', parameters('workspace'))]", - "dataConnectorCCPVersion": "3.0.3", + "dataConnectorCCPVersion": "3.0.4", "_dataConnectorContentIdConnectorDefinition1": "TailscaleCCF", "dataConnectorTemplateNameConnectorDefinition1": "[concat(parameters('workspace'),'-dc-',uniquestring(variables('_dataConnectorContentIdConnectorDefinition1')))]", "_dataConnectorContentIdConnections1": "TailscaleCCFConnections", @@ -359,7 +359,7 @@ "properties": { "connectorUiConfig": { "title": "Tailscale Standard (CCF)", - "publisher": "Custom", + "publisher": "Community", "logo": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbDpzcGFjZT0icHJlc2VydmUiIGlkPSJMYXllcl8xIiB4PSIwIiB5PSIwIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIj48c3R5bGU+LnN0MHtvcGFjaXR5Oi4yO2VuYWJsZS1iYWNrZ3JvdW5kOm5ld308L3N0eWxlPjxwYXRoIGQ9Ik02NS42IDEyNy43YzM1LjMgMCA2My45LTI4LjYgNjMuOS02My45UzEwMC45IDAgNjUuNiAwIDEuOCAyOC42IDEuOCA2My45czI4LjYgNjMuOCA2My44IDYzLjgiIGNsYXNzPSJzdDAiLz48cGF0aCBkPSJNNjUuNiAzMTguMWMzNS4zIDAgNjMuOS0yOC42IDYzLjktNjMuOXMtMjguNi02My45LTYzLjktNjMuOVMxLjggMjE5IDEuOCAyNTQuMnMyOC42IDYzLjkgNjMuOCA2My45Ii8+PHBhdGggZD0iTTY1LjYgNTEyYzM1LjMgMCA2My45LTI4LjYgNjMuOS02My45cy0yOC42LTYzLjktNjMuOS02My45LTYzLjggMjguNy02My44IDYzLjlTMzAuNCA1MTIgNjUuNiA1MTIiIGNsYXNzPSJzdDAiLz48cGF0aCBkPSJNMjU3LjIgMzE4LjFjMzUuMyAwIDYzLjktMjguNiA2My45LTYzLjlzLTI4LjYtNjMuOS02My45LTYzLjktNjMuOSAyOC42LTYzLjkgNjMuOSAyOC42IDYzLjkgNjMuOSA2My45bTAgMTkzLjljMzUuMyAwIDYzLjktMjguNiA2My45LTYzLjlzLTI4LjYtNjMuOS02My45LTYzLjktNjMuOSAyOC42LTYzLjkgNjMuOSAyOC42IDYzLjkgNjMuOSA2My45Ii8+PHBhdGggZD0iTTI1Ny4yIDEyNy43YzM1LjMgMCA2My45LTI4LjYgNjMuOS02My45UzI5Mi41IDAgMjU3LjIgMHMtNjMuOSAyOC42LTYzLjkgNjMuOSAyOC42IDYzLjggNjMuOSA2My44bTE4OS4yIDBjMzUuMyAwIDYzLjktMjguNiA2My45LTYzLjlTNDgxLjYgMCA0NDYuNCAwYy0zNS4zIDAtNjMuOSAyOC42LTYzLjkgNjMuOXMyOC42IDYzLjggNjMuOSA2My44IiBjbGFzcz0ic3QwIi8+PHBhdGggZD0iTTQ0Ni40IDMxOC4xYzM1LjMgMCA2My45LTI4LjYgNjMuOS02My45cy0yOC42LTYzLjktNjMuOS02My45LTYzLjkgMjguNi02My45IDYzLjkgMjguNiA2My45IDYzLjkgNjMuOSIvPjxwYXRoIGQ9Ik00NDYuNCA1MTJjMzUuMyAwIDYzLjktMjguNiA2My45LTYzLjlzLTI4LjYtNjMuOS02My45LTYzLjktNjMuOSAyOC42LTYzLjkgNjMuOSAyOC42IDYzLjkgNjMuOSA2My45IiBjbGFzcz0ic3QwIi8+PC9zdmc+", "descriptionMarkdown": "Comprehensive Tailscale telemetry for **Personal (Free) and Standard** tier tailnets. Polls nine endpoints in one Connect:\n\n- `/logging/configuration` - configuration audit events (includes ACL, DNS, tag/group, settings changes)\n- `/devices` - device inventory (hostname, OS, IPs, tags, lastSeen, expiry)\n- `/users` - user inventory (role, status, deviceCount, connection state)\n- `/keys?all=true` - auth keys, API tokens, and OAuth client metadata\n- `/webhooks` - webhook configuration\n- `/dns/nameservers`, `/dns/preferences`, `/dns/searchpaths` - DNS state (merged into single `Tailscale_Dns_CL` table with `ConfigType` discriminator)\n- `/settings` - tailnet settings flags (device approval, key duration, etc.)\n\nSplit-DNS state (per-domain DNS overrides) is captured via the audit log rather than a separate snapshot table - every change is recorded with the full before/after document and actor attribution, which is richer than a periodic snapshot.\n\n**OAuth scopes required on the Tailscale client:** `logs:configuration:read`, `devices:core:read`, `users:read`, `auth_keys:read`, `webhooks:read`, `dns:read`, `feature_settings:read` (or the bundled `all:read`). For Premium and Enterprise tailnets that also need network flow logs and posture integrations, install **Tailscale Premium (CCF)** instead.", "graphQueries": [ @@ -1551,7 +1551,7 @@ "properties": { "connectorUiConfig": { "title": "Tailscale Standard (CCF)", - "publisher": "Custom", + "publisher": "Community", "logo": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbDpzcGFjZT0icHJlc2VydmUiIGlkPSJMYXllcl8xIiB4PSIwIiB5PSIwIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIj48c3R5bGU+LnN0MHtvcGFjaXR5Oi4yO2VuYWJsZS1iYWNrZ3JvdW5kOm5ld308L3N0eWxlPjxwYXRoIGQ9Ik02NS42IDEyNy43YzM1LjMgMCA2My45LTI4LjYgNjMuOS02My45UzEwMC45IDAgNjUuNiAwIDEuOCAyOC42IDEuOCA2My45czI4LjYgNjMuOCA2My44IDYzLjgiIGNsYXNzPSJzdDAiLz48cGF0aCBkPSJNNjUuNiAzMTguMWMzNS4zIDAgNjMuOS0yOC42IDYzLjktNjMuOXMtMjguNi02My45LTYzLjktNjMuOVMxLjggMjE5IDEuOCAyNTQuMnMyOC42IDYzLjkgNjMuOCA2My45Ii8+PHBhdGggZD0iTTY1LjYgNTEyYzM1LjMgMCA2My45LTI4LjYgNjMuOS02My45cy0yOC42LTYzLjktNjMuOS02My45LTYzLjggMjguNy02My44IDYzLjlTMzAuNCA1MTIgNjUuNiA1MTIiIGNsYXNzPSJzdDAiLz48cGF0aCBkPSJNMjU3LjIgMzE4LjFjMzUuMyAwIDYzLjktMjguNiA2My45LTYzLjlzLTI4LjYtNjMuOS02My45LTYzLjktNjMuOSAyOC42LTYzLjkgNjMuOSAyOC42IDYzLjkgNjMuOSA2My45bTAgMTkzLjljMzUuMyAwIDYzLjktMjguNiA2My45LTYzLjlzLTI4LjYtNjMuOS02My45LTYzLjktNjMuOSAyOC42LTYzLjkgNjMuOSAyOC42IDYzLjkgNjMuOSA2My45Ii8+PHBhdGggZD0iTTI1Ny4yIDEyNy43YzM1LjMgMCA2My45LTI4LjYgNjMuOS02My45UzI5Mi41IDAgMjU3LjIgMHMtNjMuOSAyOC42LTYzLjkgNjMuOSAyOC42IDYzLjggNjMuOSA2My44bTE4OS4yIDBjMzUuMyAwIDYzLjktMjguNiA2My45LTYzLjlTNDgxLjYgMCA0NDYuNCAwYy0zNS4zIDAtNjMuOSAyOC42LTYzLjkgNjMuOXMyOC42IDYzLjggNjMuOSA2My44IiBjbGFzcz0ic3QwIi8+PHBhdGggZD0iTTQ0Ni40IDMxOC4xYzM1LjMgMCA2My45LTI4LjYgNjMuOS02My45cy0yOC42LTYzLjktNjMuOS02My45LTYzLjkgMjguNi02My45IDYzLjkgMjguNiA2My45IDYzLjkgNjMuOSIvPjxwYXRoIGQ9Ik00NDYuNCA1MTJjMzUuMyAwIDYzLjktMjguNiA2My45LTYzLjlzLTI4LjYtNjMuOS02My45LTYzLjktNjMuOSAyOC42LTYzLjkgNjMuOSAyOC42IDYzLjkgNjMuOSA2My45IiBjbGFzcz0ic3QwIi8+PC9zdmc+", "descriptionMarkdown": "Comprehensive Tailscale telemetry for **Personal (Free) and Standard** tier tailnets. Polls nine endpoints in one Connect:\n\n- `/logging/configuration` - configuration audit events (includes ACL, DNS, tag/group, settings changes)\n- `/devices` - device inventory (hostname, OS, IPs, tags, lastSeen, expiry)\n- `/users` - user inventory (role, status, deviceCount, connection state)\n- `/keys?all=true` - auth keys, API tokens, and OAuth client metadata\n- `/webhooks` - webhook configuration\n- `/dns/nameservers`, `/dns/preferences`, `/dns/searchpaths` - DNS state (merged into single `Tailscale_Dns_CL` table with `ConfigType` discriminator)\n- `/settings` - tailnet settings flags (device approval, key duration, etc.)\n\nSplit-DNS state (per-domain DNS overrides) is captured via the audit log rather than a separate snapshot table - every change is recorded with the full before/after document and actor attribution, which is richer than a periodic snapshot.\n\n**OAuth scopes required on the Tailscale client:** `logs:configuration:read`, `devices:core:read`, `users:read`, `auth_keys:read`, `webhooks:read`, `dns:read`, `feature_settings:read` (or the bundled `all:read`). For Premium and Enterprise tailnets that also need network flow logs and posture integrations, install **Tailscale Premium (CCF)** instead.", "graphQueries": [ @@ -2266,7 +2266,7 @@ "properties": { "connectorUiConfig": { "title": "Tailscale Premium (CCF)", - "publisher": "Custom", + "publisher": "Community", "logo": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbDpzcGFjZT0icHJlc2VydmUiIGlkPSJMYXllcl8xIiB4PSIwIiB5PSIwIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIj48c3R5bGU+LnN0MHtvcGFjaXR5Oi4yO2VuYWJsZS1iYWNrZ3JvdW5kOm5ld308L3N0eWxlPjxwYXRoIGQ9Ik02NS42IDEyNy43YzM1LjMgMCA2My45LTI4LjYgNjMuOS02My45UzEwMC45IDAgNjUuNiAwIDEuOCAyOC42IDEuOCA2My45czI4LjYgNjMuOCA2My44IDYzLjgiIGNsYXNzPSJzdDAiLz48cGF0aCBkPSJNNjUuNiAzMTguMWMzNS4zIDAgNjMuOS0yOC42IDYzLjktNjMuOXMtMjguNi02My45LTYzLjktNjMuOVMxLjggMjE5IDEuOCAyNTQuMnMyOC42IDYzLjkgNjMuOCA2My45Ii8+PHBhdGggZD0iTTY1LjYgNTEyYzM1LjMgMCA2My45LTI4LjYgNjMuOS02My45cy0yOC42LTYzLjktNjMuOS02My45LTYzLjggMjguNy02My44IDYzLjlTMzAuNCA1MTIgNjUuNiA1MTIiIGNsYXNzPSJzdDAiLz48cGF0aCBkPSJNMjU3LjIgMzE4LjFjMzUuMyAwIDYzLjktMjguNiA2My45LTYzLjlzLTI4LjYtNjMuOS02My45LTYzLjktNjMuOSAyOC42LTYzLjkgNjMuOSAyOC42IDYzLjkgNjMuOSA2My45bTAgMTkzLjljMzUuMyAwIDYzLjktMjguNiA2My45LTYzLjlzLTI4LjYtNjMuOS02My45LTYzLjktNjMuOSAyOC42LTYzLjkgNjMuOSAyOC42IDYzLjkgNjMuOSA2My45Ii8+PHBhdGggZD0iTTI1Ny4yIDEyNy43YzM1LjMgMCA2My45LTI4LjYgNjMuOS02My45UzI5Mi41IDAgMjU3LjIgMHMtNjMuOSAyOC42LTYzLjkgNjMuOSAyOC42IDYzLjggNjMuOSA2My44bTE4OS4yIDBjMzUuMyAwIDYzLjktMjguNiA2My45LTYzLjlTNDgxLjYgMCA0NDYuNCAwYy0zNS4zIDAtNjMuOSAyOC42LTYzLjkgNjMuOXMyOC42IDYzLjggNjMuOSA2My44IiBjbGFzcz0ic3QwIi8+PHBhdGggZD0iTTQ0Ni40IDMxOC4xYzM1LjMgMCA2My45LTI4LjYgNjMuOS02My45cy0yOC42LTYzLjktNjMuOS02My45LTYzLjkgMjguNi02My45IDYzLjkgMjguNiA2My45IDYzLjkgNjMuOSIvPjxwYXRoIGQ9Ik00NDYuNCA1MTJjMzUuMyAwIDYzLjktMjguNiA2My45LTYzLjlzLTI4LjYtNjMuOS02My45LTYzLjktNjMuOSAyOC42LTYzLjkgNjMuOSAyOC42IDYzLjkgNjMuOSA2My45IiBjbGFzcz0ic3QwIi8+PC9zdmc+", "descriptionMarkdown": "Comprehensive Tailscale telemetry for **Premium and Enterprise** tier tailnets. Polls every endpoint the Standard connector polls, **plus** Premium-only network flow logs and posture-integration inventory. Eleven endpoints in one Connect:\n\n- `/logging/configuration` - configuration audit events\n- `/logging/network` - **Premium** network flow logs (per-node traffic with src/dst/protocol/bytes)\n- `/devices` - device inventory\n- `/users` - user inventory\n- `/keys?all=true` - auth keys + API tokens + OAuth clients\n- `/webhooks` - webhook configuration\n- `/dns/nameservers`, `/dns/preferences`, `/dns/searchpaths` - DNS state (merged into single `Tailscale_Dns_CL` table with `ConfigType` discriminator)\n- `/settings` - tailnet settings flags\n- `/posture/integrations` - **Premium** MDM/EDR integration inventory (Jamf, Kandji, Intune, Kolide, Microsoft Defender for Endpoint, CrowdStrike Falcon, SentinelOne, etc.)\n\n**OAuth scopes required:** `logs:configuration:read`, `logs:network:read`, `devices:core:read`, `users:read`, `auth_keys:read`, `webhooks:read`, `dns:read`, `feature_settings:read` (or the bundled `all:read`).\n\n**If your tailnet is Personal (Free) or Standard tier, install `Tailscale Standard (CCF)` instead - this Premium connector's network and posture pollers will return 403 on lower tiers.**", "graphQueries": [ @@ -3572,7 +3572,7 @@ "properties": { "connectorUiConfig": { "title": "Tailscale Premium (CCF)", - "publisher": "Custom", + "publisher": "Community", "logo": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbDpzcGFjZT0icHJlc2VydmUiIGlkPSJMYXllcl8xIiB4PSIwIiB5PSIwIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIj48c3R5bGU+LnN0MHtvcGFjaXR5Oi4yO2VuYWJsZS1iYWNrZ3JvdW5kOm5ld308L3N0eWxlPjxwYXRoIGQ9Ik02NS42IDEyNy43YzM1LjMgMCA2My45LTI4LjYgNjMuOS02My45UzEwMC45IDAgNjUuNiAwIDEuOCAyOC42IDEuOCA2My45czI4LjYgNjMuOCA2My44IDYzLjgiIGNsYXNzPSJzdDAiLz48cGF0aCBkPSJNNjUuNiAzMTguMWMzNS4zIDAgNjMuOS0yOC42IDYzLjktNjMuOXMtMjguNi02My45LTYzLjktNjMuOVMxLjggMjE5IDEuOCAyNTQuMnMyOC42IDYzLjkgNjMuOCA2My45Ii8+PHBhdGggZD0iTTY1LjYgNTEyYzM1LjMgMCA2My45LTI4LjYgNjMuOS02My45cy0yOC42LTYzLjktNjMuOS02My45LTYzLjggMjguNy02My44IDYzLjlTMzAuNCA1MTIgNjUuNiA1MTIiIGNsYXNzPSJzdDAiLz48cGF0aCBkPSJNMjU3LjIgMzE4LjFjMzUuMyAwIDYzLjktMjguNiA2My45LTYzLjlzLTI4LjYtNjMuOS02My45LTYzLjktNjMuOSAyOC42LTYzLjkgNjMuOSAyOC42IDYzLjkgNjMuOSA2My45bTAgMTkzLjljMzUuMyAwIDYzLjktMjguNiA2My45LTYzLjlzLTI4LjYtNjMuOS02My45LTYzLjktNjMuOSAyOC42LTYzLjkgNjMuOSAyOC42IDYzLjkgNjMuOSA2My45Ii8+PHBhdGggZD0iTTI1Ny4yIDEyNy43YzM1LjMgMCA2My45LTI4LjYgNjMuOS02My45UzI5Mi41IDAgMjU3LjIgMHMtNjMuOSAyOC42LTYzLjkgNjMuOSAyOC42IDYzLjggNjMuOSA2My44bTE4OS4yIDBjMzUuMyAwIDYzLjktMjguNiA2My45LTYzLjlTNDgxLjYgMCA0NDYuNCAwYy0zNS4zIDAtNjMuOSAyOC42LTYzLjkgNjMuOXMyOC42IDYzLjggNjMuOSA2My44IiBjbGFzcz0ic3QwIi8+PHBhdGggZD0iTTQ0Ni40IDMxOC4xYzM1LjMgMCA2My45LTI4LjYgNjMuOS02My45cy0yOC42LTYzLjktNjMuOS02My45LTYzLjkgMjguNi02My45IDYzLjkgMjguNiA2My45IDYzLjkgNjMuOSIvPjxwYXRoIGQ9Ik00NDYuNCA1MTJjMzUuMyAwIDYzLjktMjguNiA2My45LTYzLjlzLTI4LjYtNjMuOS02My45LTYzLjktNjMuOSAyOC42LTYzLjkgNjMuOSAyOC42IDYzLjkgNjMuOSA2My45IiBjbGFzcz0ic3QwIi8+PC9zdmc+", "descriptionMarkdown": "Comprehensive Tailscale telemetry for **Premium and Enterprise** tier tailnets. Polls every endpoint the Standard connector polls, **plus** Premium-only network flow logs and posture-integration inventory. Eleven endpoints in one Connect:\n\n- `/logging/configuration` - configuration audit events\n- `/logging/network` - **Premium** network flow logs (per-node traffic with src/dst/protocol/bytes)\n- `/devices` - device inventory\n- `/users` - user inventory\n- `/keys?all=true` - auth keys + API tokens + OAuth clients\n- `/webhooks` - webhook configuration\n- `/dns/nameservers`, `/dns/preferences`, `/dns/searchpaths` - DNS state (merged into single `Tailscale_Dns_CL` table with `ConfigType` discriminator)\n- `/settings` - tailnet settings flags\n- `/posture/integrations` - **Premium** MDM/EDR integration inventory (Jamf, Kandji, Intune, Kolide, Microsoft Defender for Endpoint, CrowdStrike Falcon, SentinelOne, etc.)\n\n**OAuth scopes required:** `logs:configuration:read`, `logs:network:read`, `devices:core:read`, `users:read`, `auth_keys:read`, `webhooks:read`, `dns:read`, `feature_settings:read` (or the bundled `all:read`).\n\n**If your tailnet is Personal (Free) or Standard tier, install `Tailscale Standard (CCF)` instead - this Premium connector's network and posture pollers will return 403 on lower tiers.**", "graphQueries": [ @@ -4376,7 +4376,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleNewAPIaccesstokenorOAuthclientcreated_AnalyticalRules Analytics Rule with template version 3.0.3", + "description": "TailscaleNewAPIaccesstokenorOAuthclientcreated_AnalyticalRules Analytics Rule with template version 3.0.4", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject1').analyticRuleVersion1]", @@ -4404,10 +4404,10 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscaleCCF", "dataTypes": [ "Tailscale_Audit_CL" - ] + ], + "connectorId": "TailscaleCCF" } ], "tactics": [ @@ -4420,22 +4420,22 @@ ], "entityMappings": [ { + "entityType": "Account", "fieldMappings": [ { - "columnName": "ActorLogin", - "identifier": "FullName" + "identifier": "FullName", + "columnName": "ActorLogin" } - ], - "entityType": "Account" + ] } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { + "enabled": true, + "groupByEntities": [], "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "groupByEntities": [], - "enabled": true, "lookbackDuration": "5h" } } @@ -4492,7 +4492,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscalePolicyfileACLmodified_AnalyticalRules Analytics Rule with template version 3.0.3", + "description": "TailscalePolicyfileACLmodified_AnalyticalRules Analytics Rule with template version 3.0.4", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject2').analyticRuleVersion2]", @@ -4520,10 +4520,10 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscaleCCF", "dataTypes": [ "Tailscale_Audit_CL" - ] + ], + "connectorId": "TailscaleCCF" } ], "tactics": [ @@ -4535,22 +4535,22 @@ ], "entityMappings": [ { + "entityType": "Account", "fieldMappings": [ { - "columnName": "ActorLogin", - "identifier": "FullName" + "identifier": "FullName", + "columnName": "ActorLogin" } - ], - "entityType": "Account" + ] } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { + "enabled": true, + "groupByEntities": [], "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "groupByEntities": [], - "enabled": true, "lookbackDuration": "5h" } } @@ -4607,7 +4607,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleAuthkeycreated_AnalyticalRules Analytics Rule with template version 3.0.3", + "description": "TailscaleAuthkeycreated_AnalyticalRules Analytics Rule with template version 3.0.4", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject3').analyticRuleVersion3]", @@ -4635,10 +4635,10 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscaleCCF", "dataTypes": [ "Tailscale_Audit_CL" - ] + ], + "connectorId": "TailscaleCCF" } ], "tactics": [ @@ -4649,22 +4649,22 @@ ], "entityMappings": [ { + "entityType": "Account", "fieldMappings": [ { - "columnName": "ActorLogin", - "identifier": "FullName" + "identifier": "FullName", + "columnName": "ActorLogin" } - ], - "entityType": "Account" + ] } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { + "enabled": true, + "groupByEntities": [], "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "groupByEntities": [], - "enabled": true, "lookbackDuration": "5h" } } @@ -4721,7 +4721,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleExitnodeadvertisedorapproved_AnalyticalRules Analytics Rule with template version 3.0.3", + "description": "TailscaleExitnodeadvertisedorapproved_AnalyticalRules Analytics Rule with template version 3.0.4", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject4').analyticRuleVersion4]", @@ -4749,10 +4749,10 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscaleCCF", "dataTypes": [ "Tailscale_Audit_CL" - ] + ], + "connectorId": "TailscaleCCF" } ], "tactics": [ @@ -4764,31 +4764,31 @@ ], "entityMappings": [ { + "entityType": "Account", "fieldMappings": [ { - "columnName": "ActorLogin", - "identifier": "FullName" + "identifier": "FullName", + "columnName": "ActorLogin" } - ], - "entityType": "Account" + ] }, { + "entityType": "Host", "fieldMappings": [ { - "columnName": "NodeName", - "identifier": "HostName" + "identifier": "HostName", + "columnName": "NodeName" } - ], - "entityType": "Host" + ] } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { + "enabled": true, + "groupByEntities": [], "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "groupByEntities": [], - "enabled": true, "lookbackDuration": "5h" } } @@ -4845,7 +4845,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleMasscredentialrevocationinshortwindow_AnalyticalRules Analytics Rule with template version 3.0.3", + "description": "TailscaleMasscredentialrevocationinshortwindow_AnalyticalRules Analytics Rule with template version 3.0.4", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject5').analyticRuleVersion5]", @@ -4873,10 +4873,10 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscaleCCF", "dataTypes": [ "Tailscale_Audit_CL" - ] + ], + "connectorId": "TailscaleCCF" } ], "tactics": [ @@ -4888,22 +4888,22 @@ ], "entityMappings": [ { + "entityType": "Account", "fieldMappings": [ { - "columnName": "ActorLogin", - "identifier": "FullName" + "identifier": "FullName", + "columnName": "ActorLogin" } - ], - "entityType": "Account" + ] } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { + "enabled": true, + "groupByEntities": [], "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "groupByEntities": [], - "enabled": true, "lookbackDuration": "5h" } } @@ -4960,7 +4960,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleDeviceKeyExpiringSoon_AnalyticalRules Analytics Rule with template version 3.0.3", + "description": "TailscaleDeviceKeyExpiringSoon_AnalyticalRules Analytics Rule with template version 3.0.4", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject6').analyticRuleVersion6]", @@ -4988,10 +4988,10 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscaleCCF", "dataTypes": [ "Tailscale_Devices_CL" - ] + ], + "connectorId": "TailscaleCCF" } ], "tactics": [ @@ -5002,31 +5002,31 @@ ], "entityMappings": [ { + "entityType": "Host", "fieldMappings": [ { - "columnName": "Hostname", - "identifier": "HostName" + "identifier": "HostName", + "columnName": "Hostname" } - ], - "entityType": "Host" + ] }, { + "entityType": "Account", "fieldMappings": [ { - "columnName": "User", - "identifier": "FullName" + "identifier": "FullName", + "columnName": "User" } - ], - "entityType": "Account" + ] } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { + "enabled": true, + "groupByEntities": [], "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "groupByEntities": [], - "enabled": true, "lookbackDuration": "1d" } } @@ -5083,7 +5083,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleDeviceAdvertisingSubnetRoutes_AnalyticalRules Analytics Rule with template version 3.0.3", + "description": "TailscaleDeviceAdvertisingSubnetRoutes_AnalyticalRules Analytics Rule with template version 3.0.4", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject7').analyticRuleVersion7]", @@ -5111,10 +5111,10 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscaleCCF", "dataTypes": [ "Tailscale_Devices_CL" - ] + ], + "connectorId": "TailscaleCCF" } ], "tactics": [ @@ -5127,31 +5127,31 @@ ], "entityMappings": [ { + "entityType": "Host", "fieldMappings": [ { - "columnName": "Hostname", - "identifier": "HostName" + "identifier": "HostName", + "columnName": "Hostname" } - ], - "entityType": "Host" + ] }, { + "entityType": "Account", "fieldMappings": [ { - "columnName": "User", - "identifier": "FullName" + "identifier": "FullName", + "columnName": "User" } - ], - "entityType": "Account" + ] } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { + "enabled": true, + "groupByEntities": [], "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "groupByEntities": [], - "enabled": true, "lookbackDuration": "1d" } } @@ -5208,7 +5208,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleUserRoleElevated_AnalyticalRules Analytics Rule with template version 3.0.3", + "description": "TailscaleUserRoleElevated_AnalyticalRules Analytics Rule with template version 3.0.4", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject8').analyticRuleVersion8]", @@ -5236,10 +5236,10 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscaleCCF", "dataTypes": [ "Tailscale_Users_CL" - ] + ], + "connectorId": "TailscaleCCF" } ], "tactics": [ @@ -5252,22 +5252,22 @@ ], "entityMappings": [ { + "entityType": "Account", "fieldMappings": [ { - "columnName": "LoginName", - "identifier": "FullName" + "identifier": "FullName", + "columnName": "LoginName" } - ], - "entityType": "Account" + ] } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { + "enabled": true, + "groupByEntities": [], "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "groupByEntities": [], - "enabled": true, "lookbackDuration": "1d" } } @@ -5324,7 +5324,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleSplitDnsModified_AnalyticalRules Analytics Rule with template version 3.0.3", + "description": "TailscaleSplitDnsModified_AnalyticalRules Analytics Rule with template version 3.0.4", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject9').analyticRuleVersion9]", @@ -5352,10 +5352,10 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscaleCCF", "dataTypes": [ "Tailscale_Audit_CL" - ] + ], + "connectorId": "TailscaleCCF" } ], "tactics": [ @@ -5368,22 +5368,22 @@ ], "entityMappings": [ { + "entityType": "Account", "fieldMappings": [ { - "columnName": "ActorLogin", - "identifier": "FullName" + "identifier": "FullName", + "columnName": "ActorLogin" } - ], - "entityType": "Account" + ] } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { + "enabled": true, + "groupByEntities": [], "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "groupByEntities": [], - "enabled": true, "lookbackDuration": "1d" } } @@ -5440,7 +5440,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleDnsNameserversModified_AnalyticalRules Analytics Rule with template version 3.0.3", + "description": "TailscaleDnsNameserversModified_AnalyticalRules Analytics Rule with template version 3.0.4", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject10').analyticRuleVersion10]", @@ -5468,10 +5468,10 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscaleCCF", "dataTypes": [ "Tailscale_Audit_CL" - ] + ], + "connectorId": "TailscaleCCF" } ], "tactics": [ @@ -5484,22 +5484,22 @@ ], "entityMappings": [ { + "entityType": "Account", "fieldMappings": [ { - "columnName": "ActorLogin", - "identifier": "FullName" + "identifier": "FullName", + "columnName": "ActorLogin" } - ], - "entityType": "Account" + ] } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { + "enabled": true, + "groupByEntities": [], "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "groupByEntities": [], - "enabled": true, "lookbackDuration": "1d" } } @@ -5556,7 +5556,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleMagicDnsDisabled_AnalyticalRules Analytics Rule with template version 3.0.3", + "description": "TailscaleMagicDnsDisabled_AnalyticalRules Analytics Rule with template version 3.0.4", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject11').analyticRuleVersion11]", @@ -5584,10 +5584,10 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscaleCCF", "dataTypes": [ "Tailscale_Audit_CL" - ] + ], + "connectorId": "TailscaleCCF" } ], "tactics": [ @@ -5598,22 +5598,22 @@ ], "entityMappings": [ { + "entityType": "Account", "fieldMappings": [ { - "columnName": "ActorLogin", - "identifier": "FullName" + "identifier": "FullName", + "columnName": "ActorLogin" } - ], - "entityType": "Account" + ] } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { + "enabled": true, + "groupByEntities": [], "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "groupByEntities": [], - "enabled": true, "lookbackDuration": "1d" } } @@ -5670,7 +5670,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleTailnetLockValidationFailed_AnalyticalRules Analytics Rule with template version 3.0.3", + "description": "TailscaleTailnetLockValidationFailed_AnalyticalRules Analytics Rule with template version 3.0.4", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject12').analyticRuleVersion12]", @@ -5698,10 +5698,10 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscaleCCF", "dataTypes": [ "Tailscale_Devices_CL" - ] + ], + "connectorId": "TailscaleCCF" } ], "tactics": [ @@ -5714,31 +5714,31 @@ ], "entityMappings": [ { + "entityType": "Host", "fieldMappings": [ { - "columnName": "Hostname", - "identifier": "HostName" + "identifier": "HostName", + "columnName": "Hostname" } - ], - "entityType": "Host" + ] }, { + "entityType": "Account", "fieldMappings": [ { - "columnName": "User", - "identifier": "FullName" + "identifier": "FullName", + "columnName": "User" } - ], - "entityType": "Account" + ] } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { + "enabled": true, + "groupByEntities": [], "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "groupByEntities": [], - "enabled": true, "lookbackDuration": "1d" } } @@ -5795,7 +5795,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleDeviceSshNewlyEnabled_AnalyticalRules Analytics Rule with template version 3.0.3", + "description": "TailscaleDeviceSshNewlyEnabled_AnalyticalRules Analytics Rule with template version 3.0.4", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject13').analyticRuleVersion13]", @@ -5823,10 +5823,10 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscaleCCF", "dataTypes": [ "Tailscale_Devices_CL" - ] + ], + "connectorId": "TailscaleCCF" } ], "tactics": [ @@ -5839,31 +5839,31 @@ ], "entityMappings": [ { + "entityType": "Host", "fieldMappings": [ { - "columnName": "Hostname", - "identifier": "HostName" + "identifier": "HostName", + "columnName": "Hostname" } - ], - "entityType": "Host" + ] }, { + "entityType": "Account", "fieldMappings": [ { - "columnName": "User", - "identifier": "FullName" + "identifier": "FullName", + "columnName": "User" } - ], - "entityType": "Account" + ] } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { + "enabled": true, + "groupByEntities": [], "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "groupByEntities": [], - "enabled": true, "lookbackDuration": "1d" } } @@ -5920,7 +5920,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleUnauthorizedDeviceConnected_AnalyticalRules Analytics Rule with template version 3.0.3", + "description": "TailscaleUnauthorizedDeviceConnected_AnalyticalRules Analytics Rule with template version 3.0.4", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject14').analyticRuleVersion14]", @@ -5948,10 +5948,10 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscaleCCF", "dataTypes": [ "Tailscale_Devices_CL" - ] + ], + "connectorId": "TailscaleCCF" } ], "tactics": [ @@ -5964,31 +5964,31 @@ ], "entityMappings": [ { + "entityType": "Host", "fieldMappings": [ { - "columnName": "Hostname", - "identifier": "HostName" + "identifier": "HostName", + "columnName": "Hostname" } - ], - "entityType": "Host" + ] }, { + "entityType": "Account", "fieldMappings": [ { - "columnName": "User", - "identifier": "FullName" + "identifier": "FullName", + "columnName": "User" } - ], - "entityType": "Account" + ] } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { + "enabled": true, + "groupByEntities": [], "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "groupByEntities": [], - "enabled": true, "lookbackDuration": "1d" } } @@ -6045,7 +6045,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleExternalDeviceAdded_AnalyticalRules Analytics Rule with template version 3.0.3", + "description": "TailscaleExternalDeviceAdded_AnalyticalRules Analytics Rule with template version 3.0.4", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject15').analyticRuleVersion15]", @@ -6073,10 +6073,10 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscaleCCF", "dataTypes": [ "Tailscale_Devices_CL" - ] + ], + "connectorId": "TailscaleCCF" } ], "tactics": [ @@ -6087,31 +6087,31 @@ ], "entityMappings": [ { + "entityType": "Host", "fieldMappings": [ { - "columnName": "Hostname", - "identifier": "HostName" + "identifier": "HostName", + "columnName": "Hostname" } - ], - "entityType": "Host" + ] }, { + "entityType": "Account", "fieldMappings": [ { - "columnName": "User", - "identifier": "FullName" + "identifier": "FullName", + "columnName": "User" } - ], - "entityType": "Account" + ] } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { + "enabled": true, + "groupByEntities": [], "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "groupByEntities": [], - "enabled": true, "lookbackDuration": "1d" } } @@ -6168,7 +6168,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscalePremiumUnexpectedExitNodeEgress_AnalyticalRules Analytics Rule with template version 3.0.3", + "description": "TailscalePremiumUnexpectedExitNodeEgress_AnalyticalRules Analytics Rule with template version 3.0.4", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject16').analyticRuleVersion16]", @@ -6196,10 +6196,10 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscalePremiumCCF", "dataTypes": [ "Tailscale_Network_CL" - ] + ], + "connectorId": "TailscalePremiumCCF" } ], "tactics": [ @@ -6212,31 +6212,31 @@ ], "entityMappings": [ { + "entityType": "Host", "fieldMappings": [ { - "columnName": "SrcNodeName", - "identifier": "HostName" + "identifier": "HostName", + "columnName": "SrcNodeName" } - ], - "entityType": "Host" + ] }, { + "entityType": "IP", "fieldMappings": [ { - "columnName": "Src", - "identifier": "Address" + "identifier": "Address", + "columnName": "Src" } - ], - "entityType": "IP" + ] } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { + "enabled": true, + "groupByEntities": [], "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "groupByEntities": [], - "enabled": true, "lookbackDuration": "5h" } } @@ -6293,7 +6293,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscalePremiumLargeOutboundTransfer_AnalyticalRules Analytics Rule with template version 3.0.3", + "description": "TailscalePremiumLargeOutboundTransfer_AnalyticalRules Analytics Rule with template version 3.0.4", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject17').analyticRuleVersion17]", @@ -6321,10 +6321,10 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscalePremiumCCF", "dataTypes": [ "Tailscale_Network_CL" - ] + ], + "connectorId": "TailscalePremiumCCF" } ], "tactics": [ @@ -6337,31 +6337,31 @@ ], "entityMappings": [ { + "entityType": "Host", "fieldMappings": [ { - "columnName": "SrcNodeName", - "identifier": "HostName" + "identifier": "HostName", + "columnName": "SrcNodeName" } - ], - "entityType": "Host" + ] }, { + "entityType": "IP", "fieldMappings": [ { - "columnName": "Src", - "identifier": "Address" + "identifier": "Address", + "columnName": "Src" } - ], - "entityType": "IP" + ] } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { + "enabled": true, + "groupByEntities": [], "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "groupByEntities": [], - "enabled": true, "lookbackDuration": "5h" } } @@ -6418,7 +6418,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscalePremiumBeaconingDetected_AnalyticalRules Analytics Rule with template version 3.0.3", + "description": "TailscalePremiumBeaconingDetected_AnalyticalRules Analytics Rule with template version 3.0.4", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject18').analyticRuleVersion18]", @@ -6446,10 +6446,10 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscalePremiumCCF", "dataTypes": [ "Tailscale_Network_CL" - ] + ], + "connectorId": "TailscalePremiumCCF" } ], "tactics": [ @@ -6463,22 +6463,22 @@ ], "entityMappings": [ { + "entityType": "IP", "fieldMappings": [ { - "columnName": "Src", - "identifier": "Address" + "identifier": "Address", + "columnName": "Src" } - ], - "entityType": "IP" + ] } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { + "enabled": true, + "groupByEntities": [], "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "groupByEntities": [], - "enabled": true, "lookbackDuration": "1d" } } @@ -6535,7 +6535,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscalePremiumMassFanOut_AnalyticalRules Analytics Rule with template version 3.0.3", + "description": "TailscalePremiumMassFanOut_AnalyticalRules Analytics Rule with template version 3.0.4", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject19').analyticRuleVersion19]", @@ -6563,10 +6563,10 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscalePremiumCCF", "dataTypes": [ "Tailscale_Network_CL" - ] + ], + "connectorId": "TailscalePremiumCCF" } ], "tactics": [ @@ -6580,31 +6580,31 @@ ], "entityMappings": [ { + "entityType": "Host", "fieldMappings": [ { - "columnName": "SrcNodeName", - "identifier": "HostName" + "identifier": "HostName", + "columnName": "SrcNodeName" } - ], - "entityType": "Host" + ] }, { + "entityType": "IP", "fieldMappings": [ { - "columnName": "Src", - "identifier": "Address" + "identifier": "Address", + "columnName": "Src" } - ], - "entityType": "IP" + ] } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { + "enabled": true, + "groupByEntities": [], "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "groupByEntities": [], - "enabled": true, "lookbackDuration": "5h" } } @@ -6661,7 +6661,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscalePremiumSubnetRouterThroughputAnomaly_AnalyticalRules Analytics Rule with template version 3.0.3", + "description": "TailscalePremiumSubnetRouterThroughputAnomaly_AnalyticalRules Analytics Rule with template version 3.0.4", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject20').analyticRuleVersion20]", @@ -6689,10 +6689,10 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscalePremiumCCF", "dataTypes": [ "Tailscale_Network_CL" - ] + ], + "connectorId": "TailscalePremiumCCF" } ], "tactics": [ @@ -6705,22 +6705,22 @@ ], "entityMappings": [ { + "entityType": "Host", "fieldMappings": [ { - "columnName": "SrcNodeName", - "identifier": "HostName" + "identifier": "HostName", + "columnName": "SrcNodeName" } - ], - "entityType": "Host" + ] } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { + "enabled": true, + "groupByEntities": [], "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "groupByEntities": [], - "enabled": true, "lookbackDuration": "1d" } } @@ -6777,7 +6777,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscalePremiumPostureIntegrationDisabled_AnalyticalRules Analytics Rule with template version 3.0.3", + "description": "TailscalePremiumPostureIntegrationDisabled_AnalyticalRules Analytics Rule with template version 3.0.4", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject21').analyticRuleVersion21]", @@ -6805,10 +6805,10 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscalePremiumCCF", "dataTypes": [ "Tailscale_Audit_CL" - ] + ], + "connectorId": "TailscalePremiumCCF" } ], "tactics": [ @@ -6821,22 +6821,22 @@ ], "entityMappings": [ { + "entityType": "Account", "fieldMappings": [ { - "columnName": "ActorLogin", - "identifier": "FullName" + "identifier": "FullName", + "columnName": "ActorLogin" } - ], - "entityType": "Account" + ] } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { + "enabled": true, + "groupByEntities": [], "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "groupByEntities": [], - "enabled": true, "lookbackDuration": "1d" } } @@ -6893,7 +6893,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscalePremiumNewPostureIntegration_AnalyticalRules Analytics Rule with template version 3.0.3", + "description": "TailscalePremiumNewPostureIntegration_AnalyticalRules Analytics Rule with template version 3.0.4", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject22').analyticRuleVersion22]", @@ -6921,10 +6921,10 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscalePremiumCCF", "dataTypes": [ "Tailscale_Audit_CL" - ] + ], + "connectorId": "TailscalePremiumCCF" } ], "tactics": [ @@ -6935,22 +6935,22 @@ ], "entityMappings": [ { + "entityType": "Account", "fieldMappings": [ { - "columnName": "ActorLogin", - "identifier": "FullName" + "identifier": "FullName", + "columnName": "ActorLogin" } - ], - "entityType": "Account" + ] } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { + "enabled": true, + "groupByEntities": [], "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "groupByEntities": [], - "enabled": true, "lookbackDuration": "1d" } } @@ -7007,7 +7007,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleFirstSeenActor_HuntingQueries Hunting Query with template version 3.0.3", + "description": "TailscaleFirstSeenActor_HuntingQueries Hunting Query with template version 3.0.4", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('huntingQueryObject1').huntingQueryVersion1]", @@ -7092,7 +7092,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleACLPolicyChurn_HuntingQueries Hunting Query with template version 3.0.3", + "description": "TailscaleACLPolicyChurn_HuntingQueries Hunting Query with template version 3.0.4", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('huntingQueryObject2').huntingQueryVersion2]", @@ -7177,7 +7177,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleOffHoursConfigChanges_HuntingQueries Hunting Query with template version 3.0.3", + "description": "TailscaleOffHoursConfigChanges_HuntingQueries Hunting Query with template version 3.0.4", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('huntingQueryObject3').huntingQueryVersion3]", @@ -7262,7 +7262,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleAuthKeySprawl_HuntingQueries Hunting Query with template version 3.0.3", + "description": "TailscaleAuthKeySprawl_HuntingQueries Hunting Query with template version 3.0.4", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('huntingQueryObject4').huntingQueryVersion4]", @@ -7347,7 +7347,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleDormantDevices_HuntingQueries Hunting Query with template version 3.0.3", + "description": "TailscaleDormantDevices_HuntingQueries Hunting Query with template version 3.0.4", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('huntingQueryObject5').huntingQueryVersion5]", @@ -7432,7 +7432,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleAuthKeysNoExpiry_HuntingQueries Hunting Query with template version 3.0.3", + "description": "TailscaleAuthKeysNoExpiry_HuntingQueries Hunting Query with template version 3.0.4", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('huntingQueryObject6').huntingQueryVersion6]", @@ -7517,7 +7517,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleOrphanedUsers_HuntingQueries Hunting Query with template version 3.0.3", + "description": "TailscaleOrphanedUsers_HuntingQueries Hunting Query with template version 3.0.4", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('huntingQueryObject7').huntingQueryVersion7]", @@ -7602,7 +7602,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleSplitDnsPerDomainChanges_HuntingQueries Hunting Query with template version 3.0.3", + "description": "TailscaleSplitDnsPerDomainChanges_HuntingQueries Hunting Query with template version 3.0.4", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('huntingQueryObject8').huntingQueryVersion8]", @@ -7687,7 +7687,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleDevicesWithSshEnabled_HuntingQueries Hunting Query with template version 3.0.3", + "description": "TailscaleDevicesWithSshEnabled_HuntingQueries Hunting Query with template version 3.0.4", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('huntingQueryObject9').huntingQueryVersion9]", @@ -7772,7 +7772,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleExternalDeviceInventory_HuntingQueries Hunting Query with template version 3.0.3", + "description": "TailscaleExternalDeviceInventory_HuntingQueries Hunting Query with template version 3.0.4", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('huntingQueryObject10').huntingQueryVersion10]", @@ -7857,7 +7857,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleOutdatedClients_HuntingQueries Hunting Query with template version 3.0.3", + "description": "TailscaleOutdatedClients_HuntingQueries Hunting Query with template version 3.0.4", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('huntingQueryObject11').huntingQueryVersion11]", @@ -7942,7 +7942,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleSubnetRouteExposure_HuntingQueries Hunting Query with template version 3.0.3", + "description": "TailscaleSubnetRouteExposure_HuntingQueries Hunting Query with template version 3.0.4", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('huntingQueryObject12').huntingQueryVersion12]", @@ -8027,7 +8027,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscalePremiumNewNodePairs_HuntingQueries Hunting Query with template version 3.0.3", + "description": "TailscalePremiumNewNodePairs_HuntingQueries Hunting Query with template version 3.0.4", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('huntingQueryObject13').huntingQueryVersion13]", @@ -8112,7 +8112,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscalePremiumTopTalkers_HuntingQueries Hunting Query with template version 3.0.3", + "description": "TailscalePremiumTopTalkers_HuntingQueries Hunting Query with template version 3.0.4", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('huntingQueryObject14').huntingQueryVersion14]", @@ -8197,7 +8197,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscalePremiumExitNodeUsage_HuntingQueries Hunting Query with template version 3.0.3", + "description": "TailscalePremiumExitNodeUsage_HuntingQueries Hunting Query with template version 3.0.4", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('huntingQueryObject15').huntingQueryVersion15]", @@ -8282,7 +8282,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscalePremiumBeaconingCandidates_HuntingQueries Hunting Query with template version 3.0.3", + "description": "TailscalePremiumBeaconingCandidates_HuntingQueries Hunting Query with template version 3.0.4", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('huntingQueryObject16').huntingQueryVersion16]", @@ -8367,7 +8367,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscalePremiumPostureInventory_HuntingQueries Hunting Query with template version 3.0.3", + "description": "TailscalePremiumPostureInventory_HuntingQueries Hunting Query with template version 3.0.4", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('huntingQueryObject17').huntingQueryVersion17]", @@ -8452,7 +8452,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleStandardOperations Workbook with template version 3.0.3", + "description": "TailscaleStandardOperations Workbook with template version 3.0.4", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('workbookVersion1')]", @@ -8564,7 +8564,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscalePremiumOperations Workbook with template version 3.0.3", + "description": "TailscalePremiumOperations Workbook with template version 3.0.4", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('workbookVersion2')]", @@ -8680,7 +8680,7 @@ "apiVersion": "2023-04-01-preview", "location": "[parameters('workspace-location')]", "properties": { - "version": "3.0.3", + "version": "3.0.4", "kind": "Solution", "contentSchemaVersion": "3.0.0", "displayName": "Tailscale (CCF)", From ccb8266c1839086de8e9ec7ed8ef87a3dd5795d4 Mon Sep 17 00:00:00 2001 From: noodlemctwoodle Date: Wed, 13 May 2026 16:16:34 +0100 Subject: [PATCH 15/27] feat(tailscale): rebuild Standard workbook with 9-tab forensic UX Replace the existing 8-tab Tailscale Operations (Standard) workbook with a substantially richer 9-tab version that mirrors the design of the UniFi Syslog (CCF) workbook shipped on add-unifi-syslog-ccf-solution. The Premium workbook is left unchanged - parked until we have a Tailscale Premium licence to validate against. What changed: - New Standard workbook has a persistent hero KPI tile row above the tabs (Devices, Authorized, Updates Available, SSH-Enabled, Users, Admins, Active Keys, Audit Events) so the most important numbers are always in view regardless of which tab is open. - Investigate tab added: picker-driven drilldown with an Actor dropdown (sourced from Tailscale_Audit_CL Actor.loginName, top 50 by event count) and a Device dropdown (sourced from Tailscale_Devices_CL latest snapshot, top 100 by LastSeen). The Device picker uses DeviceName not DeviceId because the audit log emits Target.id in stable-control-plane ID format while the API emits a different numeric DeviceId - the only reliable join key between the two streams is the dotted device hostname. Audit events touching a device are filtered on Target.type == "NODE" (not "DEVICE", which is what the previous draft tried - Tailscale's actual enum is NODE). - Hunts tab embeds 8 of the 17 packaged hunting queries inline so the user can see results without navigating to the Hunts blade: first-seen actors (24h vs 30d baseline), off-hours config changes, key-expiry-disabled devices, never-expire auth keys, outdated clients, dormant devices, subnet route exposure, SSH-enabled devices. Each panel has a NO_DATA message explaining what zero results means. - Identity tab adds a KPI tile row (total users, admin-tier, active, connected-now, idle/dormant, shared/external) plus a recency-of-last- login bucketed barchart (Today / This week / This month / Past quarter / 90+ days), orphaned-users table (active accounts with zero devices), and a role-change history pulled from audit Action == USER_ROLE_UPDATE. - Devices tab adds a "needs attention" pre-filter panel that flags devices with one or more issues across: update-available, key-never- expires, stale (>30d LastSeen), unauthorized. Each device's issue list renders as a tag chip column. Full inventory remains. - Credentials tab adds an active-key expiry distribution barchart bucketed Already expired / <24h / 1-7d / 8-30d / 31-90d / 90+d / Never, plus a computed ExpiryStatus tag column on the active credential register. - Admin Audit tab adds a hour-of-day x action categorical bar showing when admin work happens, and an actor x action heatmap (formatter 4, blue palette) on top of the existing event log. - Network & DNS tab consolidates the previous DNS + Webhooks + signals bits into one place: current DNS snapshot (MagicDNS, nameservers, search paths), tailnet policy gates (DevicesApproval, AutoUpdates, KeyDurationDays etc.), DNS change history, ACL/policy change history, and the currently-served subnet/exit-node routes. - Pipeline Health tab is new: rows-per-hour-per-table timechart, per-table freshness with MinutesAgo bar coloured red, and a _LogOperation filter for Tailscale-touching operational events. Helps the user spot when a stream has stopped polling. - Premium-only queries (Tailscale_PostureIntegrations_CL, Tailscale_Network_CL) are stripped entirely from the Standard workbook rather than left as NO_DATA placeholders. They belong on the parked Premium workbook only. - Header banner updated: workbook title becomes "Tailscale Operations (Standard)" with a deep-link reference to the Premium companion workbook for Premium-tier customers. - 50+ KQL queries gain noDataMessage strings explaining what zero rows mean in context (e.g. "Healthy state for an organisation working business hours") rather than leaving empty panels. - Formatters added throughout: heatmap (formatter 4) on the actor x action grid, bar (formatter 8) for count columns, tag chip (formatter 11) for OS / Role / Status / Action / Issues columns, datetime (formatter 6) for TimeGenerated / LastSeen / Created columns. Files modified: - Solutions/Tailscale (CCF)/Workbooks/TailscaleStandardOperations.json - Rebuilt from scratch (52 queries across 9 tab groups, 88KB) - Solutions/Tailscale (CCF)/Data/Solution_Tailscale.json - Version 3.0.4 -> 3.0.5 - BasePath updated to absolute path for local createSolutionV3 builds - Solutions/Tailscale (CCF)/Package/3.0.5.zip - Solutions/Tailscale (CCF)/Package/mainTemplate.json - Solutions/Tailscale (CCF)/Package/createUiDefinition.json - Regenerated by createSolutionV3.ps1 / build-and-validate.ps1 wrapper - Solutions/Tailscale (CCF)/Package/3.0.4.zip - Removed (stale, superseded by 3.0.5) - Workbooks/WorkbooksMetadata.json - Surgical 1-line update to the TailscaleStandardOperationsWorkbook entry's description field. LF-preserving byte edit. Premium entry untouched. Note on the Premium workbook: - Solutions/Tailscale (CCF)/Workbooks/TailscalePremiumOperations.json is intentionally NOT touched by this commit. Premium tier requires network flow logs + posture-integration API access which is gated on a Tailscale Premium / Enterprise plan. The existing parked Premium workbook ships unchanged; we'll rebuild it to the same standard once Tailscale grant Premium access. Technical / data shape findings (worth recording for future iteration): - Tailscale_Dns_CL is a UNIFIED table fanned in from three DCR input streams (DnsNameservers, DnsPreferences, DnsSearchPaths). Queries pivot on ConfigType to read the right shape. - Tailscale audit Target.type enum values present in real data: OAUTH_ACCESS_TOKEN (319), NODE (11), API_KEY (7), TAILNET (6). The previous draft assumed DEVICE - that value doesn't exist. - Tailscale audit Target.id for NODE events is the stable control-plane ID (alphanumeric with CNTRL suffix, e.g. nGddQEhHzh11CNTRL). It does NOT match Tailscale_Devices_CL.DeviceId (which is the numeric API ID, e.g. 4195069007596891). The only reliable join key between the two streams is Target.name == DeviceName (the dotted hostname). - Tailscale_PostureIntegrations_CL schema columns are: IntegrationId, Provider, CloudId, ClientId, TenantId_Provider, ConfigOverwrites, Status - NOT ConfiguredBy (which the previous draft incorrectly assumed). Only relevant to the Premium workbook. Testing performed: - All 52 queries validated against the live test workspace (stl-sec-siem-law, uksouth, 2 days of real Tailscale audit + devices + users + keys + DNS + settings data from tailb094d7.ts.net). Result: 42 pass / 10 empty (all covered by NO_DATA messages) / 0 fail. - Workbook deployed via REST to stl-sec-siem-law for visual rendering verification (Microsoft.Insights/workbooks resource, category sentinel). Visual review confirmed tile rows render, picker dropdowns populate, NO_DATA messages display in expected panels, formatters apply correctly. - build-and-validate.ps1 wrapper run: - KQL validation: pass (47 files) - ARM-TTK: 48 / 48 pass - Field Types: pass - Classic App Insights: pass - Hyperlink Validation: 1 expected pre-merge failure (Logos/Tailscale.svg URL at master not resolving because branch not merged yet - same false-positive every new-solution PR hits; resolves itself post-merge) - .NET-based Detection Schema, .NET-based Non-ASCII, and TruffleHog skipped (.NET Core 3.1 / TruffleHog CLI not installed locally) - substituted with Python ASCII scan + manual rule checks. - Privacy grep on the workbook JSON: clean (no real tenant IDs, real device hostnames, IP addresses, OAuth tokens, etc.) Breaking changes: none. The Standard workbook is a drop-in replacement in the existing templateRelativePath slot (TailscaleStandardOperations.json). The Premium workbook is unchanged. Solution-level dependencies, Solution Hub install flow, and DCR / data connector resources are unaffected. --- .../Data/Solution_Tailscale.json | 4 +- Solutions/Tailscale (CCF)/Package/3.0.4.zip | Bin 61348 -> 0 bytes Solutions/Tailscale (CCF)/Package/3.0.5.zip | Bin 0 -> 66176 bytes .../Package/createUiDefinition.json | 2 +- .../Tailscale (CCF)/Package/mainTemplate.json | 570 +++---- .../TailscaleStandardOperations.json | 1406 +++++++++++++---- Workbooks/WorkbooksMetadata.json | 2 +- 7 files changed, 1382 insertions(+), 602 deletions(-) delete mode 100644 Solutions/Tailscale (CCF)/Package/3.0.4.zip create mode 100644 Solutions/Tailscale (CCF)/Package/3.0.5.zip diff --git a/Solutions/Tailscale (CCF)/Data/Solution_Tailscale.json b/Solutions/Tailscale (CCF)/Data/Solution_Tailscale.json index df769cb868a..08066805d99 100644 --- a/Solutions/Tailscale (CCF)/Data/Solution_Tailscale.json +++ b/Solutions/Tailscale (CCF)/Data/Solution_Tailscale.json @@ -3,8 +3,8 @@ "Author": "noodlemctwoodle - ccfconnectors.county118@passmail.com", "Logo": "", "Description": "The [Tailscale](https://tailscale.com/) solution for Microsoft Sentinel ingests Tailscale identity, device, configuration, audit and (Premium) network-flow telemetry via OAuth2-secured APIs. Built on the Codeless Connector Framework (CCF) - no Function App or container required.\n\n**Data connectors in this solution (install the one matching your Tailscale plan):**\n- **Tailscale Standard (CCF)** - Configuration audit, devices, users, keys, webhooks, DNS, settings. Use on **Personal (Free), Starter and Premium** tailnets.\n- **Tailscale Premium (CCF)** - Everything in Standard plus network flow logs and posture integrations. Use on **Premium and Enterprise** tailnets for full coverage.\n\n**Pre-requisites:**\n1. Sign in to [Tailscale OAuth settings](https://login.tailscale.com/admin/settings/oauth)\n2. Create an OAuth client with the scopes for your tier (see the README in this solution).\n3. Copy the client ID and client secret (secret shown once).\n4. Note your tailnet name (e.g. `tailb094d7.ts.net`) from the [Keys page](https://login.tailscale.com/admin/settings/keys).", - "BasePath": "C:\\GitHub\\Azure-Sentinel\\Solutions\\Tailscale (CCF)", - "Version": "3.0.4", + "BasePath": "/Users/tobygoulden/Source/_Personal_GH/Azure-Sentinel-fork-tailscale/Solutions/Tailscale (CCF)", + "Version": "3.0.5", "TemplateSpec": false, "Is1PConnector": false, "Data Connectors": [ diff --git a/Solutions/Tailscale (CCF)/Package/3.0.4.zip b/Solutions/Tailscale (CCF)/Package/3.0.4.zip deleted file mode 100644 index 1f7a3ab375a843676c4d7c139c15fc330b8940df..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 61348 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

T5$mLug|=k8?Kc6hAd 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 MGwACIbNP?@U)3+|WdHyG diff --git a/Solutions/Tailscale (CCF)/Package/3.0.5.zip b/Solutions/Tailscale (CCF)/Package/3.0.5.zip new file mode 100644 index 0000000000000000000000000000000000000000..d4391e580d09a10b08a5da210724806e04de8d36 GIT binary patch literal 66176 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 F{vTaTPH6xD literal 0 HcmV?d00001 diff --git a/Solutions/Tailscale (CCF)/Package/createUiDefinition.json b/Solutions/Tailscale (CCF)/Package/createUiDefinition.json index e06e3688ad1..5250bfcff4e 100644 --- a/Solutions/Tailscale (CCF)/Package/createUiDefinition.json +++ b/Solutions/Tailscale (CCF)/Package/createUiDefinition.json @@ -127,7 +127,7 @@ "name": "workbook1-text", "type": "Microsoft.Common.TextBlock", "options": { - "text": "Tailscale Operations workbook for Standard tier - device inventory, user roles, auth keys, DNS configuration, audit activity, and tailnet health overview." + "text": "Tailscale Operations workbook for Standard tier. Nine tabs covering an at-a-glance KPI hero row, audit activity overview, an actor + device drilldown (Investigate), embedded hunting queries (first-seen actors, off-hours changes, key-expiry-disabled devices, never-expire auth keys, outdated clients, dormant devices, subnet route exposure, SSH-enabled devices), identity (user roles, status, last login recency, role escalation history, orphaned users), devices (OS / version / tag distribution, devices needing attention, full inventory, subnet routers), credentials (expiry timeline, never-expire flag, CRUD events), admin audit (action heatmap, actor x action heatmap, recent 100), network and DNS (current snapshot, tailnet policy gates, ACL change history), and pipeline health (per-table freshness, ingest rate, operational events). Driven by data polled from the Tailscale REST API." } } ] diff --git a/Solutions/Tailscale (CCF)/Package/mainTemplate.json b/Solutions/Tailscale (CCF)/Package/mainTemplate.json index 515946da2f7..5bcdc249d94 100644 --- a/Solutions/Tailscale (CCF)/Package/mainTemplate.json +++ b/Solutions/Tailscale (CCF)/Package/mainTemplate.json @@ -63,11 +63,11 @@ "email": "ccfconnectors.county118@passmail.com", "_email": "[variables('email')]", "_solutionName": "Tailscale (CCF)", - "_solutionVersion": "3.0.4", + "_solutionVersion": "3.0.5", "solutionId": "noodlemctwoodle.azure-sentinel-solution-tailscale-ccf", "_solutionId": "[variables('solutionId')]", "workspaceResourceId": "[resourceId('microsoft.OperationalInsights/Workspaces', parameters('workspace'))]", - "dataConnectorCCPVersion": "3.0.4", + "dataConnectorCCPVersion": "3.0.5", "_dataConnectorContentIdConnectorDefinition1": "TailscaleCCF", "dataConnectorTemplateNameConnectorDefinition1": "[concat(parameters('workspace'),'-dc-',uniquestring(variables('_dataConnectorContentIdConnectorDefinition1')))]", "_dataConnectorContentIdConnections1": "TailscaleCCFConnections", @@ -4376,7 +4376,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleNewAPIaccesstokenorOAuthclientcreated_AnalyticalRules Analytics Rule with template version 3.0.4", + "description": "TailscaleNewAPIaccesstokenorOAuthclientcreated_AnalyticalRules Analytics Rule with template version 3.0.5", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject1').analyticRuleVersion1]", @@ -4404,10 +4404,10 @@ "status": "Available", "requiredDataConnectors": [ { + "connectorId": "TailscaleCCF", "dataTypes": [ "Tailscale_Audit_CL" - ], - "connectorId": "TailscaleCCF" + ] } ], "tactics": [ @@ -4420,23 +4420,23 @@ ], "entityMappings": [ { - "entityType": "Account", "fieldMappings": [ { - "identifier": "FullName", - "columnName": "ActorLogin" + "columnName": "ActorLogin", + "identifier": "FullName" } - ] + ], + "entityType": "Account" } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { "enabled": true, - "groupByEntities": [], - "reopenClosedIncident": false, + "lookbackDuration": "5h", "matchingMethod": "AllEntities", - "lookbackDuration": "5h" + "reopenClosedIncident": false, + "groupByEntities": [] } } } @@ -4492,7 +4492,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscalePolicyfileACLmodified_AnalyticalRules Analytics Rule with template version 3.0.4", + "description": "TailscalePolicyfileACLmodified_AnalyticalRules Analytics Rule with template version 3.0.5", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject2').analyticRuleVersion2]", @@ -4520,10 +4520,10 @@ "status": "Available", "requiredDataConnectors": [ { + "connectorId": "TailscaleCCF", "dataTypes": [ "Tailscale_Audit_CL" - ], - "connectorId": "TailscaleCCF" + ] } ], "tactics": [ @@ -4535,23 +4535,23 @@ ], "entityMappings": [ { - "entityType": "Account", "fieldMappings": [ { - "identifier": "FullName", - "columnName": "ActorLogin" + "columnName": "ActorLogin", + "identifier": "FullName" } - ] + ], + "entityType": "Account" } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { "enabled": true, - "groupByEntities": [], - "reopenClosedIncident": false, + "lookbackDuration": "5h", "matchingMethod": "AllEntities", - "lookbackDuration": "5h" + "reopenClosedIncident": false, + "groupByEntities": [] } } } @@ -4607,7 +4607,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleAuthkeycreated_AnalyticalRules Analytics Rule with template version 3.0.4", + "description": "TailscaleAuthkeycreated_AnalyticalRules Analytics Rule with template version 3.0.5", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject3').analyticRuleVersion3]", @@ -4635,10 +4635,10 @@ "status": "Available", "requiredDataConnectors": [ { + "connectorId": "TailscaleCCF", "dataTypes": [ "Tailscale_Audit_CL" - ], - "connectorId": "TailscaleCCF" + ] } ], "tactics": [ @@ -4649,23 +4649,23 @@ ], "entityMappings": [ { - "entityType": "Account", "fieldMappings": [ { - "identifier": "FullName", - "columnName": "ActorLogin" + "columnName": "ActorLogin", + "identifier": "FullName" } - ] + ], + "entityType": "Account" } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { "enabled": true, - "groupByEntities": [], - "reopenClosedIncident": false, + "lookbackDuration": "5h", "matchingMethod": "AllEntities", - "lookbackDuration": "5h" + "reopenClosedIncident": false, + "groupByEntities": [] } } } @@ -4721,7 +4721,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleExitnodeadvertisedorapproved_AnalyticalRules Analytics Rule with template version 3.0.4", + "description": "TailscaleExitnodeadvertisedorapproved_AnalyticalRules Analytics Rule with template version 3.0.5", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject4').analyticRuleVersion4]", @@ -4749,10 +4749,10 @@ "status": "Available", "requiredDataConnectors": [ { + "connectorId": "TailscaleCCF", "dataTypes": [ "Tailscale_Audit_CL" - ], - "connectorId": "TailscaleCCF" + ] } ], "tactics": [ @@ -4764,32 +4764,32 @@ ], "entityMappings": [ { - "entityType": "Account", "fieldMappings": [ { - "identifier": "FullName", - "columnName": "ActorLogin" + "columnName": "ActorLogin", + "identifier": "FullName" } - ] + ], + "entityType": "Account" }, { - "entityType": "Host", "fieldMappings": [ { - "identifier": "HostName", - "columnName": "NodeName" + "columnName": "NodeName", + "identifier": "HostName" } - ] + ], + "entityType": "Host" } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { "enabled": true, - "groupByEntities": [], - "reopenClosedIncident": false, + "lookbackDuration": "5h", "matchingMethod": "AllEntities", - "lookbackDuration": "5h" + "reopenClosedIncident": false, + "groupByEntities": [] } } } @@ -4845,7 +4845,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleMasscredentialrevocationinshortwindow_AnalyticalRules Analytics Rule with template version 3.0.4", + "description": "TailscaleMasscredentialrevocationinshortwindow_AnalyticalRules Analytics Rule with template version 3.0.5", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject5').analyticRuleVersion5]", @@ -4873,10 +4873,10 @@ "status": "Available", "requiredDataConnectors": [ { + "connectorId": "TailscaleCCF", "dataTypes": [ "Tailscale_Audit_CL" - ], - "connectorId": "TailscaleCCF" + ] } ], "tactics": [ @@ -4888,23 +4888,23 @@ ], "entityMappings": [ { - "entityType": "Account", "fieldMappings": [ { - "identifier": "FullName", - "columnName": "ActorLogin" + "columnName": "ActorLogin", + "identifier": "FullName" } - ] + ], + "entityType": "Account" } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { "enabled": true, - "groupByEntities": [], - "reopenClosedIncident": false, + "lookbackDuration": "5h", "matchingMethod": "AllEntities", - "lookbackDuration": "5h" + "reopenClosedIncident": false, + "groupByEntities": [] } } } @@ -4960,7 +4960,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleDeviceKeyExpiringSoon_AnalyticalRules Analytics Rule with template version 3.0.4", + "description": "TailscaleDeviceKeyExpiringSoon_AnalyticalRules Analytics Rule with template version 3.0.5", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject6').analyticRuleVersion6]", @@ -4988,10 +4988,10 @@ "status": "Available", "requiredDataConnectors": [ { + "connectorId": "TailscaleCCF", "dataTypes": [ "Tailscale_Devices_CL" - ], - "connectorId": "TailscaleCCF" + ] } ], "tactics": [ @@ -5002,32 +5002,32 @@ ], "entityMappings": [ { - "entityType": "Host", "fieldMappings": [ { - "identifier": "HostName", - "columnName": "Hostname" + "columnName": "Hostname", + "identifier": "HostName" } - ] + ], + "entityType": "Host" }, { - "entityType": "Account", "fieldMappings": [ { - "identifier": "FullName", - "columnName": "User" + "columnName": "User", + "identifier": "FullName" } - ] + ], + "entityType": "Account" } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { "enabled": true, - "groupByEntities": [], - "reopenClosedIncident": false, + "lookbackDuration": "1d", "matchingMethod": "AllEntities", - "lookbackDuration": "1d" + "reopenClosedIncident": false, + "groupByEntities": [] } } } @@ -5083,7 +5083,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleDeviceAdvertisingSubnetRoutes_AnalyticalRules Analytics Rule with template version 3.0.4", + "description": "TailscaleDeviceAdvertisingSubnetRoutes_AnalyticalRules Analytics Rule with template version 3.0.5", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject7').analyticRuleVersion7]", @@ -5111,10 +5111,10 @@ "status": "Available", "requiredDataConnectors": [ { + "connectorId": "TailscaleCCF", "dataTypes": [ "Tailscale_Devices_CL" - ], - "connectorId": "TailscaleCCF" + ] } ], "tactics": [ @@ -5127,32 +5127,32 @@ ], "entityMappings": [ { - "entityType": "Host", "fieldMappings": [ { - "identifier": "HostName", - "columnName": "Hostname" + "columnName": "Hostname", + "identifier": "HostName" } - ] + ], + "entityType": "Host" }, { - "entityType": "Account", "fieldMappings": [ { - "identifier": "FullName", - "columnName": "User" + "columnName": "User", + "identifier": "FullName" } - ] + ], + "entityType": "Account" } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { "enabled": true, - "groupByEntities": [], - "reopenClosedIncident": false, + "lookbackDuration": "1d", "matchingMethod": "AllEntities", - "lookbackDuration": "1d" + "reopenClosedIncident": false, + "groupByEntities": [] } } } @@ -5208,7 +5208,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleUserRoleElevated_AnalyticalRules Analytics Rule with template version 3.0.4", + "description": "TailscaleUserRoleElevated_AnalyticalRules Analytics Rule with template version 3.0.5", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject8').analyticRuleVersion8]", @@ -5236,10 +5236,10 @@ "status": "Available", "requiredDataConnectors": [ { + "connectorId": "TailscaleCCF", "dataTypes": [ "Tailscale_Users_CL" - ], - "connectorId": "TailscaleCCF" + ] } ], "tactics": [ @@ -5252,23 +5252,23 @@ ], "entityMappings": [ { - "entityType": "Account", "fieldMappings": [ { - "identifier": "FullName", - "columnName": "LoginName" + "columnName": "LoginName", + "identifier": "FullName" } - ] + ], + "entityType": "Account" } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { "enabled": true, - "groupByEntities": [], - "reopenClosedIncident": false, + "lookbackDuration": "1d", "matchingMethod": "AllEntities", - "lookbackDuration": "1d" + "reopenClosedIncident": false, + "groupByEntities": [] } } } @@ -5324,7 +5324,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleSplitDnsModified_AnalyticalRules Analytics Rule with template version 3.0.4", + "description": "TailscaleSplitDnsModified_AnalyticalRules Analytics Rule with template version 3.0.5", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject9').analyticRuleVersion9]", @@ -5352,10 +5352,10 @@ "status": "Available", "requiredDataConnectors": [ { + "connectorId": "TailscaleCCF", "dataTypes": [ "Tailscale_Audit_CL" - ], - "connectorId": "TailscaleCCF" + ] } ], "tactics": [ @@ -5368,23 +5368,23 @@ ], "entityMappings": [ { - "entityType": "Account", "fieldMappings": [ { - "identifier": "FullName", - "columnName": "ActorLogin" + "columnName": "ActorLogin", + "identifier": "FullName" } - ] + ], + "entityType": "Account" } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { "enabled": true, - "groupByEntities": [], - "reopenClosedIncident": false, + "lookbackDuration": "1d", "matchingMethod": "AllEntities", - "lookbackDuration": "1d" + "reopenClosedIncident": false, + "groupByEntities": [] } } } @@ -5440,7 +5440,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleDnsNameserversModified_AnalyticalRules Analytics Rule with template version 3.0.4", + "description": "TailscaleDnsNameserversModified_AnalyticalRules Analytics Rule with template version 3.0.5", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject10').analyticRuleVersion10]", @@ -5468,10 +5468,10 @@ "status": "Available", "requiredDataConnectors": [ { + "connectorId": "TailscaleCCF", "dataTypes": [ "Tailscale_Audit_CL" - ], - "connectorId": "TailscaleCCF" + ] } ], "tactics": [ @@ -5484,23 +5484,23 @@ ], "entityMappings": [ { - "entityType": "Account", "fieldMappings": [ { - "identifier": "FullName", - "columnName": "ActorLogin" + "columnName": "ActorLogin", + "identifier": "FullName" } - ] + ], + "entityType": "Account" } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { "enabled": true, - "groupByEntities": [], - "reopenClosedIncident": false, + "lookbackDuration": "1d", "matchingMethod": "AllEntities", - "lookbackDuration": "1d" + "reopenClosedIncident": false, + "groupByEntities": [] } } } @@ -5556,7 +5556,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleMagicDnsDisabled_AnalyticalRules Analytics Rule with template version 3.0.4", + "description": "TailscaleMagicDnsDisabled_AnalyticalRules Analytics Rule with template version 3.0.5", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject11').analyticRuleVersion11]", @@ -5584,10 +5584,10 @@ "status": "Available", "requiredDataConnectors": [ { + "connectorId": "TailscaleCCF", "dataTypes": [ "Tailscale_Audit_CL" - ], - "connectorId": "TailscaleCCF" + ] } ], "tactics": [ @@ -5598,23 +5598,23 @@ ], "entityMappings": [ { - "entityType": "Account", "fieldMappings": [ { - "identifier": "FullName", - "columnName": "ActorLogin" + "columnName": "ActorLogin", + "identifier": "FullName" } - ] + ], + "entityType": "Account" } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { "enabled": true, - "groupByEntities": [], - "reopenClosedIncident": false, + "lookbackDuration": "1d", "matchingMethod": "AllEntities", - "lookbackDuration": "1d" + "reopenClosedIncident": false, + "groupByEntities": [] } } } @@ -5670,7 +5670,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleTailnetLockValidationFailed_AnalyticalRules Analytics Rule with template version 3.0.4", + "description": "TailscaleTailnetLockValidationFailed_AnalyticalRules Analytics Rule with template version 3.0.5", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject12').analyticRuleVersion12]", @@ -5698,10 +5698,10 @@ "status": "Available", "requiredDataConnectors": [ { + "connectorId": "TailscaleCCF", "dataTypes": [ "Tailscale_Devices_CL" - ], - "connectorId": "TailscaleCCF" + ] } ], "tactics": [ @@ -5714,32 +5714,32 @@ ], "entityMappings": [ { - "entityType": "Host", "fieldMappings": [ { - "identifier": "HostName", - "columnName": "Hostname" + "columnName": "Hostname", + "identifier": "HostName" } - ] + ], + "entityType": "Host" }, { - "entityType": "Account", "fieldMappings": [ { - "identifier": "FullName", - "columnName": "User" + "columnName": "User", + "identifier": "FullName" } - ] + ], + "entityType": "Account" } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { "enabled": true, - "groupByEntities": [], - "reopenClosedIncident": false, + "lookbackDuration": "1d", "matchingMethod": "AllEntities", - "lookbackDuration": "1d" + "reopenClosedIncident": false, + "groupByEntities": [] } } } @@ -5795,7 +5795,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleDeviceSshNewlyEnabled_AnalyticalRules Analytics Rule with template version 3.0.4", + "description": "TailscaleDeviceSshNewlyEnabled_AnalyticalRules Analytics Rule with template version 3.0.5", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject13').analyticRuleVersion13]", @@ -5823,10 +5823,10 @@ "status": "Available", "requiredDataConnectors": [ { + "connectorId": "TailscaleCCF", "dataTypes": [ "Tailscale_Devices_CL" - ], - "connectorId": "TailscaleCCF" + ] } ], "tactics": [ @@ -5839,32 +5839,32 @@ ], "entityMappings": [ { - "entityType": "Host", "fieldMappings": [ { - "identifier": "HostName", - "columnName": "Hostname" + "columnName": "Hostname", + "identifier": "HostName" } - ] + ], + "entityType": "Host" }, { - "entityType": "Account", "fieldMappings": [ { - "identifier": "FullName", - "columnName": "User" + "columnName": "User", + "identifier": "FullName" } - ] + ], + "entityType": "Account" } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { "enabled": true, - "groupByEntities": [], - "reopenClosedIncident": false, + "lookbackDuration": "1d", "matchingMethod": "AllEntities", - "lookbackDuration": "1d" + "reopenClosedIncident": false, + "groupByEntities": [] } } } @@ -5920,7 +5920,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleUnauthorizedDeviceConnected_AnalyticalRules Analytics Rule with template version 3.0.4", + "description": "TailscaleUnauthorizedDeviceConnected_AnalyticalRules Analytics Rule with template version 3.0.5", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject14').analyticRuleVersion14]", @@ -5948,10 +5948,10 @@ "status": "Available", "requiredDataConnectors": [ { + "connectorId": "TailscaleCCF", "dataTypes": [ "Tailscale_Devices_CL" - ], - "connectorId": "TailscaleCCF" + ] } ], "tactics": [ @@ -5964,32 +5964,32 @@ ], "entityMappings": [ { - "entityType": "Host", "fieldMappings": [ { - "identifier": "HostName", - "columnName": "Hostname" + "columnName": "Hostname", + "identifier": "HostName" } - ] + ], + "entityType": "Host" }, { - "entityType": "Account", "fieldMappings": [ { - "identifier": "FullName", - "columnName": "User" + "columnName": "User", + "identifier": "FullName" } - ] + ], + "entityType": "Account" } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { "enabled": true, - "groupByEntities": [], - "reopenClosedIncident": false, + "lookbackDuration": "1d", "matchingMethod": "AllEntities", - "lookbackDuration": "1d" + "reopenClosedIncident": false, + "groupByEntities": [] } } } @@ -6045,7 +6045,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleExternalDeviceAdded_AnalyticalRules Analytics Rule with template version 3.0.4", + "description": "TailscaleExternalDeviceAdded_AnalyticalRules Analytics Rule with template version 3.0.5", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject15').analyticRuleVersion15]", @@ -6073,10 +6073,10 @@ "status": "Available", "requiredDataConnectors": [ { + "connectorId": "TailscaleCCF", "dataTypes": [ "Tailscale_Devices_CL" - ], - "connectorId": "TailscaleCCF" + ] } ], "tactics": [ @@ -6087,32 +6087,32 @@ ], "entityMappings": [ { - "entityType": "Host", "fieldMappings": [ { - "identifier": "HostName", - "columnName": "Hostname" + "columnName": "Hostname", + "identifier": "HostName" } - ] + ], + "entityType": "Host" }, { - "entityType": "Account", "fieldMappings": [ { - "identifier": "FullName", - "columnName": "User" + "columnName": "User", + "identifier": "FullName" } - ] + ], + "entityType": "Account" } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { "enabled": true, - "groupByEntities": [], - "reopenClosedIncident": false, + "lookbackDuration": "1d", "matchingMethod": "AllEntities", - "lookbackDuration": "1d" + "reopenClosedIncident": false, + "groupByEntities": [] } } } @@ -6168,7 +6168,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscalePremiumUnexpectedExitNodeEgress_AnalyticalRules Analytics Rule with template version 3.0.4", + "description": "TailscalePremiumUnexpectedExitNodeEgress_AnalyticalRules Analytics Rule with template version 3.0.5", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject16').analyticRuleVersion16]", @@ -6196,10 +6196,10 @@ "status": "Available", "requiredDataConnectors": [ { + "connectorId": "TailscalePremiumCCF", "dataTypes": [ "Tailscale_Network_CL" - ], - "connectorId": "TailscalePremiumCCF" + ] } ], "tactics": [ @@ -6212,32 +6212,32 @@ ], "entityMappings": [ { - "entityType": "Host", "fieldMappings": [ { - "identifier": "HostName", - "columnName": "SrcNodeName" + "columnName": "SrcNodeName", + "identifier": "HostName" } - ] + ], + "entityType": "Host" }, { - "entityType": "IP", "fieldMappings": [ { - "identifier": "Address", - "columnName": "Src" + "columnName": "Src", + "identifier": "Address" } - ] + ], + "entityType": "IP" } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { "enabled": true, - "groupByEntities": [], - "reopenClosedIncident": false, + "lookbackDuration": "5h", "matchingMethod": "AllEntities", - "lookbackDuration": "5h" + "reopenClosedIncident": false, + "groupByEntities": [] } } } @@ -6293,7 +6293,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscalePremiumLargeOutboundTransfer_AnalyticalRules Analytics Rule with template version 3.0.4", + "description": "TailscalePremiumLargeOutboundTransfer_AnalyticalRules Analytics Rule with template version 3.0.5", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject17').analyticRuleVersion17]", @@ -6321,10 +6321,10 @@ "status": "Available", "requiredDataConnectors": [ { + "connectorId": "TailscalePremiumCCF", "dataTypes": [ "Tailscale_Network_CL" - ], - "connectorId": "TailscalePremiumCCF" + ] } ], "tactics": [ @@ -6337,32 +6337,32 @@ ], "entityMappings": [ { - "entityType": "Host", "fieldMappings": [ { - "identifier": "HostName", - "columnName": "SrcNodeName" + "columnName": "SrcNodeName", + "identifier": "HostName" } - ] + ], + "entityType": "Host" }, { - "entityType": "IP", "fieldMappings": [ { - "identifier": "Address", - "columnName": "Src" + "columnName": "Src", + "identifier": "Address" } - ] + ], + "entityType": "IP" } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { "enabled": true, - "groupByEntities": [], - "reopenClosedIncident": false, + "lookbackDuration": "5h", "matchingMethod": "AllEntities", - "lookbackDuration": "5h" + "reopenClosedIncident": false, + "groupByEntities": [] } } } @@ -6418,7 +6418,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscalePremiumBeaconingDetected_AnalyticalRules Analytics Rule with template version 3.0.4", + "description": "TailscalePremiumBeaconingDetected_AnalyticalRules Analytics Rule with template version 3.0.5", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject18').analyticRuleVersion18]", @@ -6446,10 +6446,10 @@ "status": "Available", "requiredDataConnectors": [ { + "connectorId": "TailscalePremiumCCF", "dataTypes": [ "Tailscale_Network_CL" - ], - "connectorId": "TailscalePremiumCCF" + ] } ], "tactics": [ @@ -6463,23 +6463,23 @@ ], "entityMappings": [ { - "entityType": "IP", "fieldMappings": [ { - "identifier": "Address", - "columnName": "Src" + "columnName": "Src", + "identifier": "Address" } - ] + ], + "entityType": "IP" } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { "enabled": true, - "groupByEntities": [], - "reopenClosedIncident": false, + "lookbackDuration": "1d", "matchingMethod": "AllEntities", - "lookbackDuration": "1d" + "reopenClosedIncident": false, + "groupByEntities": [] } } } @@ -6535,7 +6535,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscalePremiumMassFanOut_AnalyticalRules Analytics Rule with template version 3.0.4", + "description": "TailscalePremiumMassFanOut_AnalyticalRules Analytics Rule with template version 3.0.5", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject19').analyticRuleVersion19]", @@ -6563,10 +6563,10 @@ "status": "Available", "requiredDataConnectors": [ { + "connectorId": "TailscalePremiumCCF", "dataTypes": [ "Tailscale_Network_CL" - ], - "connectorId": "TailscalePremiumCCF" + ] } ], "tactics": [ @@ -6580,32 +6580,32 @@ ], "entityMappings": [ { - "entityType": "Host", "fieldMappings": [ { - "identifier": "HostName", - "columnName": "SrcNodeName" + "columnName": "SrcNodeName", + "identifier": "HostName" } - ] + ], + "entityType": "Host" }, { - "entityType": "IP", "fieldMappings": [ { - "identifier": "Address", - "columnName": "Src" + "columnName": "Src", + "identifier": "Address" } - ] + ], + "entityType": "IP" } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { "enabled": true, - "groupByEntities": [], - "reopenClosedIncident": false, + "lookbackDuration": "5h", "matchingMethod": "AllEntities", - "lookbackDuration": "5h" + "reopenClosedIncident": false, + "groupByEntities": [] } } } @@ -6661,7 +6661,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscalePremiumSubnetRouterThroughputAnomaly_AnalyticalRules Analytics Rule with template version 3.0.4", + "description": "TailscalePremiumSubnetRouterThroughputAnomaly_AnalyticalRules Analytics Rule with template version 3.0.5", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject20').analyticRuleVersion20]", @@ -6689,10 +6689,10 @@ "status": "Available", "requiredDataConnectors": [ { + "connectorId": "TailscalePremiumCCF", "dataTypes": [ "Tailscale_Network_CL" - ], - "connectorId": "TailscalePremiumCCF" + ] } ], "tactics": [ @@ -6705,23 +6705,23 @@ ], "entityMappings": [ { - "entityType": "Host", "fieldMappings": [ { - "identifier": "HostName", - "columnName": "SrcNodeName" + "columnName": "SrcNodeName", + "identifier": "HostName" } - ] + ], + "entityType": "Host" } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { "enabled": true, - "groupByEntities": [], - "reopenClosedIncident": false, + "lookbackDuration": "1d", "matchingMethod": "AllEntities", - "lookbackDuration": "1d" + "reopenClosedIncident": false, + "groupByEntities": [] } } } @@ -6777,7 +6777,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscalePremiumPostureIntegrationDisabled_AnalyticalRules Analytics Rule with template version 3.0.4", + "description": "TailscalePremiumPostureIntegrationDisabled_AnalyticalRules Analytics Rule with template version 3.0.5", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject21').analyticRuleVersion21]", @@ -6805,10 +6805,10 @@ "status": "Available", "requiredDataConnectors": [ { + "connectorId": "TailscalePremiumCCF", "dataTypes": [ "Tailscale_Audit_CL" - ], - "connectorId": "TailscalePremiumCCF" + ] } ], "tactics": [ @@ -6821,23 +6821,23 @@ ], "entityMappings": [ { - "entityType": "Account", "fieldMappings": [ { - "identifier": "FullName", - "columnName": "ActorLogin" + "columnName": "ActorLogin", + "identifier": "FullName" } - ] + ], + "entityType": "Account" } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { "enabled": true, - "groupByEntities": [], - "reopenClosedIncident": false, + "lookbackDuration": "1d", "matchingMethod": "AllEntities", - "lookbackDuration": "1d" + "reopenClosedIncident": false, + "groupByEntities": [] } } } @@ -6893,7 +6893,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscalePremiumNewPostureIntegration_AnalyticalRules Analytics Rule with template version 3.0.4", + "description": "TailscalePremiumNewPostureIntegration_AnalyticalRules Analytics Rule with template version 3.0.5", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject22').analyticRuleVersion22]", @@ -6921,10 +6921,10 @@ "status": "Available", "requiredDataConnectors": [ { + "connectorId": "TailscalePremiumCCF", "dataTypes": [ "Tailscale_Audit_CL" - ], - "connectorId": "TailscalePremiumCCF" + ] } ], "tactics": [ @@ -6935,23 +6935,23 @@ ], "entityMappings": [ { - "entityType": "Account", "fieldMappings": [ { - "identifier": "FullName", - "columnName": "ActorLogin" + "columnName": "ActorLogin", + "identifier": "FullName" } - ] + ], + "entityType": "Account" } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { "enabled": true, - "groupByEntities": [], - "reopenClosedIncident": false, + "lookbackDuration": "1d", "matchingMethod": "AllEntities", - "lookbackDuration": "1d" + "reopenClosedIncident": false, + "groupByEntities": [] } } } @@ -7007,7 +7007,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleFirstSeenActor_HuntingQueries Hunting Query with template version 3.0.4", + "description": "TailscaleFirstSeenActor_HuntingQueries Hunting Query with template version 3.0.5", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('huntingQueryObject1').huntingQueryVersion1]", @@ -7092,7 +7092,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleACLPolicyChurn_HuntingQueries Hunting Query with template version 3.0.4", + "description": "TailscaleACLPolicyChurn_HuntingQueries Hunting Query with template version 3.0.5", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('huntingQueryObject2').huntingQueryVersion2]", @@ -7177,7 +7177,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleOffHoursConfigChanges_HuntingQueries Hunting Query with template version 3.0.4", + "description": "TailscaleOffHoursConfigChanges_HuntingQueries Hunting Query with template version 3.0.5", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('huntingQueryObject3').huntingQueryVersion3]", @@ -7262,7 +7262,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleAuthKeySprawl_HuntingQueries Hunting Query with template version 3.0.4", + "description": "TailscaleAuthKeySprawl_HuntingQueries Hunting Query with template version 3.0.5", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('huntingQueryObject4').huntingQueryVersion4]", @@ -7347,7 +7347,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleDormantDevices_HuntingQueries Hunting Query with template version 3.0.4", + "description": "TailscaleDormantDevices_HuntingQueries Hunting Query with template version 3.0.5", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('huntingQueryObject5').huntingQueryVersion5]", @@ -7432,7 +7432,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleAuthKeysNoExpiry_HuntingQueries Hunting Query with template version 3.0.4", + "description": "TailscaleAuthKeysNoExpiry_HuntingQueries Hunting Query with template version 3.0.5", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('huntingQueryObject6').huntingQueryVersion6]", @@ -7517,7 +7517,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleOrphanedUsers_HuntingQueries Hunting Query with template version 3.0.4", + "description": "TailscaleOrphanedUsers_HuntingQueries Hunting Query with template version 3.0.5", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('huntingQueryObject7').huntingQueryVersion7]", @@ -7602,7 +7602,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleSplitDnsPerDomainChanges_HuntingQueries Hunting Query with template version 3.0.4", + "description": "TailscaleSplitDnsPerDomainChanges_HuntingQueries Hunting Query with template version 3.0.5", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('huntingQueryObject8').huntingQueryVersion8]", @@ -7687,7 +7687,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleDevicesWithSshEnabled_HuntingQueries Hunting Query with template version 3.0.4", + "description": "TailscaleDevicesWithSshEnabled_HuntingQueries Hunting Query with template version 3.0.5", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('huntingQueryObject9').huntingQueryVersion9]", @@ -7772,7 +7772,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleExternalDeviceInventory_HuntingQueries Hunting Query with template version 3.0.4", + "description": "TailscaleExternalDeviceInventory_HuntingQueries Hunting Query with template version 3.0.5", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('huntingQueryObject10').huntingQueryVersion10]", @@ -7857,7 +7857,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleOutdatedClients_HuntingQueries Hunting Query with template version 3.0.4", + "description": "TailscaleOutdatedClients_HuntingQueries Hunting Query with template version 3.0.5", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('huntingQueryObject11').huntingQueryVersion11]", @@ -7942,7 +7942,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleSubnetRouteExposure_HuntingQueries Hunting Query with template version 3.0.4", + "description": "TailscaleSubnetRouteExposure_HuntingQueries Hunting Query with template version 3.0.5", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('huntingQueryObject12').huntingQueryVersion12]", @@ -8027,7 +8027,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscalePremiumNewNodePairs_HuntingQueries Hunting Query with template version 3.0.4", + "description": "TailscalePremiumNewNodePairs_HuntingQueries Hunting Query with template version 3.0.5", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('huntingQueryObject13').huntingQueryVersion13]", @@ -8112,7 +8112,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscalePremiumTopTalkers_HuntingQueries Hunting Query with template version 3.0.4", + "description": "TailscalePremiumTopTalkers_HuntingQueries Hunting Query with template version 3.0.5", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('huntingQueryObject14').huntingQueryVersion14]", @@ -8197,7 +8197,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscalePremiumExitNodeUsage_HuntingQueries Hunting Query with template version 3.0.4", + "description": "TailscalePremiumExitNodeUsage_HuntingQueries Hunting Query with template version 3.0.5", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('huntingQueryObject15').huntingQueryVersion15]", @@ -8282,7 +8282,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscalePremiumBeaconingCandidates_HuntingQueries Hunting Query with template version 3.0.4", + "description": "TailscalePremiumBeaconingCandidates_HuntingQueries Hunting Query with template version 3.0.5", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('huntingQueryObject16').huntingQueryVersion16]", @@ -8367,7 +8367,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscalePremiumPostureInventory_HuntingQueries Hunting Query with template version 3.0.4", + "description": "TailscalePremiumPostureInventory_HuntingQueries Hunting Query with template version 3.0.5", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('huntingQueryObject17').huntingQueryVersion17]", @@ -8452,7 +8452,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleStandardOperations Workbook with template version 3.0.4", + "description": "TailscaleStandardOperations Workbook with template version 3.0.5", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('workbookVersion1')]", @@ -8466,11 +8466,11 @@ "kind": "shared", "apiVersion": "2021-08-01", "metadata": { - "description": "Tailscale Operations workbook for Standard tier - device inventory, user roles, auth keys, DNS configuration, audit activity, and tailnet health overview." + "description": "Tailscale Operations workbook for Standard tier. Nine tabs covering an at-a-glance KPI hero row, audit activity overview, an actor + device drilldown (Investigate), embedded hunting queries (first-seen actors, off-hours changes, key-expiry-disabled devices, never-expire auth keys, outdated clients, dormant devices, subnet route exposure, SSH-enabled devices), identity (user roles, status, last login recency, role escalation history, orphaned users), devices (OS / version / tag distribution, devices needing attention, full inventory, subnet routers), credentials (expiry timeline, never-expire flag, CRUD events), admin audit (action heatmap, actor x action heatmap, recent 100), network and DNS (current snapshot, tailnet policy gates, ACL change history), and pipeline health (per-table freshness, ingest rate, operational events). Driven by data polled from the Tailscale REST API." }, "properties": { "displayName": "[parameters('workbook1-name')]", - "serializedData": "{\"version\":\"Notebook/1.0\",\"items\":[{\"type\":9,\"content\":{\"version\":\"KqlParameterItem/1.0\",\"parameters\":[{\"id\":\"97a71946-815e-4519-a889-a10d455eafd6\",\"version\":\"KqlParameterItem/1.0\",\"name\":\"TimeRange\",\"type\":4,\"isRequired\":true,\"value\":{\"durationMs\":86400000},\"typeSettings\":{\"selectableValues\":[{\"durationMs\":3600000},{\"durationMs\":14400000},{\"durationMs\":43200000},{\"durationMs\":86400000},{\"durationMs\":172800000},{\"durationMs\":604800000},{\"durationMs\":2592000000}]}}],\"style\":\"pills\",\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\"},\"name\":\"parameters\"},{\"type\":1,\"content\":{\"json\":\"

\"},\"name\":\"header\"},{\"type\":11,\"content\":{\"version\":\"LinkItem/1.0\",\"style\":\"tabs\",\"links\":[{\"id\":\"9505e7fb-6622-4014-8686-14f8432cf042\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Overview\",\"subTarget\":\"overview\",\"style\":\"link\"},{\"id\":\"1a6c8fea-a005-4fc4-a09a-4e99bb459744\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Devices\",\"subTarget\":\"devices\",\"style\":\"link\"},{\"id\":\"9b499cf5-007a-4e78-a541-94edbd5963ed\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Users & Identity\",\"subTarget\":\"users\",\"style\":\"link\"},{\"id\":\"29accdba-1ce7-4b56-a054-fa69eced7886\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Credentials\",\"subTarget\":\"credentials\",\"style\":\"link\"},{\"id\":\"10a5ef86-e291-4ec9-804b-1d95a35b82a3\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"DNS & Tailnet\",\"subTarget\":\"dns\",\"style\":\"link\"},{\"id\":\"b75f9e0e-7135-4ec7-8d82-13505b3f1f31\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Webhooks\",\"subTarget\":\"webhooks\",\"style\":\"link\"},{\"id\":\"77485d43-6f26-4ad6-afce-e76e4710820f\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Audit Activity\",\"subTarget\":\"audit\",\"style\":\"link\"},{\"id\":\"99788080-b885-4220-9190-ca3546528cfe\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Security Signals\",\"subTarget\":\"signals\",\"style\":\"link\"}]},\"name\":\"tabs\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":1,\"content\":{\"json\":\"
At a glance
\"},\"name\":\"div-at-a-glance-a23e75\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"union (Tailscale_Devices_CL | where TimeGenerated {TimeRange} | summarize arg_max(TimeGenerated, *) by DeviceId | summarize V=toreal(count()) | extend Metric=\\\"Devices\\\", Order=1),(Tailscale_Users_CL | where TimeGenerated {TimeRange} | summarize arg_max(TimeGenerated, *) by UserId | summarize V=toreal(count()) | extend Metric=\\\"Users\\\", Order=2),(Tailscale_Keys_CL | where TimeGenerated {TimeRange} | summarize arg_max(TimeGenerated, *) by KeyId | summarize V=toreal(countif(isnull(Revoked))) | extend Metric=\\\"Active keys\\\", Order=3),(Tailscale_Audit_CL | where TimeGenerated {TimeRange} | summarize V=toreal(count()) | extend Metric=\\\"Audit events\\\", Order=4),(Tailscale_Audit_CL | where TimeGenerated {TimeRange} | where tostring(Target.property) == \\\"ACL\\\" or tostring(Target.property) == \\\"DNS_SPLIT_DNS\\\" or tostring(Target.property) == \\\"DNS_NAMESERVERS\\\" or tostring(Target.property) == \\\"MAGICDNS_PREFERENCES\\\" | summarize V=toreal(count()) | extend Metric=\\\"DNS / ACL changes\\\", Order=5)\\n| order by Order asc | project Metric, Value=V\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"tiles\",\"tileSettings\":{\"titleContent\":{\"columnMatch\":\"Metric\",\"formatter\":1},\"leftContent\":{\"columnMatch\":\"Value\",\"formatter\":12,\"formatOptions\":{\"palette\":\"auto\"}},\"showBorder\":false}},\"name\":\"q-97c91b94\"},{\"type\":1,\"content\":{\"json\":\"
Activity over time
\"},\"name\":\"div-activity-over-time-cad2b2\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| summarize EventCount = count() by bin(TimeGenerated, 1h), Action\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"title\":\"Audit events by action\",\"visualization\":\"timechart\"},\"name\":\"q-b6555f0f\"},{\"type\":1,\"content\":{\"json\":\"
Top actors
\"},\"name\":\"div-top-actors-8776bb\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| extend ActorLogin = tostring(Actor.loginName)\\n| where isnotempty(ActorLogin)\\n| summarize EventCount = count() by ActorLogin\\n| top 10 by EventCount\\n| render barchart\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"title\":\"Top 10 actors\",\"visualization\":\"table\"},\"name\":\"q-91e46bcd\",\"customWidth\":\"50\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| summarize EventCount = count() by TargetType = tostring(Target.type)\\n| top 10 by EventCount\\n| render piechart\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"title\":\"Activity by target type\",\"visualization\":\"table\"},\"name\":\"q-226763e3\",\"customWidth\":\"50\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"overview\"},\"name\":\"group-overview\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":1,\"content\":{\"json\":\"
Device inventory snapshot
\"},\"name\":\"div-device-inventory-snapshot-9aebf5\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"let base = Tailscale_Devices_CL | summarize arg_max(TimeGenerated, *) by DeviceId;\\nunion (base | summarize V=toreal(count()) | extend Metric=\\\"Total devices\\\", Order=1),(base | where Authorized == true | summarize V=toreal(count()) | extend Metric=\\\"Authorized\\\", Order=2),(base | where IsExternal == true | summarize V=toreal(count()) | extend Metric=\\\"External (shared)\\\", Order=3),(base | where KeyExpiryDisabled == true | summarize V=toreal(count()) | extend Metric=\\\"Key expiry disabled\\\", Order=4),(base | where UpdateAvailable == true | summarize V=toreal(count()) | extend Metric=\\\"Update available\\\", Order=5),(base | where array_length(AdvertisedRoutes) > 0 | summarize V=toreal(count()) | extend Metric=\\\"Subnet routers\\\", Order=6),(base | where LastSeen < ago(30d) | summarize V=toreal(count()) | extend Metric=\\\"Stale (30+ days)\\\", Order=7)\\n| order by Order asc | project Metric, Value=V\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"tiles\",\"tileSettings\":{\"titleContent\":{\"columnMatch\":\"Metric\",\"formatter\":1},\"leftContent\":{\"columnMatch\":\"Value\",\"formatter\":12,\"formatOptions\":{\"palette\":\"auto\"}},\"showBorder\":false}},\"name\":\"q-2c8bcfb1\"},{\"type\":1,\"content\":{\"json\":\"
Distribution
\"},\"name\":\"div-distribution-c08880\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| summarize Count = count() by Os\\n| order by Count desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"title\":\"Devices by OS\",\"visualization\":\"piechart\"},\"name\":\"q-8435df72\",\"customWidth\":\"33\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| summarize Count = count() by ClientVersion\\n| order by Count desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"title\":\"Devices by client version\",\"visualization\":\"barchart\"},\"name\":\"q-95a9414c\",\"customWidth\":\"33\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| mv-expand Tag = Tags to typeof(string)\\n| summarize Devices = dcount(DeviceId) by Tag = iff(isempty(Tag), \\\"(untagged)\\\", Tag)\\n| order by Devices desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"title\":\"Devices by tag\",\"visualization\":\"piechart\"},\"name\":\"q-7e72903f\",\"customWidth\":\"33\"},{\"type\":1,\"content\":{\"json\":\"
Device inventory table
\"},\"name\":\"div-device-inventory-table-d1a89d\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| extend DaysSinceSeen = datetime_diff('day', now(), LastSeen)\\n| extend DaysToExpiry = iff(KeyExpiryDisabled == true, int(null), datetime_diff('day', Expires, now()))\\n| project DeviceName, Hostname, User, Os, ClientVersion, UpdateAvailable, LastSeen, DaysSinceSeen, DaysToExpiry, KeyExpiryDisabled, Tags, AdvertisedRoutes\\n| order by LastSeen desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"title\":\"All devices (latest snapshot per device)\",\"visualization\":\"table\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"DaysSinceSeen\",\"formatter\":8,\"formatOptions\":{\"palette\":\"redBright\",\"min\":0,\"max\":60}},{\"columnMatch\":\"DaysToExpiry\",\"formatter\":8,\"formatOptions\":{\"palette\":\"redBright\",\"min\":0,\"max\":30}},{\"columnMatch\":\"UpdateAvailable\",\"formatter\":11},{\"columnMatch\":\"KeyExpiryDisabled\",\"formatter\":11}]}},\"name\":\"q-d7892cab\"},{\"type\":1,\"content\":{\"json\":\"
Subnet routers and exit nodes
\"},\"name\":\"div-subnet-routers-and-exit-nodes-a6fbe2\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| where array_length(AdvertisedRoutes) > 0 or array_length(EnabledRoutes) > 0\\n| project DeviceName, User, Os, AdvertisedRoutes, EnabledRoutes, Tags, LastSeen\\n| order by DeviceName asc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"title\":\"Devices advertising routes (subnet routers / exit nodes)\",\"visualization\":\"table\"},\"name\":\"q-50b65ee3\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"devices\"},\"name\":\"group-devices\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":1,\"content\":{\"json\":\"
Identity overview
\"},\"name\":\"div-identity-overview-9335d8\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"let base = Tailscale_Users_CL | summarize arg_max(TimeGenerated, *) by UserId;\\nunion (base | summarize V=toreal(count()) | extend Metric=\\\"Users\\\", Order=1),(base | where Status =~ \\\"active\\\" | summarize V=toreal(count()) | extend Metric=\\\"Active\\\", Order=2),(base | where Status =~ \\\"suspended\\\" | summarize V=toreal(count()) | extend Metric=\\\"Suspended\\\", Order=3),(base | where CurrentlyConnected == true | summarize V=toreal(count()) | extend Metric=\\\"Currently connected\\\", Order=4),(base | where DeviceCount == 0 | summarize V=toreal(count()) | extend Metric=\\\"Zero devices\\\", Order=5),(base | where Role in (\\\"owner\\\",\\\"admin\\\",\\\"network-admin\\\") | summarize V=toreal(count()) | extend Metric=\\\"Elevated\\\", Order=6)\\n| order by Order asc | project Metric, Value=V\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"tiles\",\"tileSettings\":{\"titleContent\":{\"columnMatch\":\"Metric\",\"formatter\":1},\"leftContent\":{\"columnMatch\":\"Value\",\"formatter\":12,\"formatOptions\":{\"palette\":\"auto\"}},\"showBorder\":false}},\"name\":\"q-fb41aa51\"},{\"type\":1,\"content\":{\"json\":\"
Distribution
\"},\"name\":\"div-distribution-4b2298\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Users_CL\\n| summarize arg_max(TimeGenerated, *) by UserId\\n| summarize Users = count() by Role\\n| order by Users desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"title\":\"Users by role\",\"visualization\":\"piechart\"},\"name\":\"q-69fb1d2d\",\"customWidth\":\"50\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Users_CL\\n| summarize arg_max(TimeGenerated, *) by UserId\\n| summarize Users = count() by Status\\n| order by Users desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"title\":\"Users by status\",\"visualization\":\"piechart\"},\"name\":\"q-d026cea9\",\"customWidth\":\"50\"},{\"type\":1,\"content\":{\"json\":\"
User inventory table
\"},\"name\":\"div-user-inventory-table-79802d\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Users_CL\\n| summarize arg_max(TimeGenerated, *) by UserId\\n| extend DaysSinceSeen = datetime_diff('day', now(), LastSeen)\\n| project LoginName, DisplayName, Role, Status, UserType, DeviceCount, CurrentlyConnected, LastSeen, DaysSinceSeen, Created\\n| order by LastSeen desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"title\":\"All users (latest snapshot per user)\",\"visualization\":\"table\"},\"name\":\"q-bd4b15af\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"users\"},\"name\":\"group-users\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":1,\"content\":{\"json\":\"
Credential inventory
\"},\"name\":\"div-credential-inventory-3dbcc1\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"let base = Tailscale_Keys_CL | summarize arg_max(TimeGenerated, *) by KeyId | where isnull(Revoked);\\nunion (base | summarize V=toreal(count()) | extend Metric=\\\"Active credentials\\\", Order=1),(base | where KeyType =~ \\\"auth\\\" | summarize V=toreal(count()) | extend Metric=\\\"Auth keys\\\", Order=2),(base | where KeyType in~ (\\\"apiAccessToken\\\",\\\"api_access_token\\\",\\\"apikey\\\") | summarize V=toreal(count()) | extend Metric=\\\"API tokens\\\", Order=3),(base | where KeyType in~ (\\\"oauthClient\\\",\\\"oauth_client\\\") | summarize V=toreal(count()) | extend Metric=\\\"OAuth clients\\\", Order=4),(base | where isnull(Expires) | summarize V=toreal(count()) | extend Metric=\\\"No expiry\\\", Order=5),(base | where isnotnull(Expires) and Expires < now()+7d | summarize V=toreal(count()) | extend Metric=\\\"Expiring within 7 days\\\", Order=6)\\n| order by Order asc | project Metric, Value=V\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"tiles\",\"tileSettings\":{\"titleContent\":{\"columnMatch\":\"Metric\",\"formatter\":1},\"leftContent\":{\"columnMatch\":\"Value\",\"formatter\":12,\"formatOptions\":{\"palette\":\"auto\"}},\"showBorder\":false}},\"name\":\"q-7f141013\"},{\"type\":1,\"content\":{\"json\":\"
Credentials by type
\"},\"name\":\"div-credentials-by-type-20d5b3\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Keys_CL\\n| summarize arg_max(TimeGenerated, *) by KeyId\\n| where isnull(Revoked)\\n| summarize Count = count() by KeyType\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"title\":\"Credentials by type\",\"visualization\":\"piechart\"},\"name\":\"q-dfb3fbc4\",\"customWidth\":\"50\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Keys_CL\\n| summarize arg_max(TimeGenerated, *) by KeyId\\n| where isnull(Revoked) and isnotnull(Expires)\\n| extend DaysToExpiry = datetime_diff('day', Expires, now())\\n| extend Bucket = case(DaysToExpiry < 0, '0 expired', DaysToExpiry < 7, '1 within 7d', DaysToExpiry < 30, '2 within 30d', DaysToExpiry < 90, '3 within 90d', '4 over 90d')\\n| summarize Keys = count() by Bucket\\n| order by Bucket asc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"title\":\"Credentials by expiry window\",\"visualization\":\"barchart\"},\"name\":\"q-09938e1d\",\"customWidth\":\"50\"},{\"type\":1,\"content\":{\"json\":\"
Credential inventory table
\"},\"name\":\"div-credential-inventory-table-0ccb53\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Keys_CL\\n| summarize arg_max(TimeGenerated, *) by KeyId\\n| extend DaysToExpiry = iff(isnull(Expires), int(null), datetime_diff('day', Expires, now()))\\n| project KeyId, KeyType, Description, Created, Expires, DaysToExpiry, Revoked, Capabilities\\n| order by Created desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"title\":\"All credentials (latest snapshot per ID)\",\"visualization\":\"table\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"DaysToExpiry\",\"formatter\":8,\"formatOptions\":{\"palette\":\"redBright\",\"min\":0,\"max\":90}}]}},\"name\":\"q-0660a92f\"},{\"type\":1,\"content\":{\"json\":\"
Audit log: credential lifecycle
\"},\"name\":\"div-audit-log:-credential-lifecycl-25cd5f\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| where tostring(Target.type) in (\\\"AUTH_KEY\\\", \\\"API_KEY\\\", \\\"OAUTH_CLIENT\\\")\\n| extend ActorLogin = tostring(Actor.loginName)\\n| project TimeGenerated, ActorLogin, Action, TargetType = tostring(Target.type), TargetId = tostring(Target.id), Description = tostring(New.description)\\n| order by TimeGenerated desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"title\":\"Create / update / delete events on credentials\",\"visualization\":\"table\"},\"name\":\"q-667d6168\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"credentials\"},\"name\":\"group-credentials\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":1,\"content\":{\"json\":\"
Current DNS state
\"},\"name\":\"div-current-dns-state-b054f9\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Dns_CL\\n| summarize arg_max(TimeGenerated, *) by ConfigType\\n| project ConfigType, Nameservers, MagicDNS, SearchPaths, LastUpdated = TimeGenerated\\n| order by ConfigType asc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"title\":\"DNS configuration snapshot (latest per config type)\",\"visualization\":\"table\"},\"name\":\"q-b72ff334\"},{\"type\":1,\"content\":{\"json\":\"
Tailnet settings snapshot
\"},\"name\":\"div-tailnet-settings-snapshot-a457cb\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Settings_CL\\n| top 1 by TimeGenerated\\n| project\\n ['Snapshot at'] = TimeGenerated,\\n ['Device approval required'] = iff(DevicesApprovalOn == true, 'On', 'Off'),\\n ['Device auto-updates'] = iff(DevicesAutoUpdatesOn == true, 'On', 'Off'),\\n ['Device key duration (days)'] = DevicesKeyDurationDays,\\n ['User approval required'] = iff(UsersApprovalOn == true, 'On', 'Off'),\\n ['Users allowed to join external tailnets'] = UsersRoleAllowedToJoinExternalTailnets,\\n ['Regional routing'] = iff(RegionalRoutingOn == true, 'On', 'Off')\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"title\":\"Current tailnet settings\",\"visualization\":\"table\"},\"name\":\"q-99cf0545\"},{\"type\":1,\"content\":{\"json\":\"
DNS / ACL / Settings change history
\"},\"name\":\"div-dns-/-acl-/-settings-change-hi-b18656\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| where Action == \\\"UPDATE\\\" and tostring(Target.type) == \\\"TAILNET\\\"\\n| extend Property = tostring(Target.property), ActorLogin = tostring(Actor.loginName)\\n| where Property in (\\\"ACL\\\", \\\"DNS_NAMESERVERS\\\", \\\"DNS_SPLIT_DNS\\\", \\\"DNS_SEARCHPATHS\\\", \\\"MAGICDNS_PREFERENCES\\\", \\\"SETTINGS\\\", \\\"TAILNET_SETTINGS\\\")\\n| project TimeGenerated, ActorLogin, Property, Old, New, Origin\\n| order by TimeGenerated desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"title\":\"Tailnet configuration changes\",\"visualization\":\"table\"},\"name\":\"q-47218d2a\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"dns\"},\"name\":\"group-dns\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":1,\"content\":{\"json\":\"
Webhook inventory
\"},\"name\":\"div-webhook-inventory-ca6406\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Webhooks_CL\\n| summarize arg_max(TimeGenerated, *) by EndpointId\\n| project EndpointId, EndpointUrl, ProviderType, CreatorLoginName, Created, LastModified, Subscriptions\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"title\":\"All webhooks (latest snapshot per endpoint)\",\"visualization\":\"table\"},\"name\":\"q-d2585638\"},{\"type\":1,\"content\":{\"json\":\"
Webhook change history (from audit log)
\"},\"name\":\"div-webhook-change-history-(from-a-040d0e\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| where tostring(Target.type) == \\\"WEBHOOK\\\" or tostring(Target.property) =~ \\\"WEBHOOK\\\"\\n| extend ActorLogin = tostring(Actor.loginName)\\n| project TimeGenerated, ActorLogin, Action, Target, Old, New, Origin\\n| order by TimeGenerated desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"title\":\"Webhook create / update / delete events\",\"visualization\":\"table\"},\"name\":\"q-303ec591\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"webhooks\"},\"name\":\"group-webhooks\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":1,\"content\":{\"json\":\"
Audit volume
\"},\"name\":\"div-audit-volume-2dac6c\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| summarize EventCount = count() by bin(TimeGenerated, 1h)\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"title\":\"Audit events per hour\",\"visualization\":\"timechart\"},\"name\":\"q-6831105c\"},{\"type\":1,\"content\":{\"json\":\"
Actor and action distribution
\"},\"name\":\"div-actor-and-action-distribution-e6ef51\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| extend ActorLogin = tostring(Actor.loginName), TargetType = tostring(Target.type)\\n| summarize EventCount = count() by ActorLogin, Action, TargetType\\n| order by EventCount desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"title\":\"Actor / Action / Target heatmap\",\"visualization\":\"table\"},\"name\":\"q-2d0d578e\"},{\"type\":1,\"content\":{\"json\":\"
Recent activity
\"},\"name\":\"div-recent-activity-a8f996\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| extend ActorLogin = tostring(Actor.loginName), TargetType = tostring(Target.type), Property = tostring(Target.property)\\n| project TimeGenerated, ActorLogin, Action, TargetType, Property, TargetId = tostring(Target.id), Origin\\n| order by TimeGenerated desc\\n| take 100\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"title\":\"Recent 100 audit events\",\"visualization\":\"table\"},\"name\":\"q-03fb31a7\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"audit\"},\"name\":\"group-audit\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":1,\"content\":{\"json\":\"
Tailscale-related alerts
\"},\"name\":\"div-tailscale-related-alerts-a0f1ca\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"SecurityAlert\\n| where TimeGenerated {TimeRange}\\n| where AlertName startswith \\\"Tailscale\\\"\\n| summarize Count = count() by AlertName, AlertSeverity\\n| order by Count desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"title\":\"Alerts by rule\",\"visualization\":\"table\"},\"name\":\"q-ffba1ab4\",\"customWidth\":\"50\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"SecurityAlert\\n| where TimeGenerated {TimeRange}\\n| where AlertName startswith \\\"Tailscale\\\"\\n| summarize Count = count() by AlertSeverity\\n| render piechart\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"title\":\"Severity distribution\",\"visualization\":\"table\"},\"name\":\"q-bf9342b0\",\"customWidth\":\"50\"},{\"type\":1,\"content\":{\"json\":\"
Recent Tailscale alerts
\"},\"name\":\"div-recent-tailscale-alerts-45f843\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"SecurityAlert\\n| where TimeGenerated {TimeRange}\\n| where AlertName startswith \\\"Tailscale\\\"\\n| project TimeGenerated, AlertName, AlertSeverity, Description, Entities\\n| order by TimeGenerated desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"title\":\"Recent Tailscale alerts\",\"visualization\":\"table\"},\"name\":\"q-dfb44602\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"signals\"},\"name\":\"group-signals\"},{\"type\":1,\"content\":{\"json\":\"---\\n\\n**Tailscale Standard (CCF)** | Polled from the Tailscale Site Manager API every 5 minutes (audit) / hourly (state). For Premium / Enterprise tier tailnets that need network flow logs and posture telemetry, install **Tailscale Premium (CCF)** instead.\"},\"name\":\"footer\"}],\"fromTemplateId\":\"sentinel-TailscaleStandardOperations\",\"$schema\":\"https://github.com/Microsoft/Application-Insights-Workbooks/blob/master/schema/workbook.json\"}\n", + "serializedData": "{\"version\":\"Notebook/1.0\",\"items\":[{\"type\":9,\"content\":{\"version\":\"KqlParameterItem/1.0\",\"parameters\":[{\"id\":\"7b05a598-5120-43f4-bf5d-576c2a7ff28d\",\"version\":\"KqlParameterItem/1.0\",\"name\":\"TimeRange\",\"type\":4,\"isRequired\":true,\"value\":{\"durationMs\":86400000},\"typeSettings\":{\"selectableValues\":[{\"durationMs\":3600000},{\"durationMs\":14400000},{\"durationMs\":43200000},{\"durationMs\":86400000},{\"durationMs\":172800000},{\"durationMs\":604800000},{\"durationMs\":2592000000}]}}],\"style\":\"pills\",\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\"},\"name\":\"parameters\"},{\"type\":1,\"content\":{\"json\":\"
Tailscale Operations (Standard)
Single-pane visibility into your Tailscale tailnet on Personal (Free), Starter and Standard tiers: who, what, when, where, and what changed. Scope every panel with the time range below; the Investigate tab adds Actor and Device pickers for drilldown. Premium-tier panels (network flow logs, posture integrations) live in the separate Tailscale Operations (Premium) workbook.
\"},\"name\":\"header\"},{\"type\":1,\"content\":{\"json\":\"
Tailnet at a glance
\"},\"name\":\"div-tailnet-at-a-glance-04e62b\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"let DEV = Tailscale_Devices_CL | summarize arg_max(TimeGenerated, *) by DeviceId;\\nlet USR = Tailscale_Users_CL | summarize arg_max(TimeGenerated, *) by UserId;\\nlet KEY = Tailscale_Keys_CL | summarize arg_max(TimeGenerated, *) by KeyId;\\nunion\\n (DEV | summarize V=toreal(count()) | extend Metric=\\\"Devices\\\", Order=1),\\n (DEV | where Authorized == true | summarize V=toreal(count()) | extend Metric=\\\"Authorized\\\", Order=2),\\n (DEV | where UpdateAvailable == true | summarize V=toreal(count()) | extend Metric=\\\"Updates Available\\\", Order=3),\\n (DEV | where SshEnabled == true | summarize V=toreal(count()) | extend Metric=\\\"SSH-Enabled\\\", Order=4),\\n (USR | summarize V=toreal(count()) | extend Metric=\\\"Users\\\", Order=5),\\n (USR | where Role =~ \\\"admin\\\" or Role =~ \\\"owner\\\" or Role =~ \\\"network-admin\\\" | summarize V=toreal(count()) | extend Metric=\\\"Admins\\\", Order=6),\\n (KEY | where isnull(Revoked) and (isnull(Expires) or Expires > now()) | summarize V=toreal(count()) | extend Metric=\\\"Active Keys\\\", Order=7),\\n (Tailscale_Audit_CL | where TimeGenerated {TimeRange} | summarize V=toreal(count()) | extend Metric=\\\"Audit Events ({TimeRange:label})\\\", Order=8)\\n| order by Order asc | project Metric, Value=V\",\"size\":3,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"tiles\",\"tileSettings\":{\"titleContent\":{\"columnMatch\":\"Metric\",\"formatter\":1},\"leftContent\":{\"columnMatch\":\"Value\",\"formatter\":12,\"formatOptions\":{\"palette\":\"auto\"}},\"showBorder\":false}},\"name\":\"q-4e9d7cff\"},{\"type\":11,\"content\":{\"version\":\"LinkItem/1.0\",\"style\":\"tabs\",\"links\":[{\"id\":\"9794e9fd-916b-494d-8e72-af63d2f4c6c7\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Overview\",\"subTarget\":\"overview\",\"style\":\"link\"},{\"id\":\"71cf49db-33c5-4d4b-920a-2ec0c6a258dc\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Investigate\",\"subTarget\":\"investigate\",\"style\":\"link\"},{\"id\":\"5c56bb37-2053-47a0-b6fd-9539768c144d\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Hunts\",\"subTarget\":\"hunts\",\"style\":\"link\"},{\"id\":\"d2004ded-07f8-446a-a720-f0a63d1d9dda\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Identity\",\"subTarget\":\"identity\",\"style\":\"link\"},{\"id\":\"f23b3e14-1511-4a29-bf5e-bd65e55dbb40\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Devices\",\"subTarget\":\"devices\",\"style\":\"link\"},{\"id\":\"724f8352-e21b-45a6-9029-39dc92693c05\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Credentials\",\"subTarget\":\"credentials\",\"style\":\"link\"},{\"id\":\"8400ec64-e118-44e0-ae29-84afe94b8e0e\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Admin Audit\",\"subTarget\":\"audit\",\"style\":\"link\"},{\"id\":\"b7e04861-edfa-4426-b6be-f1481ca569b6\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Network & DNS\",\"subTarget\":\"network\",\"style\":\"link\"},{\"id\":\"cfbc96e4-8585-4d89-8f65-8344c8cc6eb2\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Pipeline Health\",\"subTarget\":\"pipeline\",\"style\":\"link\"}]},\"name\":\"tabs\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":1,\"content\":{\"json\":\"
Audit activity over time
\"},\"name\":\"div-audit-activity-over--546688\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| summarize EventCount = count() by bin(TimeGenerated, 1h), Action\\n| order by TimeGenerated asc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"timechart\",\"title\":\"Audit events by action\",\"noDataMessage\":\"No audit events in the selected window. Widen the time range; remember the Tailscale audit poll runs every ~30 min.\",\"noDataMessageStyle\":5},\"name\":\"q-effcd498\"},{\"type\":1,\"content\":{\"json\":\"
Who's doing what
\"},\"name\":\"div-who's-doing-what-52144e\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| extend Actor=tostring(coalesce(Actor.loginName, Actor.displayName, Actor.type))\\n| where isnotempty(Actor)\\n| summarize Events=count(), DistinctActions=dcount(Action), LastSeen=max(TimeGenerated) by Actor\\n| order by Events desc | take 15\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Top actors (by event count)\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"Events\",\"formatter\":8,\"formatOptions\":{\"palette\":\"blue\"}},{\"columnMatch\":\"LastSeen\",\"formatter\":6}]}},\"name\":\"q-34c77fa9\",\"customWidth\":\"50\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| extend TargetType=tostring(Target.type)\\n| where isnotempty(TargetType)\\n| summarize Events=count() by TargetType\\n| order by Events desc | take 15\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"piechart\",\"title\":\"Activity by target type\"},\"name\":\"q-4b3b709d\",\"customWidth\":\"50\"},{\"type\":1,\"content\":{\"json\":\"
Recent admin events
\"},\"name\":\"div-recent-admin-events-87c9e0\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| extend Actor=tostring(coalesce(Actor.loginName, Actor.displayName, Actor.type))\\n| extend TargetType=tostring(Target.type), TargetName=tostring(coalesce(Target.name, Target.id))\\n| project TimeGenerated, Action, Actor, TargetType, TargetName, Origin\\n| order by TimeGenerated desc | take 30\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Most recent 30 audit events\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"TimeGenerated\",\"formatter\":6}]}},\"name\":\"q-5e7d6306\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"overview\"},\"name\":\"group-overview\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":9,\"content\":{\"version\":\"KqlParameterItem/1.0\",\"parameters\":[{\"id\":\"b83a25c1-da18-49a2-a444-6517f13d891c\",\"version\":\"KqlParameterItem/1.0\",\"name\":\"SelectedActor\",\"label\":\"Actor\",\"type\":2,\"isRequired\":false,\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\\n| where isnotempty(ActorLogin)\\n| summarize Events=count() by ActorLogin\\n| order by Events desc | take 50\\n| project value=ActorLogin, label=strcat(ActorLogin, \\\" (\\\", Events, \\\")\\\")\",\"typeSettings\":{\"additionalResourceOptions\":[\"value::all\"],\"showDefault\":false},\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"value\":\"value::all\"},{\"id\":\"a9cf7907-f201-4725-a072-a8bd34bef74e\",\"version\":\"KqlParameterItem/1.0\",\"name\":\"SelectedDevice\",\"label\":\"Device\",\"type\":2,\"isRequired\":false,\"query\":\"Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| order by LastSeen desc | take 100\\n| project value=DeviceName, label=strcat(coalesce(DeviceName, Hostname), \\\" (\\\", User, \\\")\\\")\",\"typeSettings\":{\"additionalResourceOptions\":[\"value::all\"],\"showDefault\":false},\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"value\":\"value::all\"}],\"style\":\"pills\",\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\"},\"name\":\"investigate-pickers\"},{\"type\":1,\"content\":{\"json\":\"
Actor activity timeline
\"},\"name\":\"div-actor-activity-timel-5ec305\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\\n| where \\\"{SelectedActor}\\\" == \\\"value::all\\\" or ActorLogin == \\\"{SelectedActor}\\\"\\n| summarize Events=count() by bin(TimeGenerated, 1h), Action\\n| order by TimeGenerated asc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"timechart\",\"title\":\"Actions over time for selected actor (or all actors)\",\"noDataMessage\":\"Select an actor from the Actor dropdown above, or leave on 'All' to see total activity.\",\"noDataMessageStyle\":5},\"name\":\"q-32d40848\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\\n| where \\\"{SelectedActor}\\\" == \\\"value::all\\\" or ActorLogin == \\\"{SelectedActor}\\\"\\n| extend TargetType=tostring(Target.type), TargetName=tostring(coalesce(Target.name, Target.id))\\n| project TimeGenerated, ActorLogin, Action, TargetType, TargetName, Origin\\n| order by TimeGenerated desc | take 100\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Events from selected actor\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"TimeGenerated\",\"formatter\":6}]},\"noDataMessage\":\"No events for this actor in the selected window.\",\"noDataMessageStyle\":5},\"name\":\"q-a742f6fd\"},{\"type\":1,\"content\":{\"json\":\"
Selected device timeline
\"},\"name\":\"div-selected-device-time-00bead\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| where \\\"{SelectedDevice}\\\" == \\\"value::all\\\" or DeviceName == \\\"{SelectedDevice}\\\"\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| extend OnlineNow = ClientConnectivity.endpoints != \\\"\\\" or ConnectedToControl == true\\n| project DeviceName, Hostname, User, Os, ClientVersion, UpdateAvailable, Authorized, IsExternal, SshEnabled, LastSeen, Expires, KeyExpiryDisabled, OnlineNow, Addresses, Tags, AdvertisedRoutes\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Device summary\",\"noDataMessage\":\"Select a device from the Device dropdown above. Defaults to 'All'.\",\"noDataMessageStyle\":5},\"name\":\"q-11094dc8\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| extend TargetType=tostring(Target.type), TargetName=tostring(Target.name), TargetId=tostring(Target.id)\\n| where (\\\"{SelectedDevice}\\\" == \\\"value::all\\\" and TargetType == \\\"NODE\\\") or TargetName == \\\"{SelectedDevice}\\\"\\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\\n| project TimeGenerated, Action, ActorLogin, TargetName, TargetId, Origin\\n| order by TimeGenerated desc | take 100\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Audit events touching this device\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"TimeGenerated\",\"formatter\":6}]},\"noDataMessage\":\"No audit events recorded against the selected device in this window. Tailscale tags device events with Target.type=NODE; the audit feed only emits NODE events on create/update/delete, so quiet devices stay quiet here.\",\"noDataMessageStyle\":5},\"name\":\"q-ead106ce\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"investigate\"},\"name\":\"group-investigate\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":1,\"content\":{\"json\":\"
First-seen actors in the last 24h
\"},\"name\":\"div-first-seen-actors-in-ce38d9\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"let recent = Tailscale_Audit_CL | where TimeGenerated > ago(24h) | extend A=tostring(coalesce(Actor.loginName, Actor.displayName)) | summarize FirstSeen24h=min(TimeGenerated), Events=count() by A;\\nlet historical = Tailscale_Audit_CL | where TimeGenerated between(ago(30d) .. ago(24h)) | extend A=tostring(coalesce(Actor.loginName, Actor.displayName)) | distinct A;\\nrecent | join kind=leftanti historical on A | where isnotempty(A) | project FirstSeen24h, Actor=A, Events | order by Events desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Actors who have NEVER appeared before (30d baseline)\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"FirstSeen24h\",\"formatter\":6},{\"columnMatch\":\"Events\",\"formatter\":8,\"formatOptions\":{\"palette\":\"orange\"}}]},\"noDataMessage\":\"Every actor seen in the last 24h has appeared at least once in the prior 30d. Healthy state.\",\"noDataMessageStyle\":5},\"name\":\"q-9ba7b85e\"},{\"type\":1,\"content\":{\"json\":\"
Off-hours configuration changes
\"},\"name\":\"div-off-hours-configurat-3c3787\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| extend Hour=hourofday(TimeGenerated), DayOfWeek=dayofweek(TimeGenerated)/1d\\n| where Hour < 7 or Hour > 19 or DayOfWeek in (0, 6)\\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\\n| extend TargetType=tostring(Target.type)\\n| where Action !in (\\\"LOGIN\\\", \\\"LOGOUT\\\")\\n| project TimeGenerated, ActorLogin, Action, TargetType, Origin\\n| order by TimeGenerated desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Admin actions outside 07:00-19:00 weekdays\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"TimeGenerated\",\"formatter\":6}]},\"noDataMessage\":\"No off-hours admin changes recorded - healthy state for an organisation working business hours.\",\"noDataMessageStyle\":5},\"name\":\"q-721ee490\"},{\"type\":1,\"content\":{\"json\":\"
Devices with key expiry disabled
\"},\"name\":\"div-devices-with-key-exp-dfe9c1\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| where KeyExpiryDisabled == true\\n| project DeviceName, Hostname, User, Os, ClientVersion, LastSeen, Authorized, Tags\\n| order by LastSeen desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Devices that will never re-authenticate (high-risk drift)\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"LastSeen\",\"formatter\":6}]},\"noDataMessage\":\"No devices have key expiry disabled - good. Disabling key expiry creates devices that never re-auth, drifting from policy.\",\"noDataMessageStyle\":5},\"name\":\"q-8a8e2fb5\"},{\"type\":1,\"content\":{\"json\":\"
Auth keys with no expiry
\"},\"name\":\"div-auth-keys-with-no-ex-b11207\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Keys_CL\\n| summarize arg_max(TimeGenerated, *) by KeyId\\n| where isnull(Revoked) and (isnull(Expires) or ExpirySeconds == 0)\\n| project KeyId, Description, UserId, KeyType, Created, Capabilities\\n| order by Created desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Active keys that never expire\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"Created\",\"formatter\":6}]},\"noDataMessage\":\"No never-expiring auth keys - rotation hygiene is good.\",\"noDataMessageStyle\":5},\"name\":\"q-1372740c\"},{\"type\":1,\"content\":{\"json\":\"
Devices running outdated clients
\"},\"name\":\"div-devices-running-outd-bcd077\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| where UpdateAvailable == true\\n| project DeviceName, Hostname, User, Os, ClientVersion, LastSeen, Tags\\n| order by LastSeen desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Devices flagged update-available by Tailscale\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"LastSeen\",\"formatter\":6}]},\"noDataMessage\":\"All devices on current client - nothing to patch.\",\"noDataMessageStyle\":5},\"name\":\"q-ecebf1f7\"},{\"type\":1,\"content\":{\"json\":\"
Dormant devices (LastSeen > 30 days)
\"},\"name\":\"div-dormant-devices-(las-761156\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| where LastSeen < ago(30d)\\n| extend DaysIdle = toint((now() - LastSeen) / 1d)\\n| project DeviceName, Hostname, User, Os, ClientVersion, LastSeen, DaysIdle, Authorized, Tags\\n| order by DaysIdle desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Devices idle 30+ days - candidates for retirement\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"LastSeen\",\"formatter\":6},{\"columnMatch\":\"DaysIdle\",\"formatter\":8,\"formatOptions\":{\"palette\":\"redBright\"}}]},\"noDataMessage\":\"No devices idle 30+ days - inventory is fresh.\",\"noDataMessageStyle\":5},\"name\":\"q-fb6c1fcc\"},{\"type\":1,\"content\":{\"json\":\"
Subnet route exposure
\"},\"name\":\"div-subnet-route-exposur-289c42\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| where array_length(AdvertisedRoutes) > 0 or array_length(EnabledRoutes) > 0\\n| extend Routes = tostring(EnabledRoutes), Advertised = tostring(AdvertisedRoutes)\\n| project DeviceName, Hostname, User, Os, Advertised, Routes, LastSeen, SshEnabled, Authorized\\n| order by LastSeen desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Devices advertising or running subnet routes / exit-node duty\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"LastSeen\",\"formatter\":6}]},\"noDataMessage\":\"No devices advertising subnet routes. Pure mesh topology.\",\"noDataMessageStyle\":5},\"name\":\"q-9533b081\"},{\"type\":1,\"content\":{\"json\":\"
Devices with SSH enabled
\"},\"name\":\"div-devices-with-ssh-ena-285238\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| where SshEnabled == true\\n| project DeviceName, Hostname, User, Os, ClientVersion, LastSeen, Authorized, Tags\\n| order by LastSeen desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Devices with Tailscale SSH enabled (Tailscale-managed remote-shell access)\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"LastSeen\",\"formatter\":6}]},\"noDataMessage\":\"No devices have Tailscale SSH enabled - no SSH-via-Tailscale risk surface.\",\"noDataMessageStyle\":5},\"name\":\"q-735368fb\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"hunts\"},\"name\":\"group-hunts\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":1,\"content\":{\"json\":\"
User inventory snapshot
\"},\"name\":\"div-user-inventory-snaps-9dc96c\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"let U = Tailscale_Users_CL | summarize arg_max(TimeGenerated, *) by UserId;\\nunion\\n (U | summarize V=toreal(count()) | extend Metric=\\\"Total users\\\", Order=1),\\n (U | where Role in~ (\\\"admin\\\",\\\"owner\\\",\\\"network-admin\\\",\\\"it-admin\\\",\\\"billing-admin\\\") | summarize V=toreal(count()) | extend Metric=\\\"Admin-tier users\\\", Order=2),\\n (U | where Status =~ \\\"active\\\" | summarize V=toreal(count()) | extend Metric=\\\"Active\\\", Order=3),\\n (U | where CurrentlyConnected == true | summarize V=toreal(count()) | extend Metric=\\\"Connected now\\\", Order=4),\\n (U | where Status =~ \\\"idle\\\" or LastSeen < ago(30d) | summarize V=toreal(count()) | extend Metric=\\\"Idle / dormant\\\", Order=5),\\n (U | where UserType =~ \\\"shared\\\" | summarize V=toreal(count()) | extend Metric=\\\"Shared (external)\\\", Order=6)\\n| order by Order asc | project Metric, Value=V\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"tiles\",\"tileSettings\":{\"titleContent\":{\"columnMatch\":\"Metric\",\"formatter\":1},\"leftContent\":{\"columnMatch\":\"Value\",\"formatter\":12,\"formatOptions\":{\"palette\":\"auto\"}},\"showBorder\":false}},\"name\":\"q-5689d9e8\"},{\"type\":1,\"content\":{\"json\":\"
Distribution
\"},\"name\":\"div-distribution-de67ec\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Users_CL\\n| summarize arg_max(TimeGenerated, *) by UserId\\n| summarize Count=count() by Role\\n| order by Count desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"piechart\",\"title\":\"Users by role\"},\"name\":\"q-0c47912a\",\"customWidth\":\"33\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Users_CL\\n| summarize arg_max(TimeGenerated, *) by UserId\\n| summarize Count=count() by Status\\n| order by Count desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"piechart\",\"title\":\"Users by status\"},\"name\":\"q-e8c20a69\",\"customWidth\":\"33\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Users_CL\\n| summarize arg_max(TimeGenerated, *) by UserId\\n| summarize Count=count() by UserType\\n| order by Count desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"piechart\",\"title\":\"Users by type (member / shared)\"},\"name\":\"q-2c51776d\",\"customWidth\":\"33\"},{\"type\":1,\"content\":{\"json\":\"
Activity heatmap
\"},\"name\":\"div-activity-heatmap-ca6a21\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Users_CL\\n| summarize arg_max(TimeGenerated, *) by UserId\\n| extend DaysSinceLogin = toint((now() - LastSeen) / 1d)\\n| extend Bucket = case(\\n DaysSinceLogin < 1, \\\"Today\\\",\\n DaysSinceLogin < 7, \\\"This week\\\",\\n DaysSinceLogin < 30, \\\"This month\\\",\\n DaysSinceLogin < 90, \\\"Past quarter\\\",\\n \\\"90+ days\\\")\\n| summarize Users=count() by Bucket\\n| order by case(Bucket==\\\"Today\\\",1, Bucket==\\\"This week\\\",2, Bucket==\\\"This month\\\",3, Bucket==\\\"Past quarter\\\",4, 5) asc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"barchart\",\"title\":\"Users by recency of last login\"},\"name\":\"q-350e118d\"},{\"type\":1,\"content\":{\"json\":\"
Full user list
\"},\"name\":\"div-full-user-list-136aac\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Users_CL\\n| summarize arg_max(TimeGenerated, *) by UserId\\n| project DisplayName, LoginName, Role, Status, UserType, DeviceCount, CurrentlyConnected, Created, LastSeen\\n| order by LastSeen desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"All users (latest snapshot per user ID)\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"Created\",\"formatter\":6},{\"columnMatch\":\"LastSeen\",\"formatter\":6},{\"columnMatch\":\"Role\",\"formatter\":11},{\"columnMatch\":\"Status\",\"formatter\":11},{\"columnMatch\":\"DeviceCount\",\"formatter\":8,\"formatOptions\":{\"palette\":\"blue\"}}]}},\"name\":\"q-cee23d3c\"},{\"type\":1,\"content\":{\"json\":\"
Orphaned users (active but no devices)
\"},\"name\":\"div-orphaned-users-(acti-56d6f8\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Users_CL\\n| summarize arg_max(TimeGenerated, *) by UserId\\n| where Status =~ \\\"active\\\" and DeviceCount == 0\\n| project DisplayName, LoginName, Role, UserType, Created, LastSeen\\n| order by Created desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Active accounts with zero devices - candidates for offboarding review\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"Created\",\"formatter\":6},{\"columnMatch\":\"LastSeen\",\"formatter\":6}]},\"noDataMessage\":\"Every active account has at least one device - good hygiene.\",\"noDataMessageStyle\":5},\"name\":\"q-83fcd942\"},{\"type\":1,\"content\":{\"json\":\"
Role escalation history
\"},\"name\":\"div-role-escalation-hist-bd8df4\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| where Action == \\\"USER_ROLE_UPDATE\\\" or Action == \\\"USER_ROLES_ASSIGNED\\\" or Action contains \\\"ROLE\\\"\\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\\n| extend TargetName=tostring(coalesce(Target.name, Target.id))\\n| extend FromRole=tostring(Old.role), ToRole=tostring(New.role)\\n| project TimeGenerated, ActorLogin, Action, TargetName, FromRole, ToRole, Origin\\n| order by TimeGenerated desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Recent role changes\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"TimeGenerated\",\"formatter\":6},{\"columnMatch\":\"ToRole\",\"formatter\":11}]},\"noDataMessage\":\"No role changes in this window.\",\"noDataMessageStyle\":5},\"name\":\"q-f6c8358a\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"identity\"},\"name\":\"group-identity\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":1,\"content\":{\"json\":\"
Device fleet snapshot
\"},\"name\":\"div-device-fleet-snapsho-939675\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"let D = Tailscale_Devices_CL | summarize arg_max(TimeGenerated, *) by DeviceId;\\nunion\\n (D | summarize V=toreal(count()) | extend Metric=\\\"Total devices\\\", Order=1),\\n (D | where Authorized == true | summarize V=toreal(count()) | extend Metric=\\\"Authorized\\\", Order=2),\\n (D | where IsExternal == true | summarize V=toreal(count()) | extend Metric=\\\"External (shared)\\\", Order=3),\\n (D | where UpdateAvailable == true | summarize V=toreal(count()) | extend Metric=\\\"Updates available\\\", Order=4),\\n (D | where SshEnabled == true | summarize V=toreal(count()) | extend Metric=\\\"SSH-enabled\\\", Order=5),\\n (D | where KeyExpiryDisabled == true | summarize V=toreal(count()) | extend Metric=\\\"No key expiry\\\", Order=6),\\n (D | where array_length(AdvertisedRoutes) > 0 | summarize V=toreal(count()) | extend Metric=\\\"Subnet/exit-node\\\", Order=7),\\n (D | where LastSeen < ago(30d) | summarize V=toreal(count()) | extend Metric=\\\"Stale (30+ days)\\\", Order=8)\\n| order by Order asc | project Metric, Value=V\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"tiles\",\"tileSettings\":{\"titleContent\":{\"columnMatch\":\"Metric\",\"formatter\":1},\"leftContent\":{\"columnMatch\":\"Value\",\"formatter\":12,\"formatOptions\":{\"palette\":\"auto\"}},\"showBorder\":false}},\"name\":\"q-366964fc\"},{\"type\":1,\"content\":{\"json\":\"
Distribution
\"},\"name\":\"div-distribution-396d03\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| summarize Count=count() by Os\\n| order by Count desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"piechart\",\"title\":\"Devices by OS\"},\"name\":\"q-0c8f5988\",\"customWidth\":\"33\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| summarize Count=count() by ClientVersion\\n| order by Count desc | take 10\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"barchart\",\"title\":\"Top 10 client versions\"},\"name\":\"q-af1c45cd\",\"customWidth\":\"33\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| mv-expand Tag = Tags to typeof(string)\\n| summarize Devices=dcount(DeviceId) by Tag=iff(isempty(Tag), \\\"(untagged)\\\", Tag)\\n| order by Devices desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"piechart\",\"title\":\"Devices by tag\"},\"name\":\"q-c3a0ef5b\",\"customWidth\":\"33\"},{\"type\":1,\"content\":{\"json\":\"
Devices needing attention
\"},\"name\":\"div-devices-needing-atte-f04a47\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| where UpdateAvailable == true or KeyExpiryDisabled == true or LastSeen < ago(30d) or Authorized == false\\n| extend Issues = strcat_array(pack_array(\\n iff(UpdateAvailable == true, \\\"needs-update\\\", \\\"\\\"),\\n iff(KeyExpiryDisabled == true, \\\"key-never-expires\\\", \\\"\\\"),\\n iff(LastSeen < ago(30d), \\\"stale\\\", \\\"\\\"),\\n iff(Authorized == false, \\\"unauthorized\\\", \\\"\\\")), \\\",\\\")\\n| extend Issues = trim(\\\",\\\", trim_start(\\\",\\\", trim_end(\\\",\\\", replace_string(Issues, \\\",,\\\", \\\",\\\"))))\\n| project DeviceName, Hostname, User, Os, ClientVersion, LastSeen, Issues\\n| order by LastSeen desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Devices flagged with one or more issues\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"LastSeen\",\"formatter\":6},{\"columnMatch\":\"Issues\",\"formatter\":11}]},\"noDataMessage\":\"No devices need attention - all updated, fresh, authorized, and key-rotating.\",\"noDataMessageStyle\":5},\"name\":\"q-dc5db84c\"},{\"type\":1,\"content\":{\"json\":\"
Full device inventory
\"},\"name\":\"div-full-device-inventor-b70642\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| project DeviceName, Hostname, User, Os, ClientVersion, UpdateAvailable, Authorized, IsExternal, SshEnabled, LastSeen, KeyExpiryDisabled, Tags, AdvertisedRoutes\\n| order by LastSeen desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"All devices (latest snapshot per device ID)\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"LastSeen\",\"formatter\":6},{\"columnMatch\":\"Os\",\"formatter\":11},{\"columnMatch\":\"ClientVersion\",\"formatter\":11}]}},\"name\":\"q-7f7e7a9a\"},{\"type\":1,\"content\":{\"json\":\"
Subnet routers / exit nodes
\"},\"name\":\"div-subnet-routers-/-exi-b082d6\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| where array_length(AdvertisedRoutes) > 0\\n| extend AdvertisedSummary = tostring(AdvertisedRoutes), EnabledSummary = tostring(EnabledRoutes)\\n| project DeviceName, Hostname, User, Os, AdvertisedSummary, EnabledSummary, LastSeen, Authorized\\n| order by LastSeen desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Devices advertising subnet routes or exit-node capability\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"LastSeen\",\"formatter\":6}]},\"noDataMessage\":\"No subnet routers in this tailnet - pure mesh topology.\",\"noDataMessageStyle\":5},\"name\":\"q-5004df25\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"devices\"},\"name\":\"group-devices\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":1,\"content\":{\"json\":\"
Credentials snapshot
\"},\"name\":\"div-credentials-snapshot-fd464a\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"let K = Tailscale_Keys_CL | summarize arg_max(TimeGenerated, *) by KeyId;\\nunion\\n (K | summarize V=toreal(count()) | extend Metric=\\\"Total keys\\\", Order=1),\\n (K | where isnull(Revoked) and (isnull(Expires) or Expires > now()) | summarize V=toreal(count()) | extend Metric=\\\"Active\\\", Order=2),\\n (K | where isnotnull(Revoked) | summarize V=toreal(count()) | extend Metric=\\\"Revoked\\\", Order=3),\\n (K | where Expires < now() and isnull(Revoked) | summarize V=toreal(count()) | extend Metric=\\\"Expired\\\", Order=4),\\n (K | where isnull(Revoked) and Expires between(now() .. ago(-7d)) | summarize V=toreal(count()) | extend Metric=\\\"Expiring in 7d\\\", Order=5),\\n (K | where isnull(Revoked) and (isnull(Expires) or ExpirySeconds==0) | summarize V=toreal(count()) | extend Metric=\\\"Never expire\\\", Order=6),\\n (K | where KeyType =~ \\\"auth\\\" | summarize V=toreal(count()) | extend Metric=\\\"Auth keys\\\", Order=7),\\n (K | where KeyType =~ \\\"api\\\" or KeyType contains \\\"oauth\\\" | summarize V=toreal(count()) | extend Metric=\\\"API / OAuth\\\", Order=8)\\n| order by Order asc | project Metric, Value=V\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"tiles\",\"tileSettings\":{\"titleContent\":{\"columnMatch\":\"Metric\",\"formatter\":1},\"leftContent\":{\"columnMatch\":\"Value\",\"formatter\":12,\"formatOptions\":{\"palette\":\"auto\"}},\"showBorder\":false}},\"name\":\"q-79398bc0\"},{\"type\":1,\"content\":{\"json\":\"
Distribution
\"},\"name\":\"div-distribution-15f665\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Keys_CL\\n| summarize arg_max(TimeGenerated, *) by KeyId\\n| summarize Count=count() by KeyType\\n| order by Count desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"piechart\",\"title\":\"Keys by type\"},\"name\":\"q-23e6618b\",\"customWidth\":\"50\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Keys_CL\\n| summarize arg_max(TimeGenerated, *) by KeyId\\n| where isnull(Revoked)\\n| extend Bucket = case(\\n isnull(Expires) or ExpirySeconds == 0, \\\"Never\\\",\\n Expires < now(), \\\"Already expired\\\",\\n Expires < ago(-1d), \\\"<24h\\\",\\n Expires < ago(-7d), \\\"1-7d\\\",\\n Expires < ago(-30d), \\\"8-30d\\\",\\n Expires < ago(-90d), \\\"31-90d\\\",\\n \\\"90+d\\\")\\n| summarize Keys=count() by Bucket\\n| order by case(Bucket==\\\"Already expired\\\",1, Bucket==\\\"<24h\\\",2, Bucket==\\\"1-7d\\\",3, Bucket==\\\"8-30d\\\",4, Bucket==\\\"31-90d\\\",5, Bucket==\\\"90+d\\\",6, Bucket==\\\"Never\\\",7, 8) asc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"barchart\",\"title\":\"Active key expiry distribution\"},\"name\":\"q-7484d1a0\",\"customWidth\":\"50\"},{\"type\":1,\"content\":{\"json\":\"
Active credential register
\"},\"name\":\"div-active-credential-re-c27539\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Keys_CL\\n| summarize arg_max(TimeGenerated, *) by KeyId\\n| where isnull(Revoked)\\n| extend ExpiryStatus = case(\\n isnull(Expires) or ExpirySeconds == 0, \\\"Never expires\\\",\\n Expires < now(), \\\"Expired\\\",\\n Expires < ago(-7d), \\\"Expires in 7d\\\",\\n Expires < ago(-30d), \\\"Expires in 30d\\\",\\n \\\"OK\\\")\\n| project KeyId, KeyType, Description, UserId, Created, Expires, ExpiryStatus, Capabilities\\n| order by Created desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"All active credentials with computed expiry status\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"Created\",\"formatter\":6},{\"columnMatch\":\"Expires\",\"formatter\":6},{\"columnMatch\":\"ExpiryStatus\",\"formatter\":11},{\"columnMatch\":\"KeyType\",\"formatter\":11}]}},\"name\":\"q-4b27a750\"},{\"type\":1,\"content\":{\"json\":\"
Credential CRUD events
\"},\"name\":\"div-credential-crud-even-e455b0\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| where Action contains \\\"API_KEY\\\" or Action contains \\\"AUTH_KEY\\\" or Action contains \\\"OAUTH\\\" or Action contains \\\"KEY_CREATE\\\" or Action contains \\\"KEY_REVOKE\\\"\\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\\n| extend TargetId=tostring(Target.id), TargetType=tostring(Target.type)\\n| project TimeGenerated, Action, ActorLogin, TargetType, TargetId, Origin\\n| order by TimeGenerated desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Recent credential create / revoke / rotate events\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"TimeGenerated\",\"formatter\":6}]},\"noDataMessage\":\"No credential CRUD activity in this window.\",\"noDataMessageStyle\":5},\"name\":\"q-777693bd\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"credentials\"},\"name\":\"group-credentials\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":1,\"content\":{\"json\":\"
Audit volume
\"},\"name\":\"div-audit-volume-de29a2\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| summarize Events=count() by bin(TimeGenerated, 1h)\\n| order by TimeGenerated asc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"timechart\",\"title\":\"Audit events per hour\",\"noDataMessage\":\"No audit events in this window.\",\"noDataMessageStyle\":5},\"name\":\"q-f5eee265\"},{\"type\":1,\"content\":{\"json\":\"
Action heatmap by hour of day
\"},\"name\":\"div-action-heatmap-by-ho-c8bd5e\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| extend Hour=hourofday(TimeGenerated)\\n| summarize Events=count() by Hour, Action\\n| order by Hour asc, Events desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"categoricalbar\",\"title\":\"When are admin actions happening?\"},\"name\":\"q-c6f45d95\"},{\"type\":1,\"content\":{\"json\":\"
Actor / Action heatmap
\"},\"name\":\"div-actor-/-action-heatm-d820ba\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\\n| where isnotempty(ActorLogin)\\n| summarize Events=count() by ActorLogin, Action\\n| order by Events desc | take 100\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Who is firing which action\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"Events\",\"formatter\":4,\"formatOptions\":{\"palette\":\"blue\"}}]},\"noDataMessage\":\"No audit events in this window.\",\"noDataMessageStyle\":5},\"name\":\"q-06c13b3b\"},{\"type\":1,\"content\":{\"json\":\"
Recent activity
\"},\"name\":\"div-recent-activity-63b210\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\\n| extend TargetType=tostring(Target.type), TargetName=tostring(coalesce(Target.name, Target.id))\\n| project TimeGenerated, Action, ActorLogin, TargetType, TargetName, Origin, EventGroupID\\n| order by TimeGenerated desc | take 100\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Last 100 audit events\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"TimeGenerated\",\"formatter\":6},{\"columnMatch\":\"Action\",\"formatter\":11}]},\"noDataMessage\":\"No audit events in this window.\",\"noDataMessageStyle\":5},\"name\":\"q-07a25a40\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"audit\"},\"name\":\"group-audit\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":1,\"content\":{\"json\":\"
DNS configuration (current state)
\"},\"name\":\"div-dns-configuration-(c-5f2e1e\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Dns_CL\\n| summarize arg_max(TimeGenerated, *) by ConfigType\\n| project ConfigType, Nameservers, MagicDNS, SearchPaths, LastSnapshot=TimeGenerated\\n| order by ConfigType asc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"MagicDNS, nameservers, search paths\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"LastSnapshot\",\"formatter\":6},{\"columnMatch\":\"ConfigType\",\"formatter\":11}]},\"noDataMessage\":\"No DNS snapshots in the workspace yet. DNS polls runs at ~30 min cadence.\",\"noDataMessageStyle\":5},\"name\":\"q-d6a6a358\"},{\"type\":1,\"content\":{\"json\":\"
Tailnet settings (current)
\"},\"name\":\"div-tailnet-settings-(cu-e03b16\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Settings_CL\\n| summarize arg_max(TimeGenerated, *) by TenantId\\n| project DevicesApprovalOn, DevicesAutoUpdatesOn, DevicesKeyDurationDays, UsersApprovalOn, NetworkFlowLoggingOn, RegionalRoutingOn, PostureIdentityCollectionOn, UsersRoleAllowedToJoinExternalTailnets, LastSnapshot=TimeGenerated\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Tailnet policy gates\",\"noDataMessage\":\"No settings snapshot yet.\",\"noDataMessageStyle\":5},\"name\":\"q-0622cfc3\"},{\"type\":1,\"content\":{\"json\":\"
DNS change history
\"},\"name\":\"div-dns-change-history-9dd376\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| extend TargetProperty=tostring(Target.property)\\n| where Action contains \\\"DNS\\\" or TargetProperty has_any (\\\"DNS_NAMESERVERS\\\", \\\"DNS_SPLIT_DNS\\\", \\\"MAGICDNS\\\", \\\"DNS_SEARCH_PATHS\\\")\\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\\n| project TimeGenerated, ActorLogin, Action, TargetProperty, Origin\\n| order by TimeGenerated desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Recent DNS-related admin changes\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"TimeGenerated\",\"formatter\":6}]},\"noDataMessage\":\"No DNS changes in this window.\",\"noDataMessageStyle\":5},\"name\":\"q-a132ff6a\"},{\"type\":1,\"content\":{\"json\":\"
ACL policy changes
\"},\"name\":\"div-acl-policy-changes-ff0e68\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| where Action == \\\"ACL_UPDATE\\\" or Action contains \\\"ACL\\\"\\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\\n| project TimeGenerated, ActorLogin, Action, Origin, EventGroupID\\n| order by TimeGenerated desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Recent ACL / policy file modifications\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"TimeGenerated\",\"formatter\":6}]},\"noDataMessage\":\"No ACL changes in this window.\",\"noDataMessageStyle\":5},\"name\":\"q-94818c53\"},{\"type\":1,\"content\":{\"json\":\"
Subnet routes & exit nodes
\"},\"name\":\"div-subnet-routes-and-ex-eef47f\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| where array_length(EnabledRoutes) > 0\\n| project DeviceName, User, Os, EnabledRoutes=tostring(EnabledRoutes), AdvertisedRoutes=tostring(AdvertisedRoutes), LastSeen\\n| order by LastSeen desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Routes currently being served from devices\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"LastSeen\",\"formatter\":6}]},\"noDataMessage\":\"No subnet routers active in this tailnet.\",\"noDataMessageStyle\":5},\"name\":\"q-61a792cd\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"network\"},\"name\":\"group-network\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":1,\"content\":{\"json\":\"
Ingest rate per table
\"},\"name\":\"div-ingest-rate-per-tabl-24e5f6\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"union withsource=Table Tailscale_Audit_CL, Tailscale_Devices_CL, Tailscale_Users_CL, Tailscale_Keys_CL, Tailscale_Dns_CL, Tailscale_Settings_CL\\n| where TimeGenerated > ago(24h)\\n| summarize Rows=count() by Table, bin(TimeGenerated, 1h)\\n| order by TimeGenerated asc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"timechart\",\"title\":\"Rows ingested per Tailscale table per hour (last 24h)\",\"noDataMessage\":\"No Tailscale data ingested in the last 24h - check the connector card under Sentinel Data Connectors.\",\"noDataMessageStyle\":5},\"name\":\"q-c6fd0143\"},{\"type\":1,\"content\":{\"json\":\"
Last poll time per table
\"},\"name\":\"div-last-poll-time-per-t-49b051\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"union withsource=Table Tailscale_Audit_CL, Tailscale_Devices_CL, Tailscale_Users_CL, Tailscale_Keys_CL, Tailscale_Dns_CL, Tailscale_Settings_CL\\n| summarize LastRow=max(TimeGenerated), TotalRows=count() by Table\\n| extend MinutesAgo=toint((now() - LastRow) / 1m)\\n| extend Status=case(MinutesAgo < 60, \\\"Fresh\\\", MinutesAgo < 360, \\\"Recent\\\", MinutesAgo < 1440, \\\"Stale\\\", \\\"Very Stale\\\")\\n| project Table, LastRow, MinutesAgo, TotalRows, Status\\n| order by MinutesAgo asc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Per-table freshness\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"LastRow\",\"formatter\":6},{\"columnMatch\":\"MinutesAgo\",\"formatter\":8,\"formatOptions\":{\"palette\":\"redBright\"}},{\"columnMatch\":\"TotalRows\",\"formatter\":8,\"formatOptions\":{\"palette\":\"blue\"}},{\"columnMatch\":\"Status\",\"formatter\":11}]}},\"name\":\"q-a02e37a8\"},{\"type\":1,\"content\":{\"json\":\"
Log Analytics operational events
\"},\"name\":\"div-log-analytics-operat-630749\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"_LogOperation\\n| where TimeGenerated > ago(24h)\\n| where _ResourceId contains \\\"tailscale\\\" or Detail contains \\\"Tailscale_\\\"\\n| project TimeGenerated, Operation, Level, Detail\\n| order by TimeGenerated desc | take 100\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Log Analytics operational events touching Tailscale tables\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"TimeGenerated\",\"formatter\":6},{\"columnMatch\":\"Level\",\"formatter\":11}]},\"noDataMessage\":\"No operational issues recorded in the last 24h.\",\"noDataMessageStyle\":5},\"name\":\"q-b492f150\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"pipeline\"},\"name\":\"group-pipeline\"},{\"type\":1,\"content\":{\"json\":\"
Tailscale Operations (Standard) (CCF) - Microsoft Sentinel content from the Tailscale (CCF) solution, Standard-tier surface. Tables polled from the Tailscale REST API: audit, devices, users, keys, dns, settings. Filter every panel via the time range above; the Investigate tab adds Actor and Device pickers for drilldown. For network flow logs and posture integrations, install the companion Tailscale Operations (Premium) workbook on a Premium / Enterprise tailnet.
\"},\"name\":\"footer\"}],\"fallbackResourceIds\":[\"Azure Monitor\"],\"fromTemplateId\":\"sentinel-Tailscale-CCF\",\"$schema\":\"https://github.com/Microsoft/Application-Insights-Workbooks/blob/master/schema/workbook.json\"}\n", "version": "1.0", "sourceId": "[variables('workspaceResourceId')]", "category": "sentinel" @@ -8481,7 +8481,7 @@ "apiVersion": "2022-01-01-preview", "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('Workbook-', last(split(variables('workbookId1'),'/'))))]", "properties": { - "description": "@{workbookKey=TailscaleStandardOperationsWorkbook; logoFileName=Tailscale.svg; description=Tailscale Operations workbook for Standard tier - device inventory, user roles, auth keys, DNS configuration, audit activity, and tailnet health overview.; dataTypesDependencies=System.Object[]; dataConnectorsDependencies=System.Object[]; previewImagesFileNames=System.Object[]; version=1.0.0; title=Tailscale Operations (Standard); templateRelativePath=TailscaleStandardOperations.json; subtitle=; provider=Community; support=; source=; categories=}.description", + "description": "@{workbookKey=TailscaleStandardOperationsWorkbook; logoFileName=Tailscale.svg; description=Tailscale Operations workbook for Standard tier. Nine tabs covering an at-a-glance KPI hero row, audit activity overview, an actor + device drilldown (Investigate), embedded hunting queries (first-seen actors, off-hours changes, key-expiry-disabled devices, never-expire auth keys, outdated clients, dormant devices, subnet route exposure, SSH-enabled devices), identity (user roles, status, last login recency, role escalation history, orphaned users), devices (OS / version / tag distribution, devices needing attention, full inventory, subnet routers), credentials (expiry timeline, never-expire flag, CRUD events), admin audit (action heatmap, actor x action heatmap, recent 100), network and DNS (current snapshot, tailnet policy gates, ACL change history), and pipeline health (per-table freshness, ingest rate, operational events). Driven by data polled from the Tailscale REST API.; dataTypesDependencies=System.Object[]; dataConnectorsDependencies=System.Object[]; previewImagesFileNames=System.Object[]; version=1.0.0; title=Tailscale Operations (Standard); templateRelativePath=TailscaleStandardOperations.json; subtitle=; provider=Community; support=; source=; categories=}.description", "parentId": "[variables('workbookId1')]", "contentId": "[variables('_workbookContentId1')]", "kind": "Workbook", @@ -8564,7 +8564,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscalePremiumOperations Workbook with template version 3.0.4", + "description": "TailscalePremiumOperations Workbook with template version 3.0.5", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('workbookVersion2')]", @@ -8680,7 +8680,7 @@ "apiVersion": "2023-04-01-preview", "location": "[parameters('workspace-location')]", "properties": { - "version": "3.0.4", + "version": "3.0.5", "kind": "Solution", "contentSchemaVersion": "3.0.0", "displayName": "Tailscale (CCF)", diff --git a/Solutions/Tailscale (CCF)/Workbooks/TailscaleStandardOperations.json b/Solutions/Tailscale (CCF)/Workbooks/TailscaleStandardOperations.json index 4604d91f4f9..906a5b57f50 100644 --- a/Solutions/Tailscale (CCF)/Workbooks/TailscaleStandardOperations.json +++ b/Solutions/Tailscale (CCF)/Workbooks/TailscaleStandardOperations.json @@ -7,7 +7,7 @@ "version": "KqlParameterItem/1.0", "parameters": [ { - "id": "97a71946-815e-4519-a889-a10d455eafd6", + "id": "7b05a598-5120-43f4-bf5d-576c2a7ff28d", "version": "KqlParameterItem/1.0", "name": "TimeRange", "type": 4, @@ -51,10 +51,43 @@ { "type": 1, "content": { - "json": "
Tailscale Standard (CCF)
Comprehensive visibility into a Tailscale tailnet on Personal (Free) and Standard tiers. Audit events, device inventory, users, credentials, DNS and tailnet settings polled from the Tailscale API. Scope every panel with the time range below.
" + "json": "
Tailscale Operations (Standard)
Single-pane visibility into your Tailscale tailnet on Personal (Free), Starter and Standard tiers: who, what, when, where, and what changed. Scope every panel with the time range below; the Investigate tab adds Actor and Device pickers for drilldown. Premium-tier panels (network flow logs, posture integrations) live in the separate Tailscale Operations (Premium) workbook.
" }, "name": "header" }, + { + "type": 1, + "content": { + "json": "
Tailnet at a glance
" + }, + "name": "div-tailnet-at-a-glance-04e62b" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "let DEV = Tailscale_Devices_CL | summarize arg_max(TimeGenerated, *) by DeviceId;\nlet USR = Tailscale_Users_CL | summarize arg_max(TimeGenerated, *) by UserId;\nlet KEY = Tailscale_Keys_CL | summarize arg_max(TimeGenerated, *) by KeyId;\nunion\n (DEV | summarize V=toreal(count()) | extend Metric=\"Devices\", Order=1),\n (DEV | where Authorized == true | summarize V=toreal(count()) | extend Metric=\"Authorized\", Order=2),\n (DEV | where UpdateAvailable == true | summarize V=toreal(count()) | extend Metric=\"Updates Available\", Order=3),\n (DEV | where SshEnabled == true | summarize V=toreal(count()) | extend Metric=\"SSH-Enabled\", Order=4),\n (USR | summarize V=toreal(count()) | extend Metric=\"Users\", Order=5),\n (USR | where Role =~ \"admin\" or Role =~ \"owner\" or Role =~ \"network-admin\" | summarize V=toreal(count()) | extend Metric=\"Admins\", Order=6),\n (KEY | where isnull(Revoked) and (isnull(Expires) or Expires > now()) | summarize V=toreal(count()) | extend Metric=\"Active Keys\", Order=7),\n (Tailscale_Audit_CL | where TimeGenerated {TimeRange} | summarize V=toreal(count()) | extend Metric=\"Audit Events ({TimeRange:label})\", Order=8)\n| order by Order asc | project Metric, Value=V", + "size": 3, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "tiles", + "tileSettings": { + "titleContent": { + "columnMatch": "Metric", + "formatter": 1 + }, + "leftContent": { + "columnMatch": "Value", + "formatter": 12, + "formatOptions": { + "palette": "auto" + } + }, + "showBorder": false + } + }, + "name": "q-4e9d7cff" + }, { "type": 11, "content": { @@ -62,7 +95,7 @@ "style": "tabs", "links": [ { - "id": "9505e7fb-6622-4014-8686-14f8432cf042", + "id": "9794e9fd-916b-494d-8e72-af63d2f4c6c7", "cellValue": "selectedTab", "linkTarget": "parameter", "linkLabel": "Overview", @@ -70,59 +103,67 @@ "style": "link" }, { - "id": "1a6c8fea-a005-4fc4-a09a-4e99bb459744", + "id": "71cf49db-33c5-4d4b-920a-2ec0c6a258dc", "cellValue": "selectedTab", "linkTarget": "parameter", - "linkLabel": "Devices", - "subTarget": "devices", + "linkLabel": "Investigate", + "subTarget": "investigate", "style": "link" }, { - "id": "9b499cf5-007a-4e78-a541-94edbd5963ed", + "id": "5c56bb37-2053-47a0-b6fd-9539768c144d", "cellValue": "selectedTab", "linkTarget": "parameter", - "linkLabel": "Users & Identity", - "subTarget": "users", + "linkLabel": "Hunts", + "subTarget": "hunts", "style": "link" }, { - "id": "29accdba-1ce7-4b56-a054-fa69eced7886", + "id": "d2004ded-07f8-446a-a720-f0a63d1d9dda", "cellValue": "selectedTab", "linkTarget": "parameter", - "linkLabel": "Credentials", - "subTarget": "credentials", + "linkLabel": "Identity", + "subTarget": "identity", "style": "link" }, { - "id": "10a5ef86-e291-4ec9-804b-1d95a35b82a3", + "id": "f23b3e14-1511-4a29-bf5e-bd65e55dbb40", "cellValue": "selectedTab", "linkTarget": "parameter", - "linkLabel": "DNS & Tailnet", - "subTarget": "dns", + "linkLabel": "Devices", + "subTarget": "devices", "style": "link" }, { - "id": "b75f9e0e-7135-4ec7-8d82-13505b3f1f31", + "id": "724f8352-e21b-45a6-9029-39dc92693c05", "cellValue": "selectedTab", "linkTarget": "parameter", - "linkLabel": "Webhooks", - "subTarget": "webhooks", + "linkLabel": "Credentials", + "subTarget": "credentials", "style": "link" }, { - "id": "77485d43-6f26-4ad6-afce-e76e4710820f", + "id": "8400ec64-e118-44e0-ae29-84afe94b8e0e", "cellValue": "selectedTab", "linkTarget": "parameter", - "linkLabel": "Audit Activity", + "linkLabel": "Admin Audit", "subTarget": "audit", "style": "link" }, { - "id": "99788080-b885-4220-9190-ca3546528cfe", + "id": "b7e04861-edfa-4426-b6be-f1481ca569b6", "cellValue": "selectedTab", "linkTarget": "parameter", - "linkLabel": "Security Signals", - "subTarget": "signals", + "linkLabel": "Network & DNS", + "subTarget": "network", + "style": "link" + }, + { + "id": "cfbc96e4-8585-4d89-8f65-8344c8cc6eb2", + "cellValue": "selectedTab", + "linkTarget": "parameter", + "linkLabel": "Pipeline Health", + "subTarget": "pipeline", "style": "link" } ] @@ -138,15 +179,552 @@ { "type": 1, "content": { - "json": "
At a glance
" + "json": "
Audit activity over time
" + }, + "name": "div-audit-activity-over--546688" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| summarize EventCount = count() by bin(TimeGenerated, 1h), Action\n| order by TimeGenerated asc", + "size": 0, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "timechart", + "title": "Audit events by action", + "noDataMessage": "No audit events in the selected window. Widen the time range; remember the Tailscale audit poll runs every ~30 min.", + "noDataMessageStyle": 5 + }, + "name": "q-effcd498" + }, + { + "type": 1, + "content": { + "json": "
Who's doing what
" + }, + "name": "div-who's-doing-what-52144e" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| extend Actor=tostring(coalesce(Actor.loginName, Actor.displayName, Actor.type))\n| where isnotempty(Actor)\n| summarize Events=count(), DistinctActions=dcount(Action), LastSeen=max(TimeGenerated) by Actor\n| order by Events desc | take 15", + "size": 0, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "table", + "title": "Top actors (by event count)", + "gridSettings": { + "formatters": [ + { + "columnMatch": "Events", + "formatter": 8, + "formatOptions": { + "palette": "blue" + } + }, + { + "columnMatch": "LastSeen", + "formatter": 6 + } + ] + } + }, + "name": "q-34c77fa9", + "customWidth": "50" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| extend TargetType=tostring(Target.type)\n| where isnotempty(TargetType)\n| summarize Events=count() by TargetType\n| order by Events desc | take 15", + "size": 0, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "piechart", + "title": "Activity by target type" + }, + "name": "q-4b3b709d", + "customWidth": "50" + }, + { + "type": 1, + "content": { + "json": "
Recent admin events
" + }, + "name": "div-recent-admin-events-87c9e0" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| extend Actor=tostring(coalesce(Actor.loginName, Actor.displayName, Actor.type))\n| extend TargetType=tostring(Target.type), TargetName=tostring(coalesce(Target.name, Target.id))\n| project TimeGenerated, Action, Actor, TargetType, TargetName, Origin\n| order by TimeGenerated desc | take 30", + "size": 0, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "table", + "title": "Most recent 30 audit events", + "gridSettings": { + "formatters": [ + { + "columnMatch": "TimeGenerated", + "formatter": 6 + } + ] + } + }, + "name": "q-5e7d6306" + } + ] + }, + "conditionalVisibility": { + "parameterName": "selectedTab", + "comparison": "isEqualTo", + "value": "overview" + }, + "name": "group-overview" + }, + { + "type": 12, + "content": { + "version": "NotebookGroup/1.0", + "groupType": "editable", + "items": [ + { + "type": 9, + "content": { + "version": "KqlParameterItem/1.0", + "parameters": [ + { + "id": "b83a25c1-da18-49a2-a444-6517f13d891c", + "version": "KqlParameterItem/1.0", + "name": "SelectedActor", + "label": "Actor", + "type": 2, + "isRequired": false, + "query": "Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\n| where isnotempty(ActorLogin)\n| summarize Events=count() by ActorLogin\n| order by Events desc | take 50\n| project value=ActorLogin, label=strcat(ActorLogin, \" (\", Events, \")\")", + "typeSettings": { + "additionalResourceOptions": [ + "value::all" + ], + "showDefault": false + }, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "value": "value::all" + }, + { + "id": "a9cf7907-f201-4725-a072-a8bd34bef74e", + "version": "KqlParameterItem/1.0", + "name": "SelectedDevice", + "label": "Device", + "type": 2, + "isRequired": false, + "query": "Tailscale_Devices_CL\n| summarize arg_max(TimeGenerated, *) by DeviceId\n| order by LastSeen desc | take 100\n| project value=DeviceName, label=strcat(coalesce(DeviceName, Hostname), \" (\", User, \")\")", + "typeSettings": { + "additionalResourceOptions": [ + "value::all" + ], + "showDefault": false + }, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "value": "value::all" + } + ], + "style": "pills", + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces" + }, + "name": "investigate-pickers" + }, + { + "type": 1, + "content": { + "json": "
Actor activity timeline
" + }, + "name": "div-actor-activity-timel-5ec305" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\n| where \"{SelectedActor}\" == \"value::all\" or ActorLogin == \"{SelectedActor}\"\n| summarize Events=count() by bin(TimeGenerated, 1h), Action\n| order by TimeGenerated asc", + "size": 0, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "timechart", + "title": "Actions over time for selected actor (or all actors)", + "noDataMessage": "Select an actor from the Actor dropdown above, or leave on 'All' to see total activity.", + "noDataMessageStyle": 5 + }, + "name": "q-32d40848" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\n| where \"{SelectedActor}\" == \"value::all\" or ActorLogin == \"{SelectedActor}\"\n| extend TargetType=tostring(Target.type), TargetName=tostring(coalesce(Target.name, Target.id))\n| project TimeGenerated, ActorLogin, Action, TargetType, TargetName, Origin\n| order by TimeGenerated desc | take 100", + "size": 0, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "table", + "title": "Events from selected actor", + "gridSettings": { + "formatters": [ + { + "columnMatch": "TimeGenerated", + "formatter": 6 + } + ] + }, + "noDataMessage": "No events for this actor in the selected window.", + "noDataMessageStyle": 5 + }, + "name": "q-a742f6fd" + }, + { + "type": 1, + "content": { + "json": "
Selected device timeline
" + }, + "name": "div-selected-device-time-00bead" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_Devices_CL\n| where \"{SelectedDevice}\" == \"value::all\" or DeviceName == \"{SelectedDevice}\"\n| summarize arg_max(TimeGenerated, *) by DeviceId\n| extend OnlineNow = ClientConnectivity.endpoints != \"\" or ConnectedToControl == true\n| project DeviceName, Hostname, User, Os, ClientVersion, UpdateAvailable, Authorized, IsExternal, SshEnabled, LastSeen, Expires, KeyExpiryDisabled, OnlineNow, Addresses, Tags, AdvertisedRoutes", + "size": 0, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "table", + "title": "Device summary", + "noDataMessage": "Select a device from the Device dropdown above. Defaults to 'All'.", + "noDataMessageStyle": 5 + }, + "name": "q-11094dc8" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| extend TargetType=tostring(Target.type), TargetName=tostring(Target.name), TargetId=tostring(Target.id)\n| where (\"{SelectedDevice}\" == \"value::all\" and TargetType == \"NODE\") or TargetName == \"{SelectedDevice}\"\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\n| project TimeGenerated, Action, ActorLogin, TargetName, TargetId, Origin\n| order by TimeGenerated desc | take 100", + "size": 0, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "table", + "title": "Audit events touching this device", + "gridSettings": { + "formatters": [ + { + "columnMatch": "TimeGenerated", + "formatter": 6 + } + ] + }, + "noDataMessage": "No audit events recorded against the selected device in this window. Tailscale tags device events with Target.type=NODE; the audit feed only emits NODE events on create/update/delete, so quiet devices stay quiet here.", + "noDataMessageStyle": 5 + }, + "name": "q-ead106ce" + } + ] + }, + "conditionalVisibility": { + "parameterName": "selectedTab", + "comparison": "isEqualTo", + "value": "investigate" + }, + "name": "group-investigate" + }, + { + "type": 12, + "content": { + "version": "NotebookGroup/1.0", + "groupType": "editable", + "items": [ + { + "type": 1, + "content": { + "json": "
First-seen actors in the last 24h
" + }, + "name": "div-first-seen-actors-in-ce38d9" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "let recent = Tailscale_Audit_CL | where TimeGenerated > ago(24h) | extend A=tostring(coalesce(Actor.loginName, Actor.displayName)) | summarize FirstSeen24h=min(TimeGenerated), Events=count() by A;\nlet historical = Tailscale_Audit_CL | where TimeGenerated between(ago(30d) .. ago(24h)) | extend A=tostring(coalesce(Actor.loginName, Actor.displayName)) | distinct A;\nrecent | join kind=leftanti historical on A | where isnotempty(A) | project FirstSeen24h, Actor=A, Events | order by Events desc", + "size": 0, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "table", + "title": "Actors who have NEVER appeared before (30d baseline)", + "gridSettings": { + "formatters": [ + { + "columnMatch": "FirstSeen24h", + "formatter": 6 + }, + { + "columnMatch": "Events", + "formatter": 8, + "formatOptions": { + "palette": "orange" + } + } + ] + }, + "noDataMessage": "Every actor seen in the last 24h has appeared at least once in the prior 30d. Healthy state.", + "noDataMessageStyle": 5 + }, + "name": "q-9ba7b85e" + }, + { + "type": 1, + "content": { + "json": "
Off-hours configuration changes
" }, - "name": "div-at-a-glance-a23e75" + "name": "div-off-hours-configurat-3c3787" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "union (Tailscale_Devices_CL | where TimeGenerated {TimeRange} | summarize arg_max(TimeGenerated, *) by DeviceId | summarize V=toreal(count()) | extend Metric=\"Devices\", Order=1),(Tailscale_Users_CL | where TimeGenerated {TimeRange} | summarize arg_max(TimeGenerated, *) by UserId | summarize V=toreal(count()) | extend Metric=\"Users\", Order=2),(Tailscale_Keys_CL | where TimeGenerated {TimeRange} | summarize arg_max(TimeGenerated, *) by KeyId | summarize V=toreal(countif(isnull(Revoked))) | extend Metric=\"Active keys\", Order=3),(Tailscale_Audit_CL | where TimeGenerated {TimeRange} | summarize V=toreal(count()) | extend Metric=\"Audit events\", Order=4),(Tailscale_Audit_CL | where TimeGenerated {TimeRange} | where tostring(Target.property) == \"ACL\" or tostring(Target.property) == \"DNS_SPLIT_DNS\" or tostring(Target.property) == \"DNS_NAMESERVERS\" or tostring(Target.property) == \"MAGICDNS_PREFERENCES\" | summarize V=toreal(count()) | extend Metric=\"DNS / ACL changes\", Order=5)\n| order by Order asc | project Metric, Value=V", + "query": "Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| extend Hour=hourofday(TimeGenerated), DayOfWeek=dayofweek(TimeGenerated)/1d\n| where Hour < 7 or Hour > 19 or DayOfWeek in (0, 6)\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\n| extend TargetType=tostring(Target.type)\n| where Action !in (\"LOGIN\", \"LOGOUT\")\n| project TimeGenerated, ActorLogin, Action, TargetType, Origin\n| order by TimeGenerated desc", + "size": 0, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "table", + "title": "Admin actions outside 07:00-19:00 weekdays", + "gridSettings": { + "formatters": [ + { + "columnMatch": "TimeGenerated", + "formatter": 6 + } + ] + }, + "noDataMessage": "No off-hours admin changes recorded - healthy state for an organisation working business hours.", + "noDataMessageStyle": 5 + }, + "name": "q-721ee490" + }, + { + "type": 1, + "content": { + "json": "
Devices with key expiry disabled
" + }, + "name": "div-devices-with-key-exp-dfe9c1" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_Devices_CL\n| summarize arg_max(TimeGenerated, *) by DeviceId\n| where KeyExpiryDisabled == true\n| project DeviceName, Hostname, User, Os, ClientVersion, LastSeen, Authorized, Tags\n| order by LastSeen desc", + "size": 0, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "table", + "title": "Devices that will never re-authenticate (high-risk drift)", + "gridSettings": { + "formatters": [ + { + "columnMatch": "LastSeen", + "formatter": 6 + } + ] + }, + "noDataMessage": "No devices have key expiry disabled - good. Disabling key expiry creates devices that never re-auth, drifting from policy.", + "noDataMessageStyle": 5 + }, + "name": "q-8a8e2fb5" + }, + { + "type": 1, + "content": { + "json": "
Auth keys with no expiry
" + }, + "name": "div-auth-keys-with-no-ex-b11207" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_Keys_CL\n| summarize arg_max(TimeGenerated, *) by KeyId\n| where isnull(Revoked) and (isnull(Expires) or ExpirySeconds == 0)\n| project KeyId, Description, UserId, KeyType, Created, Capabilities\n| order by Created desc", + "size": 0, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "table", + "title": "Active keys that never expire", + "gridSettings": { + "formatters": [ + { + "columnMatch": "Created", + "formatter": 6 + } + ] + }, + "noDataMessage": "No never-expiring auth keys - rotation hygiene is good.", + "noDataMessageStyle": 5 + }, + "name": "q-1372740c" + }, + { + "type": 1, + "content": { + "json": "
Devices running outdated clients
" + }, + "name": "div-devices-running-outd-bcd077" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_Devices_CL\n| summarize arg_max(TimeGenerated, *) by DeviceId\n| where UpdateAvailable == true\n| project DeviceName, Hostname, User, Os, ClientVersion, LastSeen, Tags\n| order by LastSeen desc", + "size": 0, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "table", + "title": "Devices flagged update-available by Tailscale", + "gridSettings": { + "formatters": [ + { + "columnMatch": "LastSeen", + "formatter": 6 + } + ] + }, + "noDataMessage": "All devices on current client - nothing to patch.", + "noDataMessageStyle": 5 + }, + "name": "q-ecebf1f7" + }, + { + "type": 1, + "content": { + "json": "
Dormant devices (LastSeen > 30 days)
" + }, + "name": "div-dormant-devices-(las-761156" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_Devices_CL\n| summarize arg_max(TimeGenerated, *) by DeviceId\n| where LastSeen < ago(30d)\n| extend DaysIdle = toint((now() - LastSeen) / 1d)\n| project DeviceName, Hostname, User, Os, ClientVersion, LastSeen, DaysIdle, Authorized, Tags\n| order by DaysIdle desc", + "size": 0, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "table", + "title": "Devices idle 30+ days - candidates for retirement", + "gridSettings": { + "formatters": [ + { + "columnMatch": "LastSeen", + "formatter": 6 + }, + { + "columnMatch": "DaysIdle", + "formatter": 8, + "formatOptions": { + "palette": "redBright" + } + } + ] + }, + "noDataMessage": "No devices idle 30+ days - inventory is fresh.", + "noDataMessageStyle": 5 + }, + "name": "q-fb6c1fcc" + }, + { + "type": 1, + "content": { + "json": "
Subnet route exposure
" + }, + "name": "div-subnet-route-exposur-289c42" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_Devices_CL\n| summarize arg_max(TimeGenerated, *) by DeviceId\n| where array_length(AdvertisedRoutes) > 0 or array_length(EnabledRoutes) > 0\n| extend Routes = tostring(EnabledRoutes), Advertised = tostring(AdvertisedRoutes)\n| project DeviceName, Hostname, User, Os, Advertised, Routes, LastSeen, SshEnabled, Authorized\n| order by LastSeen desc", + "size": 0, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "table", + "title": "Devices advertising or running subnet routes / exit-node duty", + "gridSettings": { + "formatters": [ + { + "columnMatch": "LastSeen", + "formatter": 6 + } + ] + }, + "noDataMessage": "No devices advertising subnet routes. Pure mesh topology.", + "noDataMessageStyle": 5 + }, + "name": "q-9533b081" + }, + { + "type": 1, + "content": { + "json": "
Devices with SSH enabled
" + }, + "name": "div-devices-with-ssh-ena-285238" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_Devices_CL\n| summarize arg_max(TimeGenerated, *) by DeviceId\n| where SshEnabled == true\n| project DeviceName, Hostname, User, Os, ClientVersion, LastSeen, Authorized, Tags\n| order by LastSeen desc", + "size": 0, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "table", + "title": "Devices with Tailscale SSH enabled (Tailscale-managed remote-shell access)", + "gridSettings": { + "formatters": [ + { + "columnMatch": "LastSeen", + "formatter": 6 + } + ] + }, + "noDataMessage": "No devices have Tailscale SSH enabled - no SSH-via-Tailscale risk surface.", + "noDataMessageStyle": 5 + }, + "name": "q-735368fb" + } + ] + }, + "conditionalVisibility": { + "parameterName": "selectedTab", + "comparison": "isEqualTo", + "value": "hunts" + }, + "name": "group-hunts" + }, + { + "type": 12, + "content": { + "version": "NotebookGroup/1.0", + "groupType": "editable", + "items": [ + { + "type": 1, + "content": { + "json": "
User inventory snapshot
" + }, + "name": "div-user-inventory-snaps-9dc96c" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "let U = Tailscale_Users_CL | summarize arg_max(TimeGenerated, *) by UserId;\nunion\n (U | summarize V=toreal(count()) | extend Metric=\"Total users\", Order=1),\n (U | where Role in~ (\"admin\",\"owner\",\"network-admin\",\"it-admin\",\"billing-admin\") | summarize V=toreal(count()) | extend Metric=\"Admin-tier users\", Order=2),\n (U | where Status =~ \"active\" | summarize V=toreal(count()) | extend Metric=\"Active\", Order=3),\n (U | where CurrentlyConnected == true | summarize V=toreal(count()) | extend Metric=\"Connected now\", Order=4),\n (U | where Status =~ \"idle\" or LastSeen < ago(30d) | summarize V=toreal(count()) | extend Metric=\"Idle / dormant\", Order=5),\n (U | where UserType =~ \"shared\" | summarize V=toreal(count()) | extend Metric=\"Shared (external)\", Order=6)\n| order by Order asc | project Metric, Value=V", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", @@ -166,71 +744,200 @@ "showBorder": false } }, - "name": "q-97c91b94" + "name": "q-5689d9e8" }, { "type": 1, "content": { - "json": "
Activity over time
" + "json": "
Distribution
" }, - "name": "div-activity-over-time-cad2b2" + "name": "div-distribution-de67ec" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| summarize EventCount = count() by bin(TimeGenerated, 1h), Action", + "query": "Tailscale_Users_CL\n| summarize arg_max(TimeGenerated, *) by UserId\n| summarize Count=count() by Role\n| order by Count desc", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", - "title": "Audit events by action", - "visualization": "timechart" + "visualization": "piechart", + "title": "Users by role" + }, + "name": "q-0c47912a", + "customWidth": "33" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_Users_CL\n| summarize arg_max(TimeGenerated, *) by UserId\n| summarize Count=count() by Status\n| order by Count desc", + "size": 0, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "piechart", + "title": "Users by status" + }, + "name": "q-e8c20a69", + "customWidth": "33" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_Users_CL\n| summarize arg_max(TimeGenerated, *) by UserId\n| summarize Count=count() by UserType\n| order by Count desc", + "size": 0, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "piechart", + "title": "Users by type (member / shared)" + }, + "name": "q-2c51776d", + "customWidth": "33" + }, + { + "type": 1, + "content": { + "json": "
Activity heatmap
" + }, + "name": "div-activity-heatmap-ca6a21" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_Users_CL\n| summarize arg_max(TimeGenerated, *) by UserId\n| extend DaysSinceLogin = toint((now() - LastSeen) / 1d)\n| extend Bucket = case(\n DaysSinceLogin < 1, \"Today\",\n DaysSinceLogin < 7, \"This week\",\n DaysSinceLogin < 30, \"This month\",\n DaysSinceLogin < 90, \"Past quarter\",\n \"90+ days\")\n| summarize Users=count() by Bucket\n| order by case(Bucket==\"Today\",1, Bucket==\"This week\",2, Bucket==\"This month\",3, Bucket==\"Past quarter\",4, 5) asc", + "size": 0, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "barchart", + "title": "Users by recency of last login" + }, + "name": "q-350e118d" + }, + { + "type": 1, + "content": { + "json": "
Full user list
" + }, + "name": "div-full-user-list-136aac" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_Users_CL\n| summarize arg_max(TimeGenerated, *) by UserId\n| project DisplayName, LoginName, Role, Status, UserType, DeviceCount, CurrentlyConnected, Created, LastSeen\n| order by LastSeen desc", + "size": 0, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "table", + "title": "All users (latest snapshot per user ID)", + "gridSettings": { + "formatters": [ + { + "columnMatch": "Created", + "formatter": 6 + }, + { + "columnMatch": "LastSeen", + "formatter": 6 + }, + { + "columnMatch": "Role", + "formatter": 11 + }, + { + "columnMatch": "Status", + "formatter": 11 + }, + { + "columnMatch": "DeviceCount", + "formatter": 8, + "formatOptions": { + "palette": "blue" + } + } + ] + } }, - "name": "q-b6555f0f" + "name": "q-cee23d3c" }, { "type": 1, "content": { - "json": "
Top actors
" + "json": "
Orphaned users (active but no devices)
" }, - "name": "div-top-actors-8776bb" + "name": "div-orphaned-users-(acti-56d6f8" }, { "type": 3, "content": { - "version": "KqlItem/1.0", - "query": "Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| extend ActorLogin = tostring(Actor.loginName)\n| where isnotempty(ActorLogin)\n| summarize EventCount = count() by ActorLogin\n| top 10 by EventCount\n| render barchart", - "size": 0, - "queryType": 0, - "resourceType": "microsoft.operationalinsights/workspaces", - "title": "Top 10 actors", - "visualization": "table" + "version": "KqlItem/1.0", + "query": "Tailscale_Users_CL\n| summarize arg_max(TimeGenerated, *) by UserId\n| where Status =~ \"active\" and DeviceCount == 0\n| project DisplayName, LoginName, Role, UserType, Created, LastSeen\n| order by Created desc", + "size": 0, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "table", + "title": "Active accounts with zero devices - candidates for offboarding review", + "gridSettings": { + "formatters": [ + { + "columnMatch": "Created", + "formatter": 6 + }, + { + "columnMatch": "LastSeen", + "formatter": 6 + } + ] + }, + "noDataMessage": "Every active account has at least one device - good hygiene.", + "noDataMessageStyle": 5 + }, + "name": "q-83fcd942" + }, + { + "type": 1, + "content": { + "json": "
Role escalation history
" }, - "name": "q-91e46bcd", - "customWidth": "50" + "name": "div-role-escalation-hist-bd8df4" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| summarize EventCount = count() by TargetType = tostring(Target.type)\n| top 10 by EventCount\n| render piechart", + "query": "Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| where Action == \"USER_ROLE_UPDATE\" or Action == \"USER_ROLES_ASSIGNED\" or Action contains \"ROLE\"\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\n| extend TargetName=tostring(coalesce(Target.name, Target.id))\n| extend FromRole=tostring(Old.role), ToRole=tostring(New.role)\n| project TimeGenerated, ActorLogin, Action, TargetName, FromRole, ToRole, Origin\n| order by TimeGenerated desc", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", - "title": "Activity by target type", - "visualization": "table" + "visualization": "table", + "title": "Recent role changes", + "gridSettings": { + "formatters": [ + { + "columnMatch": "TimeGenerated", + "formatter": 6 + }, + { + "columnMatch": "ToRole", + "formatter": 11 + } + ] + }, + "noDataMessage": "No role changes in this window.", + "noDataMessageStyle": 5 }, - "name": "q-226763e3", - "customWidth": "50" + "name": "q-f6c8358a" } ] }, "conditionalVisibility": { "parameterName": "selectedTab", "comparison": "isEqualTo", - "value": "overview" + "value": "identity" }, - "name": "group-overview" + "name": "group-identity" }, { "type": 12, @@ -241,15 +948,15 @@ { "type": 1, "content": { - "json": "
Device inventory snapshot
" + "json": "
Device fleet snapshot
" }, - "name": "div-device-inventory-snapshot-9aebf5" + "name": "div-device-fleet-snapsho-939675" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "let base = Tailscale_Devices_CL | summarize arg_max(TimeGenerated, *) by DeviceId;\nunion (base | summarize V=toreal(count()) | extend Metric=\"Total devices\", Order=1),(base | where Authorized == true | summarize V=toreal(count()) | extend Metric=\"Authorized\", Order=2),(base | where IsExternal == true | summarize V=toreal(count()) | extend Metric=\"External (shared)\", Order=3),(base | where KeyExpiryDisabled == true | summarize V=toreal(count()) | extend Metric=\"Key expiry disabled\", Order=4),(base | where UpdateAvailable == true | summarize V=toreal(count()) | extend Metric=\"Update available\", Order=5),(base | where array_length(AdvertisedRoutes) > 0 | summarize V=toreal(count()) | extend Metric=\"Subnet routers\", Order=6),(base | where LastSeen < ago(30d) | summarize V=toreal(count()) | extend Metric=\"Stale (30+ days)\", Order=7)\n| order by Order asc | project Metric, Value=V", + "query": "let D = Tailscale_Devices_CL | summarize arg_max(TimeGenerated, *) by DeviceId;\nunion\n (D | summarize V=toreal(count()) | extend Metric=\"Total devices\", Order=1),\n (D | where Authorized == true | summarize V=toreal(count()) | extend Metric=\"Authorized\", Order=2),\n (D | where IsExternal == true | summarize V=toreal(count()) | extend Metric=\"External (shared)\", Order=3),\n (D | where UpdateAvailable == true | summarize V=toreal(count()) | extend Metric=\"Updates available\", Order=4),\n (D | where SshEnabled == true | summarize V=toreal(count()) | extend Metric=\"SSH-enabled\", Order=5),\n (D | where KeyExpiryDisabled == true | summarize V=toreal(count()) | extend Metric=\"No key expiry\", Order=6),\n (D | where array_length(AdvertisedRoutes) > 0 | summarize V=toreal(count()) | extend Metric=\"Subnet/exit-node\", Order=7),\n (D | where LastSeen < ago(30d) | summarize V=toreal(count()) | extend Metric=\"Stale (30+ days)\", Order=8)\n| order by Order asc | project Metric, Value=V", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", @@ -269,238 +976,165 @@ "showBorder": false } }, - "name": "q-2c8bcfb1" + "name": "q-366964fc" }, { "type": 1, "content": { "json": "
Distribution
" }, - "name": "div-distribution-c08880" + "name": "div-distribution-396d03" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "Tailscale_Devices_CL\n| summarize arg_max(TimeGenerated, *) by DeviceId\n| summarize Count = count() by Os\n| order by Count desc", + "query": "Tailscale_Devices_CL\n| summarize arg_max(TimeGenerated, *) by DeviceId\n| summarize Count=count() by Os\n| order by Count desc", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", - "title": "Devices by OS", - "visualization": "piechart" + "visualization": "piechart", + "title": "Devices by OS" }, - "name": "q-8435df72", + "name": "q-0c8f5988", "customWidth": "33" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "Tailscale_Devices_CL\n| summarize arg_max(TimeGenerated, *) by DeviceId\n| summarize Count = count() by ClientVersion\n| order by Count desc", + "query": "Tailscale_Devices_CL\n| summarize arg_max(TimeGenerated, *) by DeviceId\n| summarize Count=count() by ClientVersion\n| order by Count desc | take 10", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", - "title": "Devices by client version", - "visualization": "barchart" + "visualization": "barchart", + "title": "Top 10 client versions" }, - "name": "q-95a9414c", + "name": "q-af1c45cd", "customWidth": "33" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "Tailscale_Devices_CL\n| summarize arg_max(TimeGenerated, *) by DeviceId\n| mv-expand Tag = Tags to typeof(string)\n| summarize Devices = dcount(DeviceId) by Tag = iff(isempty(Tag), \"(untagged)\", Tag)\n| order by Devices desc", + "query": "Tailscale_Devices_CL\n| summarize arg_max(TimeGenerated, *) by DeviceId\n| mv-expand Tag = Tags to typeof(string)\n| summarize Devices=dcount(DeviceId) by Tag=iff(isempty(Tag), \"(untagged)\", Tag)\n| order by Devices desc", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", - "title": "Devices by tag", - "visualization": "piechart" + "visualization": "piechart", + "title": "Devices by tag" }, - "name": "q-7e72903f", + "name": "q-c3a0ef5b", "customWidth": "33" }, { "type": 1, "content": { - "json": "
Device inventory table
" + "json": "
Devices needing attention
" }, - "name": "div-device-inventory-table-d1a89d" + "name": "div-devices-needing-atte-f04a47" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "Tailscale_Devices_CL\n| summarize arg_max(TimeGenerated, *) by DeviceId\n| extend DaysSinceSeen = datetime_diff('day', now(), LastSeen)\n| extend DaysToExpiry = iff(KeyExpiryDisabled == true, int(null), datetime_diff('day', Expires, now()))\n| project DeviceName, Hostname, User, Os, ClientVersion, UpdateAvailable, LastSeen, DaysSinceSeen, DaysToExpiry, KeyExpiryDisabled, Tags, AdvertisedRoutes\n| order by LastSeen desc", + "query": "Tailscale_Devices_CL\n| summarize arg_max(TimeGenerated, *) by DeviceId\n| where UpdateAvailable == true or KeyExpiryDisabled == true or LastSeen < ago(30d) or Authorized == false\n| extend Issues = strcat_array(pack_array(\n iff(UpdateAvailable == true, \"needs-update\", \"\"),\n iff(KeyExpiryDisabled == true, \"key-never-expires\", \"\"),\n iff(LastSeen < ago(30d), \"stale\", \"\"),\n iff(Authorized == false, \"unauthorized\", \"\")), \",\")\n| extend Issues = trim(\",\", trim_start(\",\", trim_end(\",\", replace_string(Issues, \",,\", \",\"))))\n| project DeviceName, Hostname, User, Os, ClientVersion, LastSeen, Issues\n| order by LastSeen desc", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", - "title": "All devices (latest snapshot per device)", "visualization": "table", + "title": "Devices flagged with one or more issues", "gridSettings": { "formatters": [ { - "columnMatch": "DaysSinceSeen", - "formatter": 8, - "formatOptions": { - "palette": "redBright", - "min": 0, - "max": 60 - } - }, - { - "columnMatch": "DaysToExpiry", - "formatter": 8, - "formatOptions": { - "palette": "redBright", - "min": 0, - "max": 30 - } - }, - { - "columnMatch": "UpdateAvailable", - "formatter": 11 + "columnMatch": "LastSeen", + "formatter": 6 }, { - "columnMatch": "KeyExpiryDisabled", + "columnMatch": "Issues", "formatter": 11 } ] - } - }, - "name": "q-d7892cab" - }, - { - "type": 1, - "content": { - "json": "
Subnet routers and exit nodes
" + }, + "noDataMessage": "No devices need attention - all updated, fresh, authorized, and key-rotating.", + "noDataMessageStyle": 5 }, - "name": "div-subnet-routers-and-exit-nodes-a6fbe2" + "name": "q-dc5db84c" }, - { - "type": 3, - "content": { - "version": "KqlItem/1.0", - "query": "Tailscale_Devices_CL\n| summarize arg_max(TimeGenerated, *) by DeviceId\n| where array_length(AdvertisedRoutes) > 0 or array_length(EnabledRoutes) > 0\n| project DeviceName, User, Os, AdvertisedRoutes, EnabledRoutes, Tags, LastSeen\n| order by DeviceName asc", - "size": 0, - "queryType": 0, - "resourceType": "microsoft.operationalinsights/workspaces", - "title": "Devices advertising routes (subnet routers / exit nodes)", - "visualization": "table" - }, - "name": "q-50b65ee3" - } - ] - }, - "conditionalVisibility": { - "parameterName": "selectedTab", - "comparison": "isEqualTo", - "value": "devices" - }, - "name": "group-devices" - }, - { - "type": 12, - "content": { - "version": "NotebookGroup/1.0", - "groupType": "editable", - "items": [ { "type": 1, "content": { - "json": "
Identity overview
" + "json": "
Full device inventory
" }, - "name": "div-identity-overview-9335d8" + "name": "div-full-device-inventor-b70642" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "let base = Tailscale_Users_CL | summarize arg_max(TimeGenerated, *) by UserId;\nunion (base | summarize V=toreal(count()) | extend Metric=\"Users\", Order=1),(base | where Status =~ \"active\" | summarize V=toreal(count()) | extend Metric=\"Active\", Order=2),(base | where Status =~ \"suspended\" | summarize V=toreal(count()) | extend Metric=\"Suspended\", Order=3),(base | where CurrentlyConnected == true | summarize V=toreal(count()) | extend Metric=\"Currently connected\", Order=4),(base | where DeviceCount == 0 | summarize V=toreal(count()) | extend Metric=\"Zero devices\", Order=5),(base | where Role in (\"owner\",\"admin\",\"network-admin\") | summarize V=toreal(count()) | extend Metric=\"Elevated\", Order=6)\n| order by Order asc | project Metric, Value=V", + "query": "Tailscale_Devices_CL\n| summarize arg_max(TimeGenerated, *) by DeviceId\n| project DeviceName, Hostname, User, Os, ClientVersion, UpdateAvailable, Authorized, IsExternal, SshEnabled, LastSeen, KeyExpiryDisabled, Tags, AdvertisedRoutes\n| order by LastSeen desc", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", - "visualization": "tiles", - "tileSettings": { - "titleContent": { - "columnMatch": "Metric", - "formatter": 1 - }, - "leftContent": { - "columnMatch": "Value", - "formatter": 12, - "formatOptions": { - "palette": "auto" + "visualization": "table", + "title": "All devices (latest snapshot per device ID)", + "gridSettings": { + "formatters": [ + { + "columnMatch": "LastSeen", + "formatter": 6 + }, + { + "columnMatch": "Os", + "formatter": 11 + }, + { + "columnMatch": "ClientVersion", + "formatter": 11 } - }, - "showBorder": false + ] } }, - "name": "q-fb41aa51" - }, - { - "type": 1, - "content": { - "json": "
Distribution
" - }, - "name": "div-distribution-4b2298" - }, - { - "type": 3, - "content": { - "version": "KqlItem/1.0", - "query": "Tailscale_Users_CL\n| summarize arg_max(TimeGenerated, *) by UserId\n| summarize Users = count() by Role\n| order by Users desc", - "size": 0, - "queryType": 0, - "resourceType": "microsoft.operationalinsights/workspaces", - "title": "Users by role", - "visualization": "piechart" - }, - "name": "q-69fb1d2d", - "customWidth": "50" - }, - { - "type": 3, - "content": { - "version": "KqlItem/1.0", - "query": "Tailscale_Users_CL\n| summarize arg_max(TimeGenerated, *) by UserId\n| summarize Users = count() by Status\n| order by Users desc", - "size": 0, - "queryType": 0, - "resourceType": "microsoft.operationalinsights/workspaces", - "title": "Users by status", - "visualization": "piechart" - }, - "name": "q-d026cea9", - "customWidth": "50" + "name": "q-7f7e7a9a" }, { "type": 1, "content": { - "json": "
User inventory table
" + "json": "
Subnet routers / exit nodes
" }, - "name": "div-user-inventory-table-79802d" + "name": "div-subnet-routers-/-exi-b082d6" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "Tailscale_Users_CL\n| summarize arg_max(TimeGenerated, *) by UserId\n| extend DaysSinceSeen = datetime_diff('day', now(), LastSeen)\n| project LoginName, DisplayName, Role, Status, UserType, DeviceCount, CurrentlyConnected, LastSeen, DaysSinceSeen, Created\n| order by LastSeen desc", + "query": "Tailscale_Devices_CL\n| summarize arg_max(TimeGenerated, *) by DeviceId\n| where array_length(AdvertisedRoutes) > 0\n| extend AdvertisedSummary = tostring(AdvertisedRoutes), EnabledSummary = tostring(EnabledRoutes)\n| project DeviceName, Hostname, User, Os, AdvertisedSummary, EnabledSummary, LastSeen, Authorized\n| order by LastSeen desc", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", - "title": "All users (latest snapshot per user)", - "visualization": "table" + "visualization": "table", + "title": "Devices advertising subnet routes or exit-node capability", + "gridSettings": { + "formatters": [ + { + "columnMatch": "LastSeen", + "formatter": 6 + } + ] + }, + "noDataMessage": "No subnet routers in this tailnet - pure mesh topology.", + "noDataMessageStyle": 5 }, - "name": "q-bd4b15af" + "name": "q-5004df25" } ] }, "conditionalVisibility": { "parameterName": "selectedTab", "comparison": "isEqualTo", - "value": "users" + "value": "devices" }, - "name": "group-users" + "name": "group-devices" }, { "type": 12, @@ -511,15 +1145,15 @@ { "type": 1, "content": { - "json": "
Credential inventory
" + "json": "
Credentials snapshot
" }, - "name": "div-credential-inventory-3dbcc1" + "name": "div-credentials-snapshot-fd464a" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "let base = Tailscale_Keys_CL | summarize arg_max(TimeGenerated, *) by KeyId | where isnull(Revoked);\nunion (base | summarize V=toreal(count()) | extend Metric=\"Active credentials\", Order=1),(base | where KeyType =~ \"auth\" | summarize V=toreal(count()) | extend Metric=\"Auth keys\", Order=2),(base | where KeyType in~ (\"apiAccessToken\",\"api_access_token\",\"apikey\") | summarize V=toreal(count()) | extend Metric=\"API tokens\", Order=3),(base | where KeyType in~ (\"oauthClient\",\"oauth_client\") | summarize V=toreal(count()) | extend Metric=\"OAuth clients\", Order=4),(base | where isnull(Expires) | summarize V=toreal(count()) | extend Metric=\"No expiry\", Order=5),(base | where isnotnull(Expires) and Expires < now()+7d | summarize V=toreal(count()) | extend Metric=\"Expiring within 7 days\", Order=6)\n| order by Order asc | project Metric, Value=V", + "query": "let K = Tailscale_Keys_CL | summarize arg_max(TimeGenerated, *) by KeyId;\nunion\n (K | summarize V=toreal(count()) | extend Metric=\"Total keys\", Order=1),\n (K | where isnull(Revoked) and (isnull(Expires) or Expires > now()) | summarize V=toreal(count()) | extend Metric=\"Active\", Order=2),\n (K | where isnotnull(Revoked) | summarize V=toreal(count()) | extend Metric=\"Revoked\", Order=3),\n (K | where Expires < now() and isnull(Revoked) | summarize V=toreal(count()) | extend Metric=\"Expired\", Order=4),\n (K | where isnull(Revoked) and Expires between(now() .. ago(-7d)) | summarize V=toreal(count()) | extend Metric=\"Expiring in 7d\", Order=5),\n (K | where isnull(Revoked) and (isnull(Expires) or ExpirySeconds==0) | summarize V=toreal(count()) | extend Metric=\"Never expire\", Order=6),\n (K | where KeyType =~ \"auth\" | summarize V=toreal(count()) | extend Metric=\"Auth keys\", Order=7),\n (K | where KeyType =~ \"api\" or KeyType contains \"oauth\" | summarize V=toreal(count()) | extend Metric=\"API / OAuth\", Order=8)\n| order by Order asc | project Metric, Value=V", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", @@ -539,95 +1173,112 @@ "showBorder": false } }, - "name": "q-7f141013" + "name": "q-79398bc0" }, { "type": 1, "content": { - "json": "
Credentials by type
" + "json": "
Distribution
" }, - "name": "div-credentials-by-type-20d5b3" + "name": "div-distribution-15f665" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "Tailscale_Keys_CL\n| summarize arg_max(TimeGenerated, *) by KeyId\n| where isnull(Revoked)\n| summarize Count = count() by KeyType", + "query": "Tailscale_Keys_CL\n| summarize arg_max(TimeGenerated, *) by KeyId\n| summarize Count=count() by KeyType\n| order by Count desc", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", - "title": "Credentials by type", - "visualization": "piechart" + "visualization": "piechart", + "title": "Keys by type" }, - "name": "q-dfb3fbc4", + "name": "q-23e6618b", "customWidth": "50" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "Tailscale_Keys_CL\n| summarize arg_max(TimeGenerated, *) by KeyId\n| where isnull(Revoked) and isnotnull(Expires)\n| extend DaysToExpiry = datetime_diff('day', Expires, now())\n| extend Bucket = case(DaysToExpiry < 0, '0 expired', DaysToExpiry < 7, '1 within 7d', DaysToExpiry < 30, '2 within 30d', DaysToExpiry < 90, '3 within 90d', '4 over 90d')\n| summarize Keys = count() by Bucket\n| order by Bucket asc", + "query": "Tailscale_Keys_CL\n| summarize arg_max(TimeGenerated, *) by KeyId\n| where isnull(Revoked)\n| extend Bucket = case(\n isnull(Expires) or ExpirySeconds == 0, \"Never\",\n Expires < now(), \"Already expired\",\n Expires < ago(-1d), \"<24h\",\n Expires < ago(-7d), \"1-7d\",\n Expires < ago(-30d), \"8-30d\",\n Expires < ago(-90d), \"31-90d\",\n \"90+d\")\n| summarize Keys=count() by Bucket\n| order by case(Bucket==\"Already expired\",1, Bucket==\"<24h\",2, Bucket==\"1-7d\",3, Bucket==\"8-30d\",4, Bucket==\"31-90d\",5, Bucket==\"90+d\",6, Bucket==\"Never\",7, 8) asc", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", - "title": "Credentials by expiry window", - "visualization": "barchart" + "visualization": "barchart", + "title": "Active key expiry distribution" }, - "name": "q-09938e1d", + "name": "q-7484d1a0", "customWidth": "50" }, { "type": 1, "content": { - "json": "
Credential inventory table
" + "json": "
Active credential register
" }, - "name": "div-credential-inventory-table-0ccb53" + "name": "div-active-credential-re-c27539" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "Tailscale_Keys_CL\n| summarize arg_max(TimeGenerated, *) by KeyId\n| extend DaysToExpiry = iff(isnull(Expires), int(null), datetime_diff('day', Expires, now()))\n| project KeyId, KeyType, Description, Created, Expires, DaysToExpiry, Revoked, Capabilities\n| order by Created desc", + "query": "Tailscale_Keys_CL\n| summarize arg_max(TimeGenerated, *) by KeyId\n| where isnull(Revoked)\n| extend ExpiryStatus = case(\n isnull(Expires) or ExpirySeconds == 0, \"Never expires\",\n Expires < now(), \"Expired\",\n Expires < ago(-7d), \"Expires in 7d\",\n Expires < ago(-30d), \"Expires in 30d\",\n \"OK\")\n| project KeyId, KeyType, Description, UserId, Created, Expires, ExpiryStatus, Capabilities\n| order by Created desc", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", - "title": "All credentials (latest snapshot per ID)", "visualization": "table", + "title": "All active credentials with computed expiry status", "gridSettings": { "formatters": [ { - "columnMatch": "DaysToExpiry", - "formatter": 8, - "formatOptions": { - "palette": "redBright", - "min": 0, - "max": 90 - } + "columnMatch": "Created", + "formatter": 6 + }, + { + "columnMatch": "Expires", + "formatter": 6 + }, + { + "columnMatch": "ExpiryStatus", + "formatter": 11 + }, + { + "columnMatch": "KeyType", + "formatter": 11 } ] } }, - "name": "q-0660a92f" + "name": "q-4b27a750" }, { "type": 1, "content": { - "json": "
Audit log: credential lifecycle
" + "json": "
Credential CRUD events
" }, - "name": "div-audit-log:-credential-lifecycl-25cd5f" + "name": "div-credential-crud-even-e455b0" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| where tostring(Target.type) in (\"AUTH_KEY\", \"API_KEY\", \"OAUTH_CLIENT\")\n| extend ActorLogin = tostring(Actor.loginName)\n| project TimeGenerated, ActorLogin, Action, TargetType = tostring(Target.type), TargetId = tostring(Target.id), Description = tostring(New.description)\n| order by TimeGenerated desc", + "query": "Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| where Action contains \"API_KEY\" or Action contains \"AUTH_KEY\" or Action contains \"OAUTH\" or Action contains \"KEY_CREATE\" or Action contains \"KEY_REVOKE\"\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\n| extend TargetId=tostring(Target.id), TargetType=tostring(Target.type)\n| project TimeGenerated, Action, ActorLogin, TargetType, TargetId, Origin\n| order by TimeGenerated desc", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", - "title": "Create / update / delete events on credentials", - "visualization": "table" + "visualization": "table", + "title": "Recent credential create / revoke / rotate events", + "gridSettings": { + "formatters": [ + { + "columnMatch": "TimeGenerated", + "formatter": 6 + } + ] + }, + "noDataMessage": "No credential CRUD activity in this window.", + "noDataMessageStyle": 5 }, - "name": "q-667d6168" + "name": "q-777693bd" } ] }, @@ -647,71 +1298,120 @@ { "type": 1, "content": { - "json": "
Current DNS state
" + "json": "
Audit volume
" + }, + "name": "div-audit-volume-de29a2" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| summarize Events=count() by bin(TimeGenerated, 1h)\n| order by TimeGenerated asc", + "size": 0, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "timechart", + "title": "Audit events per hour", + "noDataMessage": "No audit events in this window.", + "noDataMessageStyle": 5 + }, + "name": "q-f5eee265" + }, + { + "type": 1, + "content": { + "json": "
Action heatmap by hour of day
" }, - "name": "div-current-dns-state-b054f9" + "name": "div-action-heatmap-by-ho-c8bd5e" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "Tailscale_Dns_CL\n| summarize arg_max(TimeGenerated, *) by ConfigType\n| project ConfigType, Nameservers, MagicDNS, SearchPaths, LastUpdated = TimeGenerated\n| order by ConfigType asc", + "query": "Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| extend Hour=hourofday(TimeGenerated)\n| summarize Events=count() by Hour, Action\n| order by Hour asc, Events desc", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", - "title": "DNS configuration snapshot (latest per config type)", - "visualization": "table" + "visualization": "categoricalbar", + "title": "When are admin actions happening?" }, - "name": "q-b72ff334" + "name": "q-c6f45d95" }, { "type": 1, "content": { - "json": "
Tailnet settings snapshot
" + "json": "
Actor / Action heatmap
" }, - "name": "div-tailnet-settings-snapshot-a457cb" + "name": "div-actor-/-action-heatm-d820ba" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "Tailscale_Settings_CL\n| top 1 by TimeGenerated\n| project\n ['Snapshot at'] = TimeGenerated,\n ['Device approval required'] = iff(DevicesApprovalOn == true, 'On', 'Off'),\n ['Device auto-updates'] = iff(DevicesAutoUpdatesOn == true, 'On', 'Off'),\n ['Device key duration (days)'] = DevicesKeyDurationDays,\n ['User approval required'] = iff(UsersApprovalOn == true, 'On', 'Off'),\n ['Users allowed to join external tailnets'] = UsersRoleAllowedToJoinExternalTailnets,\n ['Regional routing'] = iff(RegionalRoutingOn == true, 'On', 'Off')", + "query": "Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\n| where isnotempty(ActorLogin)\n| summarize Events=count() by ActorLogin, Action\n| order by Events desc | take 100", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", - "title": "Current tailnet settings", - "visualization": "table" + "visualization": "table", + "title": "Who is firing which action", + "gridSettings": { + "formatters": [ + { + "columnMatch": "Events", + "formatter": 4, + "formatOptions": { + "palette": "blue" + } + } + ] + }, + "noDataMessage": "No audit events in this window.", + "noDataMessageStyle": 5 }, - "name": "q-99cf0545" + "name": "q-06c13b3b" }, { "type": 1, "content": { - "json": "
DNS / ACL / Settings change history
" + "json": "
Recent activity
" }, - "name": "div-dns-/-acl-/-settings-change-hi-b18656" + "name": "div-recent-activity-63b210" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| where Action == \"UPDATE\" and tostring(Target.type) == \"TAILNET\"\n| extend Property = tostring(Target.property), ActorLogin = tostring(Actor.loginName)\n| where Property in (\"ACL\", \"DNS_NAMESERVERS\", \"DNS_SPLIT_DNS\", \"DNS_SEARCHPATHS\", \"MAGICDNS_PREFERENCES\", \"SETTINGS\", \"TAILNET_SETTINGS\")\n| project TimeGenerated, ActorLogin, Property, Old, New, Origin\n| order by TimeGenerated desc", + "query": "Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\n| extend TargetType=tostring(Target.type), TargetName=tostring(coalesce(Target.name, Target.id))\n| project TimeGenerated, Action, ActorLogin, TargetType, TargetName, Origin, EventGroupID\n| order by TimeGenerated desc | take 100", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", - "title": "Tailnet configuration changes", - "visualization": "table" + "visualization": "table", + "title": "Last 100 audit events", + "gridSettings": { + "formatters": [ + { + "columnMatch": "TimeGenerated", + "formatter": 6 + }, + { + "columnMatch": "Action", + "formatter": 11 + } + ] + }, + "noDataMessage": "No audit events in this window.", + "noDataMessageStyle": 5 }, - "name": "q-47218d2a" + "name": "q-07a25a40" } ] }, "conditionalVisibility": { "parameterName": "selectedTab", "comparison": "isEqualTo", - "value": "dns" + "value": "audit" }, - "name": "group-dns" + "name": "group-audit" }, { "type": 12, @@ -722,126 +1422,157 @@ { "type": 1, "content": { - "json": "
Webhook inventory
" + "json": "
DNS configuration (current state)
" }, - "name": "div-webhook-inventory-ca6406" + "name": "div-dns-configuration-(c-5f2e1e" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "Tailscale_Webhooks_CL\n| summarize arg_max(TimeGenerated, *) by EndpointId\n| project EndpointId, EndpointUrl, ProviderType, CreatorLoginName, Created, LastModified, Subscriptions", + "query": "Tailscale_Dns_CL\n| summarize arg_max(TimeGenerated, *) by ConfigType\n| project ConfigType, Nameservers, MagicDNS, SearchPaths, LastSnapshot=TimeGenerated\n| order by ConfigType asc", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", - "title": "All webhooks (latest snapshot per endpoint)", - "visualization": "table" + "visualization": "table", + "title": "MagicDNS, nameservers, search paths", + "gridSettings": { + "formatters": [ + { + "columnMatch": "LastSnapshot", + "formatter": 6 + }, + { + "columnMatch": "ConfigType", + "formatter": 11 + } + ] + }, + "noDataMessage": "No DNS snapshots in the workspace yet. DNS polls runs at ~30 min cadence.", + "noDataMessageStyle": 5 }, - "name": "q-d2585638" + "name": "q-d6a6a358" }, { "type": 1, "content": { - "json": "
Webhook change history (from audit log)
" + "json": "
Tailnet settings (current)
" }, - "name": "div-webhook-change-history-(from-a-040d0e" + "name": "div-tailnet-settings-(cu-e03b16" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| where tostring(Target.type) == \"WEBHOOK\" or tostring(Target.property) =~ \"WEBHOOK\"\n| extend ActorLogin = tostring(Actor.loginName)\n| project TimeGenerated, ActorLogin, Action, Target, Old, New, Origin\n| order by TimeGenerated desc", + "query": "Tailscale_Settings_CL\n| summarize arg_max(TimeGenerated, *) by TenantId\n| project DevicesApprovalOn, DevicesAutoUpdatesOn, DevicesKeyDurationDays, UsersApprovalOn, NetworkFlowLoggingOn, RegionalRoutingOn, PostureIdentityCollectionOn, UsersRoleAllowedToJoinExternalTailnets, LastSnapshot=TimeGenerated", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", - "title": "Webhook create / update / delete events", - "visualization": "table" + "visualization": "table", + "title": "Tailnet policy gates", + "noDataMessage": "No settings snapshot yet.", + "noDataMessageStyle": 5 }, - "name": "q-303ec591" - } - ] - }, - "conditionalVisibility": { - "parameterName": "selectedTab", - "comparison": "isEqualTo", - "value": "webhooks" - }, - "name": "group-webhooks" - }, - { - "type": 12, - "content": { - "version": "NotebookGroup/1.0", - "groupType": "editable", - "items": [ + "name": "q-0622cfc3" + }, { "type": 1, "content": { - "json": "
Audit volume
" + "json": "
DNS change history
" }, - "name": "div-audit-volume-2dac6c" + "name": "div-dns-change-history-9dd376" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| summarize EventCount = count() by bin(TimeGenerated, 1h)", + "query": "Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| extend TargetProperty=tostring(Target.property)\n| where Action contains \"DNS\" or TargetProperty has_any (\"DNS_NAMESERVERS\", \"DNS_SPLIT_DNS\", \"MAGICDNS\", \"DNS_SEARCH_PATHS\")\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\n| project TimeGenerated, ActorLogin, Action, TargetProperty, Origin\n| order by TimeGenerated desc", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", - "title": "Audit events per hour", - "visualization": "timechart" + "visualization": "table", + "title": "Recent DNS-related admin changes", + "gridSettings": { + "formatters": [ + { + "columnMatch": "TimeGenerated", + "formatter": 6 + } + ] + }, + "noDataMessage": "No DNS changes in this window.", + "noDataMessageStyle": 5 }, - "name": "q-6831105c" + "name": "q-a132ff6a" }, { "type": 1, "content": { - "json": "
Actor and action distribution
" + "json": "
ACL policy changes
" }, - "name": "div-actor-and-action-distribution-e6ef51" + "name": "div-acl-policy-changes-ff0e68" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| extend ActorLogin = tostring(Actor.loginName), TargetType = tostring(Target.type)\n| summarize EventCount = count() by ActorLogin, Action, TargetType\n| order by EventCount desc", + "query": "Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| where Action == \"ACL_UPDATE\" or Action contains \"ACL\"\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\n| project TimeGenerated, ActorLogin, Action, Origin, EventGroupID\n| order by TimeGenerated desc", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", - "title": "Actor / Action / Target heatmap", - "visualization": "table" + "visualization": "table", + "title": "Recent ACL / policy file modifications", + "gridSettings": { + "formatters": [ + { + "columnMatch": "TimeGenerated", + "formatter": 6 + } + ] + }, + "noDataMessage": "No ACL changes in this window.", + "noDataMessageStyle": 5 }, - "name": "q-2d0d578e" + "name": "q-94818c53" }, { "type": 1, "content": { - "json": "
Recent activity
" + "json": "
Subnet routes & exit nodes
" }, - "name": "div-recent-activity-a8f996" + "name": "div-subnet-routes-and-ex-eef47f" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| extend ActorLogin = tostring(Actor.loginName), TargetType = tostring(Target.type), Property = tostring(Target.property)\n| project TimeGenerated, ActorLogin, Action, TargetType, Property, TargetId = tostring(Target.id), Origin\n| order by TimeGenerated desc\n| take 100", + "query": "Tailscale_Devices_CL\n| summarize arg_max(TimeGenerated, *) by DeviceId\n| where array_length(EnabledRoutes) > 0\n| project DeviceName, User, Os, EnabledRoutes=tostring(EnabledRoutes), AdvertisedRoutes=tostring(AdvertisedRoutes), LastSeen\n| order by LastSeen desc", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", - "title": "Recent 100 audit events", - "visualization": "table" + "visualization": "table", + "title": "Routes currently being served from devices", + "gridSettings": { + "formatters": [ + { + "columnMatch": "LastSeen", + "formatter": 6 + } + ] + }, + "noDataMessage": "No subnet routers active in this tailnet.", + "noDataMessageStyle": 5 }, - "name": "q-03fb31a7" + "name": "q-61a792cd" } ] }, "conditionalVisibility": { "parameterName": "selectedTab", "comparison": "isEqualTo", - "value": "audit" + "value": "network" }, - "name": "group-audit" + "name": "group-network" }, { "type": 12, @@ -852,76 +1583,125 @@ { "type": 1, "content": { - "json": "
Tailscale-related alerts
" + "json": "
Ingest rate per table
" }, - "name": "div-tailscale-related-alerts-a0f1ca" + "name": "div-ingest-rate-per-tabl-24e5f6" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "SecurityAlert\n| where TimeGenerated {TimeRange}\n| where AlertName startswith \"Tailscale\"\n| summarize Count = count() by AlertName, AlertSeverity\n| order by Count desc", + "query": "union withsource=Table Tailscale_Audit_CL, Tailscale_Devices_CL, Tailscale_Users_CL, Tailscale_Keys_CL, Tailscale_Dns_CL, Tailscale_Settings_CL\n| where TimeGenerated > ago(24h)\n| summarize Rows=count() by Table, bin(TimeGenerated, 1h)\n| order by TimeGenerated asc", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", - "title": "Alerts by rule", - "visualization": "table" + "visualization": "timechart", + "title": "Rows ingested per Tailscale table per hour (last 24h)", + "noDataMessage": "No Tailscale data ingested in the last 24h - check the connector card under Sentinel Data Connectors.", + "noDataMessageStyle": 5 }, - "name": "q-ffba1ab4", - "customWidth": "50" + "name": "q-c6fd0143" + }, + { + "type": 1, + "content": { + "json": "
Last poll time per table
" + }, + "name": "div-last-poll-time-per-t-49b051" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "SecurityAlert\n| where TimeGenerated {TimeRange}\n| where AlertName startswith \"Tailscale\"\n| summarize Count = count() by AlertSeverity\n| render piechart", + "query": "union withsource=Table Tailscale_Audit_CL, Tailscale_Devices_CL, Tailscale_Users_CL, Tailscale_Keys_CL, Tailscale_Dns_CL, Tailscale_Settings_CL\n| summarize LastRow=max(TimeGenerated), TotalRows=count() by Table\n| extend MinutesAgo=toint((now() - LastRow) / 1m)\n| extend Status=case(MinutesAgo < 60, \"Fresh\", MinutesAgo < 360, \"Recent\", MinutesAgo < 1440, \"Stale\", \"Very Stale\")\n| project Table, LastRow, MinutesAgo, TotalRows, Status\n| order by MinutesAgo asc", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", - "title": "Severity distribution", - "visualization": "table" + "visualization": "table", + "title": "Per-table freshness", + "gridSettings": { + "formatters": [ + { + "columnMatch": "LastRow", + "formatter": 6 + }, + { + "columnMatch": "MinutesAgo", + "formatter": 8, + "formatOptions": { + "palette": "redBright" + } + }, + { + "columnMatch": "TotalRows", + "formatter": 8, + "formatOptions": { + "palette": "blue" + } + }, + { + "columnMatch": "Status", + "formatter": 11 + } + ] + } }, - "name": "q-bf9342b0", - "customWidth": "50" + "name": "q-a02e37a8" }, { "type": 1, "content": { - "json": "
Recent Tailscale alerts
" + "json": "
Log Analytics operational events
" }, - "name": "div-recent-tailscale-alerts-45f843" + "name": "div-log-analytics-operat-630749" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "SecurityAlert\n| where TimeGenerated {TimeRange}\n| where AlertName startswith \"Tailscale\"\n| project TimeGenerated, AlertName, AlertSeverity, Description, Entities\n| order by TimeGenerated desc", + "query": "_LogOperation\n| where TimeGenerated > ago(24h)\n| where _ResourceId contains \"tailscale\" or Detail contains \"Tailscale_\"\n| project TimeGenerated, Operation, Level, Detail\n| order by TimeGenerated desc | take 100", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", - "title": "Recent Tailscale alerts", - "visualization": "table" + "visualization": "table", + "title": "Log Analytics operational events touching Tailscale tables", + "gridSettings": { + "formatters": [ + { + "columnMatch": "TimeGenerated", + "formatter": 6 + }, + { + "columnMatch": "Level", + "formatter": 11 + } + ] + }, + "noDataMessage": "No operational issues recorded in the last 24h.", + "noDataMessageStyle": 5 }, - "name": "q-dfb44602" + "name": "q-b492f150" } ] }, "conditionalVisibility": { "parameterName": "selectedTab", "comparison": "isEqualTo", - "value": "signals" + "value": "pipeline" }, - "name": "group-signals" + "name": "group-pipeline" }, { "type": 1, "content": { - "json": "---\n\n**Tailscale Standard (CCF)** | Polled from the Tailscale Site Manager API every 5 minutes (audit) / hourly (state). For Premium / Enterprise tier tailnets that need network flow logs and posture telemetry, install **Tailscale Premium (CCF)** instead." + "json": "
Tailscale Operations (Standard) (CCF) - Microsoft Sentinel content from the Tailscale (CCF) solution, Standard-tier surface. Tables polled from the Tailscale REST API: audit, devices, users, keys, dns, settings. Filter every panel via the time range above; the Investigate tab adds Actor and Device pickers for drilldown. For network flow logs and posture integrations, install the companion Tailscale Operations (Premium) workbook on a Premium / Enterprise tailnet.
" }, "name": "footer" } ], - "fallbackResourceIds": [], - "fromTemplateId": "sentinel-TailscaleStandardOperations", + "fallbackResourceIds": [ + "Azure Monitor" + ], + "fromTemplateId": "sentinel-Tailscale-CCF", "$schema": "https://github.com/Microsoft/Application-Insights-Workbooks/blob/master/schema/workbook.json" } \ No newline at end of file diff --git a/Workbooks/WorkbooksMetadata.json b/Workbooks/WorkbooksMetadata.json index 75474e04e9d..98ed6467276 100644 --- a/Workbooks/WorkbooksMetadata.json +++ b/Workbooks/WorkbooksMetadata.json @@ -10600,7 +10600,7 @@ { "workbookKey": "TailscaleStandardOperationsWorkbook", "logoFileName": "Tailscale.svg", - "description": "Tailscale Operations workbook for Standard tier - device inventory, user roles, auth keys, DNS configuration, audit activity, and tailnet health overview.", + "description": "Tailscale Operations workbook for Standard tier. Nine tabs covering an at-a-glance KPI hero row, audit activity overview, an actor + device drilldown (Investigate), embedded hunting queries (first-seen actors, off-hours changes, key-expiry-disabled devices, never-expire auth keys, outdated clients, dormant devices, subnet route exposure, SSH-enabled devices), identity (user roles, status, last login recency, role escalation history, orphaned users), devices (OS / version / tag distribution, devices needing attention, full inventory, subnet routers), credentials (expiry timeline, never-expire flag, CRUD events), admin audit (action heatmap, actor x action heatmap, recent 100), network and DNS (current snapshot, tailnet policy gates, ACL change history), and pipeline health (per-table freshness, ingest rate, operational events). Driven by data polled from the Tailscale REST API.", "dataTypesDependencies": [ "Tailscale_Audit_CL", "Tailscale_Devices_CL", From 7c3ee1f3764881ca8ecd2eaf10ebc032925621fa Mon Sep 17 00:00:00 2001 From: noodlemctwoodle Date: Tue, 19 May 2026 12:34:32 +0100 Subject: [PATCH 16/27] feat(tailscale): finalize 3.0.0 for upstream PR submission Brings the Tailscale (CCF) solution to upstream-ready state. Net-new content (analytic rule + 5 hunting queries, schema additions, workbook rebuild), reviewer-feedback compliance (mirroring v-shukore comments on UniFi PR), and the ARM-TTK fix the V3 wrapper doesn't filter. Net content additions: - New analytic rule: TailscaleOAuthClientCreatedWithWriteScopes (High, Persistence/PrivilegeEscalation, T1098/T1136) - flags OAuth clients granted any :write scope. - New analytic rule: TailscalePremiumDerpRelaySurge (Low, CommandAndControl, T1572) - >75% flows fallen back to DERP relay (NAT/firewall failure or evasion signal). - 5 new Premium hunting queries leveraging 3.1.0 promoted columns: CrossTagFlowMatrix, DerpRelayPersistence, OffHoursFlows, TaggedServiceFanIn, UserMultiDevice. Schema additions (carried from earlier dev iterations, now landing in 3.0.0): - Tailscale_Audit_CL: EventType, ActionDetails (previously silently dropped by DCR streamDecl). - Tailscale_Network_CL: 15 columns promoted from srcNode/dstNodes (SrcUser/SrcNodeName/SrcOs/SrcTags/SrcAddresses/DstCount/DstNodeId/ DstNodeName/DstUser/DstOs/DstTags/DstAddresses) plus traffic-shape flags (HasVirtualTraffic/HasSubnetTraffic/HasExitTraffic/ HasPhysicalTraffic/IsRelayed via 127.3.3.40 DERP detection). - Premium DCR DNS consolidation: 3 separate streams unified to Custom-Tailscale_DnsConfig_CL (under Azure 10-dataFlow limit). - All 5 Premium network analytic rules rewritten against promoted columns instead of dynamic JSON traversal; gained Host + Account entityMappings. Workbooks rebuilt: - TailscaleStandardOperations: 9-tab forensic UX (overview, identity, devices, credentials, admin audit, network, DNS, investigate, pipeline health). - TailscalePremiumOperations: Standard tabs plus Network Flows + Posture tabs with traffic-mix area chart, DERP direct-vs-relayed pie, exit-node tag breakdown, posture provider/health pies, beaconing-candidate table. - Investigate-tab pickers use explicit __ALL__ sentinel row (the magic value::all additionalResourceOptions does not reliably substitute). - Positive-outcome panels use noDataMessageStyle: 1 (info/blue) not 5 (critical/red). Reviewer-feedback compliance (v-shukore on UniFi PR): - Solution_Tailscale.json BasePath -> Windows path (C:\\GitHub\\azure-Sentinel\\Solutions\\Tailscale (CCF)). - SolutionMetadata.json support.name "Community" -> "Tailscale (CCF)". - firstPublishDate / lastPublishDate -> 2026-05-19. - Version pinned 3.0.0 (was 3.1.28 across dev iterations); ReleaseNotes.md collapsed to single "Initial Solution Release" row. Hunting query polish: - 14 descriptions normalised to start with "Identifies" (consistent with existing 8 that already did, matches detection-rule convention). - 5 new Premium hunting queries gained entityMappings + version: 1.0.0. ARM-TTK fix (47/48 -> 48/48): - Package/mainTemplate.json: stripped 7 unreferenced variables (_solutionVersion, dataConnectorTemplateName{ConnectorDefinition, Connections}{1,2}, dataCollectionEndpointId{1,2}) and 3 dependent unreferenced parameters (resourceGroupName, subscription, dataCollectionEndpoint). Legacy artefacts of the single-card CCF template pattern; Tailscale uses the Proofpoint TAP multi-poller- per-card shape (guidValue + innerWorkspace) which does not reference them. Tooling for future rebuilds (gitignored, not for upstream): - .gitignore: added .local-helpers/ exclusion. - .local-helpers/finalize-tailscale-package.py: idempotent post-build script. Auto-detects the wrapper-bumped Version, pins it back to target (default 3.0.0), strips unreferenced vars/params, repackages Package/.zip, sweeps .DS_Store. Package regeneration: - Removed stale Package/3.0.5.zip. - Generated fresh Package/3.0.0.zip. - Package/mainTemplate.json + createUiDefinition.json regenerated. Verified locally: ARM-TTK 48/48, KQL validators pass, no privacy leaks in source files. --- .gitignore | 3 + ...caleOAuthClientCreatedWithWriteScopes.yaml | 52 + .../TailscalePremiumBeaconingDetected.yaml | 19 +- .../TailscalePremiumDerpRelaySurge.yaml | 51 + ...TailscalePremiumLargeOutboundTransfer.yaml | 13 +- .../TailscalePremiumMassFanOut.yaml | 7 +- ...ePremiumSubnetRouterThroughputAnomaly.yaml | 14 +- ...lscalePremiumUnexpectedExitNodeEgress.yaml | 12 +- .../TailscaleAuditLogs_ccf/Tailscale_DCR.json | 10 +- .../Tailscale_tables.json | 8 + .../TailscalePremium_DCR.json | 62 +- .../TailscalePremium_PollerConfig.json | 6 +- .../TailscalePremium_tables.json | 76 + .../Data/Solution_Tailscale.json | 15 +- .../TailscaleAuthKeySprawl.yaml | 2 +- .../TailscaleDevicesWithSshEnabled.yaml | 2 +- .../TailscaleDormantDevices.yaml | 2 +- .../TailscaleExternalDeviceInventory.yaml | 2 +- .../TailscaleFirstSeenActor.yaml | 2 +- .../TailscaleOffHoursConfigChanges.yaml | 2 +- .../TailscaleOrphanedUsers.yaml | 2 +- .../TailscaleOutdatedClients.yaml | 2 +- .../TailscalePremiumCrossTagFlowMatrix.yaml | 41 + .../TailscalePremiumDerpRelayPersistence.yaml | 37 + .../TailscalePremiumExitNodeUsage.yaml | 2 +- .../TailscalePremiumNewNodePairs.yaml | 2 +- .../TailscalePremiumOffHoursFlows.yaml | 40 + .../TailscalePremiumPostureInventory.yaml | 2 +- .../TailscalePremiumTaggedServiceFanIn.yaml | 36 + .../TailscalePremiumTopTalkers.yaml | 2 +- .../TailscalePremiumUserMultiDevice.yaml | 43 + .../Tailscale (CCF)/PREMIUM-ENDPOINTS.md | 11 +- Solutions/Tailscale (CCF)/Package/3.0.0.zip | Bin 0 -> 77845 bytes Solutions/Tailscale (CCF)/Package/3.0.5.zip | Bin 66176 -> 0 bytes .../Package/createUiDefinition.json | 206 +- .../Tailscale (CCF)/Package/mainTemplate.json | 2729 +++++++++++------ Solutions/Tailscale (CCF)/ReleaseNotes.md | 2 +- .../Tailscale (CCF)/SolutionMetadata.json | 6 +- .../Workbooks/TailscalePremiumOperations.json | 1906 +++++++++--- .../TailscaleStandardOperations.json | 194 +- 40 files changed, 4092 insertions(+), 1531 deletions(-) create mode 100644 Solutions/Tailscale (CCF)/Analytic Rules/TailscaleOAuthClientCreatedWithWriteScopes.yaml create mode 100644 Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumDerpRelaySurge.yaml create mode 100644 Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumCrossTagFlowMatrix.yaml create mode 100644 Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumDerpRelayPersistence.yaml create mode 100644 Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumOffHoursFlows.yaml create mode 100644 Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumTaggedServiceFanIn.yaml create mode 100644 Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumUserMultiDevice.yaml create mode 100644 Solutions/Tailscale (CCF)/Package/3.0.0.zip delete mode 100644 Solutions/Tailscale (CCF)/Package/3.0.5.zip diff --git a/.gitignore b/.gitignore index 2c7b098fd9d..a5172aa0ec4 100644 --- a/.gitignore +++ b/.gitignore @@ -354,3 +354,6 @@ Hunting Queries/DeployedQueries.json .arm-ttk + +# Local-only helper scripts (not for upstream submission) +.local-helpers/ diff --git a/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleOAuthClientCreatedWithWriteScopes.yaml b/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleOAuthClientCreatedWithWriteScopes.yaml new file mode 100644 index 00000000000..1acfe79d43c --- /dev/null +++ b/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleOAuthClientCreatedWithWriteScopes.yaml @@ -0,0 +1,52 @@ +id: 7237a848-30f2-499b-9ad5-024aea1288bd +name: "Tailscale: OAuth client or API key created with write scopes" +description: | + Detects creation of a Tailscale OAuth client or API access key whose granted scopes include WRITE permissions (anything matching ":write"). Tokens with write scopes can modify tailnet configuration, manage devices, write ACLs, and revoke keys - high-value adversary targets. Compare against the recent actor history; revoke immediately if unexpected. +severity: High +status: Available +requiredDataConnectors: + - connectorId: TailscaleCCF + dataTypes: + - Tailscale_Audit_CL + - connectorId: TailscalePremiumCCF + dataTypes: + - Tailscale_Audit_CL +queryFrequency: 15m +queryPeriod: 15m +triggerOperator: gt +triggerThreshold: 0 +tactics: + - Persistence + - PrivilegeEscalation +relevantTechniques: + - T1098 + - T1136 +query: | + Tailscale_Audit_CL + | where EventType == "CONFIG" + | where Action == "CREATE" + | where tostring(Target.type) in ("API_KEY", "OAUTH_CLIENT") + | extend Scopes = extract(@"scopes\s*-\s*(.+)$", 1, ActionDetails) + | where Scopes contains ":write" + | extend WriteScopes = extract_all(@"([a-zA-Z_]+:write)", Scopes) + | extend ActorLogin = tostring(Actor.loginName) + | extend ActorType = tostring(Actor.type) + | extend TargetName = tostring(Target.name) + | extend TargetId = tostring(Target.id) + | extend TargetType = tostring(Target.type) + | project TimeGenerated, ActorLogin, ActorType, Action, TargetType, TargetName, TargetId, WriteScopes, Scopes, Origin, ActionDetails +entityMappings: + - entityType: Account + fieldMappings: + - identifier: FullName + columnName: ActorLogin +incidentConfiguration: + createIncident: true + groupingConfiguration: + enabled: true + reopenClosedIncident: false + lookbackDuration: PT5H + matchingMethod: AllEntities + groupByEntities: [] +kind: Scheduled +version: 1.0.0 diff --git a/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumBeaconingDetected.yaml b/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumBeaconingDetected.yaml index 422ab6163cf..194f9881724 100644 --- a/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumBeaconingDetected.yaml +++ b/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumBeaconingDetected.yaml @@ -25,21 +25,34 @@ query: | let beaconPercentThreshold = 80.0; Tailscale_Network_CL | where TimeGenerated > ago(lookback) + | where HasVirtualTraffic | mv-expand t = VirtualTraffic | extend Src = tostring(t.src), Dst = tostring(t.dst), Proto = toint(t.proto) - | project TimeGenerated, Src, Dst, Proto + | project TimeGenerated, Src, Dst, Proto, SrcNodeName, SrcUser, DstNodeName, DstUser | sort by Src asc, Dst asc, Proto asc, TimeGenerated asc | serialize | extend NextTime = next(TimeGenerated), NextSrc = next(Src), NextDst = next(Dst), NextProto = next(Proto) | where Src == NextSrc and Dst == NextDst and Proto == NextProto | extend DeltaSec = datetime_diff('second', NextTime, TimeGenerated) | where DeltaSec > 5 - | summarize DeltaCount = count() by Src, Dst, Proto, DeltaSec - | summarize (MostFrequentDeltaCount, MostFrequentDeltaSec) = arg_max(DeltaCount, DeltaSec), TotalFlows = sum(DeltaCount) by Src, Dst, Proto + | summarize DeltaCount = count() by Src, Dst, Proto, DeltaSec, SrcNodeName, SrcUser, DstNodeName, DstUser + | summarize (MostFrequentDeltaCount, MostFrequentDeltaSec) = arg_max(DeltaCount, DeltaSec), TotalFlows = sum(DeltaCount) by Src, Dst, Proto, SrcNodeName, SrcUser, DstNodeName, DstUser | where TotalFlows >= minFlows | extend BeaconPercent = round(MostFrequentDeltaCount * 100.0 / TotalFlows, 1) | where BeaconPercent >= beaconPercentThreshold entityMappings: + - entityType: Host + fieldMappings: + - identifier: HostName + columnName: SrcNodeName + - entityType: Host + fieldMappings: + - identifier: HostName + columnName: DstNodeName + - entityType: Account + fieldMappings: + - identifier: FullName + columnName: SrcUser - entityType: IP fieldMappings: - identifier: Address diff --git a/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumDerpRelaySurge.yaml b/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumDerpRelaySurge.yaml new file mode 100644 index 00000000000..0a67b705d5a --- /dev/null +++ b/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumDerpRelaySurge.yaml @@ -0,0 +1,51 @@ +id: 0a1c8d12-e7d3-4890-8b89-8d6dbc1be2f0 +name: "Tailscale Premium: DERP relay traffic surge" +description: | + Identifies when a source node has more than 75 percent of its recent flows falling back to a DERP relay (Tailscale's IsRelayed flag, traffic via 127.3.3.40). Sustained high relay rate indicates direct WireGuard peer-to-peer is failing - causes include NAT/firewall changes, a network blocking UDP 41641, or potential evasion attempts. Operational signal but useful for spotting policy drift. Requires Tailscale Premium or Enterprise. +severity: Low +status: Available +requiredDataConnectors: + - connectorId: TailscalePremiumCCF + dataTypes: + - Tailscale_Network_CL +queryFrequency: 15m +queryPeriod: 15m +triggerOperator: gt +triggerThreshold: 0 +tactics: + - CommandAndControl +relevantTechniques: + - T1572 +query: | + let minFlows = 20; + let relayedPctThreshold = 75.0; + Tailscale_Network_CL + | where TimeGenerated > ago(15m) + | summarize + TotalFlows = count(), + RelayedFlows = countif(IsRelayed) + by SrcNodeName, SrcUser, SrcOs, SrcTags=tostring(SrcTags) + | where TotalFlows >= minFlows + | extend RelayedPct = round(100.0 * RelayedFlows / TotalFlows, 1) + | where RelayedPct > relayedPctThreshold + | project SrcNodeName, SrcUser, SrcOs, SrcTags, TotalFlows, RelayedFlows, RelayedPct + | order by RelayedPct desc +entityMappings: + - entityType: Host + fieldMappings: + - identifier: HostName + columnName: SrcNodeName + - entityType: Account + fieldMappings: + - identifier: FullName + columnName: SrcUser +incidentConfiguration: + createIncident: true + groupingConfiguration: + enabled: true + reopenClosedIncident: false + lookbackDuration: PT6H + matchingMethod: AllEntities + groupByEntities: [] +kind: Scheduled +version: 1.0.0 diff --git a/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumLargeOutboundTransfer.yaml b/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumLargeOutboundTransfer.yaml index cc9b9f60ba5..cfd89a96d1c 100644 --- a/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumLargeOutboundTransfer.yaml +++ b/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumLargeOutboundTransfer.yaml @@ -22,9 +22,10 @@ query: | let bytesThreshold = 100 * 1024 * 1024; Tailscale_Network_CL | where TimeGenerated > ago(1h) + | where HasVirtualTraffic | mv-expand t = VirtualTraffic - | extend Src = tostring(t.src), Dst = tostring(t.dst), Proto = toint(t.proto), TxBytes = tolong(t.txBytes), RxBytes = tolong(t.rxBytes), Bytes = tolong(t.txBytes) + tolong(t.rxBytes) - | summarize TotalBytes = sum(Bytes), TotalPackets = sum(tolong(t.txPkts) + tolong(t.rxPkts)), SrcNodeName = take_any(tostring(SrcNode.name)) by NodeId, Src, Dst, Proto + | extend Src = tostring(t.src), Dst = tostring(t.dst), Proto = toint(t.proto), Bytes = tolong(t.txBytes) + tolong(t.rxBytes), Pkts = tolong(t.txPkts) + tolong(t.rxPkts) + | summarize TotalBytes = sum(Bytes), TotalPackets = sum(Pkts) by NodeId, SrcNodeName, SrcUser, DstNodeName, DstUser, Src, Dst, Proto | where TotalBytes > bytesThreshold | extend TotalMB = round(TotalBytes / 1024.0 / 1024.0, 2) | order by TotalBytes desc @@ -33,6 +34,14 @@ entityMappings: fieldMappings: - identifier: HostName columnName: SrcNodeName + - entityType: Host + fieldMappings: + - identifier: HostName + columnName: DstNodeName + - entityType: Account + fieldMappings: + - identifier: FullName + columnName: SrcUser - entityType: IP fieldMappings: - identifier: Address diff --git a/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumMassFanOut.yaml b/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumMassFanOut.yaml index b8e2815aebb..5695dd4f8ec 100644 --- a/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumMassFanOut.yaml +++ b/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumMassFanOut.yaml @@ -23,9 +23,10 @@ query: | let dstThreshold = 25; Tailscale_Network_CL | where TimeGenerated > ago(15m) + | where HasVirtualTraffic | mv-expand t = VirtualTraffic | extend Src = tostring(t.src), Dst = tostring(t.dst) - | summarize UniqueDestinations = dcount(Dst), SrcNodeName = take_any(tostring(SrcNode.name)), TopDestinations = make_set(Dst, 25) by NodeId, Src + | summarize UniqueDestinations = dcount(Dst), TopDestinations = make_set(Dst, 25) by NodeId, SrcNodeName, SrcUser, SrcOs, SrcTags=tostring(SrcTags), Src | where UniqueDestinations >= dstThreshold | order by UniqueDestinations desc entityMappings: @@ -33,6 +34,10 @@ entityMappings: fieldMappings: - identifier: HostName columnName: SrcNodeName + - entityType: Account + fieldMappings: + - identifier: FullName + columnName: SrcUser - entityType: IP fieldMappings: - identifier: Address diff --git a/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumSubnetRouterThroughputAnomaly.yaml b/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumSubnetRouterThroughputAnomaly.yaml index d7c9815f2e3..70723c50128 100644 --- a/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumSubnetRouterThroughputAnomaly.yaml +++ b/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumSubnetRouterThroughputAnomaly.yaml @@ -25,14 +25,14 @@ query: | let recentTraffic = Tailscale_Network_CL | where TimeGenerated > ago(recent) - | where array_length(SubnetTraffic) > 0 + | where HasSubnetTraffic | mv-expand t = SubnetTraffic - | extend Bytes = tolong(t.txBytes) + tolong(t.rxBytes), SrcNodeName = tostring(SrcNode.name) - | summarize RecentBytes = sum(Bytes) by NodeId, SrcNodeName; + | extend Bytes = tolong(t.txBytes) + tolong(t.rxBytes) + | summarize RecentBytes = sum(Bytes) by NodeId, SrcNodeName, SrcUser, SrcOs, SrcTags=tostring(SrcTags); let baseline = Tailscale_Network_CL | where TimeGenerated between (ago(baselineDays + recent) .. ago(recent)) - | where array_length(SubnetTraffic) > 0 + | where HasSubnetTraffic | mv-expand t = SubnetTraffic | extend Bytes = tolong(t.txBytes) + tolong(t.rxBytes) | summarize TotalBaselineBytes = sum(Bytes) by NodeId @@ -41,13 +41,17 @@ query: | | join kind=inner baseline on NodeId | where RecentBytes > BaselineHourlyBytes * multiplier | extend Multiplier = round(RecentBytes / BaselineHourlyBytes, 1), RecentMB = round(RecentBytes / 1024.0 / 1024.0, 2), BaselineHourlyMB = round(BaselineHourlyBytes / 1024.0 / 1024.0, 2) - | project NodeId, SrcNodeName, RecentMB, BaselineHourlyMB, Multiplier + | project NodeId, SrcNodeName, SrcUser, SrcOs, SrcTags, RecentMB, BaselineHourlyMB, Multiplier | order by Multiplier desc entityMappings: - entityType: Host fieldMappings: - identifier: HostName columnName: SrcNodeName + - entityType: Account + fieldMappings: + - identifier: FullName + columnName: SrcUser incidentConfiguration: createIncident: true groupingConfiguration: diff --git a/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumUnexpectedExitNodeEgress.yaml b/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumUnexpectedExitNodeEgress.yaml index e95fe6c50f1..4826adbaef9 100644 --- a/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumUnexpectedExitNodeEgress.yaml +++ b/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumUnexpectedExitNodeEgress.yaml @@ -24,14 +24,14 @@ query: | let recentEgress = Tailscale_Network_CL | where TimeGenerated > ago(recent) - | where array_length(ExitTraffic) > 0 + | where HasExitTraffic | mv-expand t = ExitTraffic - | extend Src = tostring(t.src), ExitDst = tostring(t.dst), SrcNodeName = tostring(SrcNode.name), Bytes = tolong(t.txBytes) + tolong(t.rxBytes) - | summarize FirstSeen = min(TimeGenerated), TotalBytes = sum(Bytes) by NodeId, SrcNodeName, Src, ExitDst; + | extend Src = tostring(t.src), ExitDst = tostring(t.dst), Bytes = tolong(t.txBytes) + tolong(t.rxBytes) + | summarize FirstSeen = min(TimeGenerated), TotalBytes = sum(Bytes) by NodeId, SrcNodeName, SrcUser, SrcOs, Src, ExitDst; let baselineEgress = Tailscale_Network_CL | where TimeGenerated between (ago(baseline + recent) .. ago(recent)) - | where array_length(ExitTraffic) > 0 + | where HasExitTraffic | mv-expand t = ExitTraffic | extend Src = tostring(t.src), ExitDst = tostring(t.dst) | distinct NodeId, Src, ExitDst; @@ -43,6 +43,10 @@ entityMappings: fieldMappings: - identifier: HostName columnName: SrcNodeName + - entityType: Account + fieldMappings: + - identifier: FullName + columnName: SrcUser - entityType: IP fieldMappings: - identifier: Address diff --git a/Solutions/Tailscale (CCF)/Data Connectors/TailscaleAuditLogs_ccf/Tailscale_DCR.json b/Solutions/Tailscale (CCF)/Data Connectors/TailscaleAuditLogs_ccf/Tailscale_DCR.json index 204b891b03c..ad29d608675 100644 --- a/Solutions/Tailscale (CCF)/Data Connectors/TailscaleAuditLogs_ccf/Tailscale_DCR.json +++ b/Solutions/Tailscale (CCF)/Data Connectors/TailscaleAuditLogs_ccf/Tailscale_DCR.json @@ -16,6 +16,14 @@ "name": "eventGroupID", "type": "string" }, + { + "name": "type", + "type": "string" + }, + { + "name": "actionDetails", + "type": "string" + }, { "name": "actor", "type": "dynamic" @@ -351,7 +359,7 @@ "destinations": [ "sentinelWorkspace" ], - "transformKql": "source | extend TimeGenerated = eventTime | extend EventTime = eventTime | extend EventGroupID = eventGroupID | extend Actor = actor | extend Action = action | extend Target = target | extend Origin = origin | extend New = new | extend Old = old | project TimeGenerated, EventTime, EventGroupID, Actor, Action, Target, Origin, New, Old", + "transformKql": "source | extend TimeGenerated = eventTime | extend EventTime = eventTime | extend EventGroupID = eventGroupID | extend EventType = type | extend ActionDetails = actionDetails | extend Actor = actor | extend Action = action | extend Target = target | extend Origin = origin | extend New = new | extend Old = old | project TimeGenerated, EventTime, EventGroupID, EventType, ActionDetails, Actor, Action, Target, Origin, New, Old", "outputStream": "Custom-Tailscale_Audit_CL" }, { diff --git a/Solutions/Tailscale (CCF)/Data Connectors/TailscaleAuditLogs_ccf/Tailscale_tables.json b/Solutions/Tailscale (CCF)/Data Connectors/TailscaleAuditLogs_ccf/Tailscale_tables.json index cc66f067d17..91ab32a73b0 100644 --- a/Solutions/Tailscale (CCF)/Data Connectors/TailscaleAuditLogs_ccf/Tailscale_tables.json +++ b/Solutions/Tailscale (CCF)/Data Connectors/TailscaleAuditLogs_ccf/Tailscale_tables.json @@ -19,6 +19,14 @@ "name": "EventGroupID", "type": "string" }, + { + "name": "EventType", + "type": "string" + }, + { + "name": "ActionDetails", + "type": "string" + }, { "name": "Actor", "type": "dynamic" diff --git a/Solutions/Tailscale (CCF)/Data Connectors/TailscalePremium_ccf/TailscalePremium_DCR.json b/Solutions/Tailscale (CCF)/Data Connectors/TailscalePremium_ccf/TailscalePremium_DCR.json index efc87a67749..64ca601323a 100644 --- a/Solutions/Tailscale (CCF)/Data Connectors/TailscalePremium_ccf/TailscalePremium_DCR.json +++ b/Solutions/Tailscale (CCF)/Data Connectors/TailscalePremium_ccf/TailscalePremium_DCR.json @@ -16,6 +16,14 @@ "name": "eventGroupID", "type": "string" }, + { + "name": "type", + "type": "string" + }, + { + "name": "actionDetails", + "type": "string" + }, { "name": "actor", "type": "dynamic" @@ -274,11 +282,19 @@ } ] }, - "Custom-Tailscale_DnsNameservers_CL": { + "Custom-Tailscale_DnsConfig_CL": { "columns": [ { "name": "dns", "type": "dynamic" + }, + { + "name": "magicDNS", + "type": "boolean" + }, + { + "name": "searchPaths", + "type": "dynamic" } ] }, @@ -318,22 +334,6 @@ } ] }, - "Custom-Tailscale_DnsPreferences_CL": { - "columns": [ - { - "name": "magicDNS", - "type": "boolean" - } - ] - }, - "Custom-Tailscale_DnsSearchPaths_CL": { - "columns": [ - { - "name": "searchPaths", - "type": "dynamic" - } - ] - }, "Custom-Tailscale_Network_CL": { "columns": [ { @@ -427,7 +427,7 @@ "destinations": [ "sentinelWorkspace" ], - "transformKql": "source | extend TimeGenerated = eventTime | extend EventTime = eventTime | extend EventGroupID = eventGroupID | extend Actor = actor | extend Action = action | extend Target = target | extend Origin = origin | extend New = new | extend Old = old | project TimeGenerated, EventTime, EventGroupID, Actor, Action, Target, Origin, New, Old", + "transformKql": "source | extend TimeGenerated = eventTime | extend EventTime = eventTime | extend EventGroupID = eventGroupID | extend EventType = type | extend ActionDetails = actionDetails | extend Actor = actor | extend Action = action | extend Target = target | extend Origin = origin | extend New = new | extend Old = old | project TimeGenerated, EventTime, EventGroupID, EventType, ActionDetails, Actor, Action, Target, Origin, New, Old", "outputStream": "Custom-Tailscale_Audit_CL" }, { @@ -472,12 +472,12 @@ }, { "streams": [ - "Custom-Tailscale_DnsNameservers_CL" + "Custom-Tailscale_DnsConfig_CL" ], "destinations": [ "sentinelWorkspace" ], - "transformKql": "source | extend TimeGenerated = now() | extend ConfigType = 'nameservers' | extend Nameservers = dns | project TimeGenerated, ConfigType, Nameservers", + "transformKql": "source | extend TimeGenerated = now() | extend ConfigType = case(isnotnull(dns), 'nameservers', isnotnull(searchPaths), 'searchpaths', 'preferences') | extend Nameservers = dns | extend MagicDNS = magicDNS | extend SearchPaths = searchPaths | project TimeGenerated, ConfigType, Nameservers, MagicDNS, SearchPaths", "outputStream": "Custom-Tailscale_Dns_CL" }, { @@ -490,26 +490,6 @@ "transformKql": "source | extend TimeGenerated = now() | extend DevicesApprovalOn = devicesApprovalOn | extend DevicesAutoUpdatesOn = devicesAutoUpdatesOn | extend DevicesKeyDurationDays = devicesKeyDurationDays | extend UsersApprovalOn = usersApprovalOn | extend UsersRoleAllowedToJoinExternalTailnets = usersRoleAllowedToJoinExternalTailnets | extend NetworkFlowLoggingOn = networkFlowLoggingOn | extend RegionalRoutingOn = regionalRoutingOn | extend PostureIdentityCollectionOn = postureIdentityCollectionOn | project TimeGenerated, DevicesApprovalOn, DevicesAutoUpdatesOn, DevicesKeyDurationDays, UsersApprovalOn, UsersRoleAllowedToJoinExternalTailnets, NetworkFlowLoggingOn, RegionalRoutingOn, PostureIdentityCollectionOn", "outputStream": "Custom-Tailscale_Settings_CL" }, - { - "streams": [ - "Custom-Tailscale_DnsPreferences_CL" - ], - "destinations": [ - "sentinelWorkspace" - ], - "transformKql": "source | extend TimeGenerated = now() | extend ConfigType = 'preferences' | extend MagicDNS = magicDNS | project TimeGenerated, ConfigType, MagicDNS", - "outputStream": "Custom-Tailscale_Dns_CL" - }, - { - "streams": [ - "Custom-Tailscale_DnsSearchPaths_CL" - ], - "destinations": [ - "sentinelWorkspace" - ], - "transformKql": "source | extend TimeGenerated = now() | extend ConfigType = 'searchpaths' | extend SearchPaths = searchPaths | project TimeGenerated, ConfigType, SearchPaths", - "outputStream": "Custom-Tailscale_Dns_CL" - }, { "streams": [ "Custom-Tailscale_Network_CL" @@ -517,7 +497,7 @@ "destinations": [ "sentinelWorkspace" ], - "transformKql": "source | extend TimeGenerated = logged | extend NodeId = nodeId | extend FlowStart = start | extend FlowEnd = end | extend SrcNode = srcNode | extend DstNodes = dstNodes | extend VirtualTraffic = virtualTraffic | extend SubnetTraffic = subnetTraffic | extend ExitTraffic = exitTraffic | extend PhysicalTraffic = physicalTraffic | project TimeGenerated, NodeId, FlowStart, FlowEnd, SrcNode, DstNodes, VirtualTraffic, SubnetTraffic, ExitTraffic, PhysicalTraffic", + "transformKql": "source | extend TimeGenerated = logged | extend NodeId = nodeId | extend FlowStart = start | extend FlowEnd = end | extend SrcNode = srcNode | extend SrcUser = tostring(srcNode.user) | extend SrcNodeName = tostring(srcNode.name) | extend SrcOs = tostring(srcNode.os) | extend SrcTags = srcNode.tags | extend SrcAddresses = srcNode.addresses | extend DstNodes = dstNodes | extend DstCount = toint(array_length(dstNodes)) | extend DstNodeId = tostring(dstNodes[0].nodeId) | extend DstNodeName = tostring(dstNodes[0].name) | extend DstUser = tostring(dstNodes[0].user) | extend DstOs = tostring(dstNodes[0].os) | extend DstTags = dstNodes[0].tags | extend DstAddresses = dstNodes[0].addresses | extend VirtualTraffic = virtualTraffic | extend SubnetTraffic = subnetTraffic | extend ExitTraffic = exitTraffic | extend PhysicalTraffic = physicalTraffic | extend HasVirtualTraffic = array_length(virtualTraffic) > 0 | extend HasSubnetTraffic = array_length(subnetTraffic) > 0 | extend HasExitTraffic = array_length(exitTraffic) > 0 | extend HasPhysicalTraffic = array_length(physicalTraffic) > 0 | extend IsRelayed = tostring(physicalTraffic) contains '127.3.3.40' | project TimeGenerated, NodeId, FlowStart, FlowEnd, SrcNode, SrcUser, SrcNodeName, SrcOs, SrcTags, SrcAddresses, DstNodes, DstCount, DstNodeId, DstNodeName, DstUser, DstOs, DstTags, DstAddresses, VirtualTraffic, SubnetTraffic, ExitTraffic, PhysicalTraffic, HasVirtualTraffic, HasSubnetTraffic, HasExitTraffic, HasPhysicalTraffic, IsRelayed", "outputStream": "Custom-Tailscale_Network_CL" }, { diff --git a/Solutions/Tailscale (CCF)/Data Connectors/TailscalePremium_ccf/TailscalePremium_PollerConfig.json b/Solutions/Tailscale (CCF)/Data Connectors/TailscalePremium_ccf/TailscalePremium_PollerConfig.json index 0c5fa871db8..e00d83fc569 100644 --- a/Solutions/Tailscale (CCF)/Data Connectors/TailscalePremium_ccf/TailscalePremium_PollerConfig.json +++ b/Solutions/Tailscale (CCF)/Data Connectors/TailscalePremium_ccf/TailscalePremium_PollerConfig.json @@ -268,7 +268,7 @@ "dcrConfig": { "dataCollectionEndpoint": "[[parameters('dcrConfig').dataCollectionEndpoint]", "dataCollectionRuleImmutableId": "[[parameters('dcrConfig').dataCollectionRuleImmutableId]", - "streamName": "Custom-Tailscale_DnsNameservers_CL" + "streamName": "Custom-Tailscale_DnsConfig_CL" }, "auth": { "type": "OAuth2", @@ -310,7 +310,7 @@ "dcrConfig": { "dataCollectionEndpoint": "[[parameters('dcrConfig').dataCollectionEndpoint]", "dataCollectionRuleImmutableId": "[[parameters('dcrConfig').dataCollectionRuleImmutableId]", - "streamName": "Custom-Tailscale_DnsPreferences_CL" + "streamName": "Custom-Tailscale_DnsConfig_CL" }, "auth": { "type": "OAuth2", @@ -352,7 +352,7 @@ "dcrConfig": { "dataCollectionEndpoint": "[[parameters('dcrConfig').dataCollectionEndpoint]", "dataCollectionRuleImmutableId": "[[parameters('dcrConfig').dataCollectionRuleImmutableId]", - "streamName": "Custom-Tailscale_DnsSearchPaths_CL" + "streamName": "Custom-Tailscale_DnsConfig_CL" }, "auth": { "type": "OAuth2", diff --git a/Solutions/Tailscale (CCF)/Data Connectors/TailscalePremium_ccf/TailscalePremium_tables.json b/Solutions/Tailscale (CCF)/Data Connectors/TailscalePremium_ccf/TailscalePremium_tables.json index 24b04a9d5d1..5ab2dcabcf2 100644 --- a/Solutions/Tailscale (CCF)/Data Connectors/TailscalePremium_ccf/TailscalePremium_tables.json +++ b/Solutions/Tailscale (CCF)/Data Connectors/TailscalePremium_ccf/TailscalePremium_tables.json @@ -19,6 +19,14 @@ "name": "EventGroupID", "type": "string" }, + { + "name": "EventType", + "type": "string" + }, + { + "name": "ActionDetails", + "type": "string" + }, { "name": "Actor", "type": "dynamic" @@ -409,10 +417,58 @@ "name": "SrcNode", "type": "dynamic" }, + { + "name": "SrcUser", + "type": "string" + }, + { + "name": "SrcNodeName", + "type": "string" + }, + { + "name": "SrcOs", + "type": "string" + }, + { + "name": "SrcTags", + "type": "dynamic" + }, + { + "name": "SrcAddresses", + "type": "dynamic" + }, { "name": "DstNodes", "type": "dynamic" }, + { + "name": "DstCount", + "type": "int" + }, + { + "name": "DstNodeId", + "type": "string" + }, + { + "name": "DstNodeName", + "type": "string" + }, + { + "name": "DstUser", + "type": "string" + }, + { + "name": "DstOs", + "type": "string" + }, + { + "name": "DstTags", + "type": "dynamic" + }, + { + "name": "DstAddresses", + "type": "dynamic" + }, { "name": "VirtualTraffic", "type": "dynamic" @@ -428,6 +484,26 @@ { "name": "PhysicalTraffic", "type": "dynamic" + }, + { + "name": "HasVirtualTraffic", + "type": "boolean" + }, + { + "name": "HasSubnetTraffic", + "type": "boolean" + }, + { + "name": "HasExitTraffic", + "type": "boolean" + }, + { + "name": "HasPhysicalTraffic", + "type": "boolean" + }, + { + "name": "IsRelayed", + "type": "boolean" } ] }, diff --git a/Solutions/Tailscale (CCF)/Data/Solution_Tailscale.json b/Solutions/Tailscale (CCF)/Data/Solution_Tailscale.json index 08066805d99..edd83d9d060 100644 --- a/Solutions/Tailscale (CCF)/Data/Solution_Tailscale.json +++ b/Solutions/Tailscale (CCF)/Data/Solution_Tailscale.json @@ -3,8 +3,8 @@ "Author": "noodlemctwoodle - ccfconnectors.county118@passmail.com", "Logo": "", "Description": "The [Tailscale](https://tailscale.com/) solution for Microsoft Sentinel ingests Tailscale identity, device, configuration, audit and (Premium) network-flow telemetry via OAuth2-secured APIs. Built on the Codeless Connector Framework (CCF) - no Function App or container required.\n\n**Data connectors in this solution (install the one matching your Tailscale plan):**\n- **Tailscale Standard (CCF)** - Configuration audit, devices, users, keys, webhooks, DNS, settings. Use on **Personal (Free), Starter and Premium** tailnets.\n- **Tailscale Premium (CCF)** - Everything in Standard plus network flow logs and posture integrations. Use on **Premium and Enterprise** tailnets for full coverage.\n\n**Pre-requisites:**\n1. Sign in to [Tailscale OAuth settings](https://login.tailscale.com/admin/settings/oauth)\n2. Create an OAuth client with the scopes for your tier (see the README in this solution).\n3. Copy the client ID and client secret (secret shown once).\n4. Note your tailnet name (e.g. `tailb094d7.ts.net`) from the [Keys page](https://login.tailscale.com/admin/settings/keys).", - "BasePath": "/Users/tobygoulden/Source/_Personal_GH/Azure-Sentinel-fork-tailscale/Solutions/Tailscale (CCF)", - "Version": "3.0.5", + "BasePath": "C:\\GitHub\\azure-Sentinel\\Solutions\\Tailscale (CCF)", + "Version": "3.0.0", "TemplateSpec": false, "Is1PConnector": false, "Data Connectors": [ @@ -13,6 +13,7 @@ ], "Analytic Rules": [ "Analytic Rules/TailscaleNewAPIaccesstokenorOAuthclientcreated.yaml", + "Analytic Rules/TailscaleOAuthClientCreatedWithWriteScopes.yaml", "Analytic Rules/TailscalePolicyfileACLmodified.yaml", "Analytic Rules/TailscaleAuthkeycreated.yaml", "Analytic Rules/TailscaleExitnodeadvertisedorapproved.yaml", @@ -33,7 +34,8 @@ "Analytic Rules/TailscalePremiumMassFanOut.yaml", "Analytic Rules/TailscalePremiumSubnetRouterThroughputAnomaly.yaml", "Analytic Rules/TailscalePremiumPostureIntegrationDisabled.yaml", - "Analytic Rules/TailscalePremiumNewPostureIntegration.yaml" + "Analytic Rules/TailscalePremiumNewPostureIntegration.yaml", + "Analytic Rules/TailscalePremiumDerpRelaySurge.yaml" ], "Hunting Queries": [ "Hunting Queries/TailscaleFirstSeenActor.yaml", @@ -52,7 +54,12 @@ "Hunting Queries/TailscalePremiumTopTalkers.yaml", "Hunting Queries/TailscalePremiumExitNodeUsage.yaml", "Hunting Queries/TailscalePremiumBeaconingCandidates.yaml", - "Hunting Queries/TailscalePremiumPostureInventory.yaml" + "Hunting Queries/TailscalePremiumPostureInventory.yaml", + "Hunting Queries/TailscalePremiumDerpRelayPersistence.yaml", + "Hunting Queries/TailscalePremiumTaggedServiceFanIn.yaml", + "Hunting Queries/TailscalePremiumCrossTagFlowMatrix.yaml", + "Hunting Queries/TailscalePremiumOffHoursFlows.yaml", + "Hunting Queries/TailscalePremiumUserMultiDevice.yaml" ], "Workbooks": [ "Workbooks/TailscaleStandardOperations.json", diff --git a/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleAuthKeySprawl.yaml b/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleAuthKeySprawl.yaml index 47f3c118375..b9b3e83fc9b 100644 --- a/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleAuthKeySprawl.yaml +++ b/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleAuthKeySprawl.yaml @@ -1,7 +1,7 @@ id: d64e7f6f-4eab-7a5d-cf4a-3b6e8aafbcad name: "Tailscale: Auth key sprawl" description: | - Actors creating multiple auth keys in a short window. A single admin creating many keys for unattended enrollment is normal during a rollout; the same pattern can also indicate token-spraying for persistence. Cross-reference with the Tailscale: Auth key created alert to filter context. + Identifies actors creating multiple auth keys in a short window. A single admin creating many keys for unattended enrollment is normal during a rollout; the same pattern can also indicate token-spraying for persistence. Cross-reference with the Tailscale: Auth key created alert to filter context. requiredDataConnectors: - connectorId: TailscaleCCF dataTypes: diff --git a/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleDevicesWithSshEnabled.yaml b/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleDevicesWithSshEnabled.yaml index a84947a623d..fde464a26fd 100644 --- a/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleDevicesWithSshEnabled.yaml +++ b/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleDevicesWithSshEnabled.yaml @@ -1,7 +1,7 @@ id: c3d4e5f6-7890-1234-5678-901234560044 name: "Tailscale: Devices with Tailscale SSH enabled" description: | - Inventory of devices that currently have Tailscale SSH enabled. Tailscale SSH delivers SSH access over the tailnet using Tailscale identity (no SSH keys needed) and is governed by the SSH ACL block in the policy file. Cross-reference this list with the SSH ACL to confirm only the intended devices are reachable as SSH targets. + Identifies devices that currently have Tailscale SSH enabled. Tailscale SSH delivers SSH access over the tailnet using Tailscale identity (no SSH keys needed) and is governed by the SSH ACL block in the policy file. Cross-reference this list with the SSH ACL to confirm only the intended devices are reachable as SSH targets. requiredDataConnectors: - connectorId: TailscaleCCF dataTypes: diff --git a/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleDormantDevices.yaml b/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleDormantDevices.yaml index c510a59837b..2ab88f565e6 100644 --- a/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleDormantDevices.yaml +++ b/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleDormantDevices.yaml @@ -1,7 +1,7 @@ id: e4d5f6a7-4567-8901-23ab-cdef12345004 name: "Tailscale: Devices not seen in 30+ days" description: | - Lists tailnet devices that have not connected to the control plane for at least 30 days. Dormant devices accumulate risk - they may still have valid keys, advertised routes, or tags but no operational oversight. Candidate for removal or key rotation. + Identifies tailnet devices that have not connected to the control plane for at least 30 days. Dormant devices accumulate risk - they may still have valid keys, advertised routes, or tags but no operational oversight. Candidate for removal or key rotation. requiredDataConnectors: - connectorId: TailscaleCCF dataTypes: diff --git a/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleExternalDeviceInventory.yaml b/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleExternalDeviceInventory.yaml index 6c0978535a0..6fce5a0e915 100644 --- a/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleExternalDeviceInventory.yaml +++ b/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleExternalDeviceInventory.yaml @@ -1,7 +1,7 @@ id: d4e5f6a7-8901-2345-6789-012345670045 name: "Tailscale: External (shared-in) device inventory" description: | - Inventory of external (shared-in) devices currently active in the tailnet. These devices belong to another tailnet and have been admitted via a Tailscale sharing arrangement. Confirm each entry maps to a documented collaboration and that the corresponding ACL restricts the device to the intended resources. + Identifies external (shared-in) devices currently active in the tailnet. These devices belong to another tailnet and have been admitted via a Tailscale sharing arrangement. Confirm each entry maps to a documented collaboration and that the corresponding ACL restricts the device to the intended resources. requiredDataConnectors: - connectorId: TailscaleCCF dataTypes: diff --git a/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleFirstSeenActor.yaml b/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleFirstSeenActor.yaml index 545df8d85f1..de917c1d588 100644 --- a/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleFirstSeenActor.yaml +++ b/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleFirstSeenActor.yaml @@ -1,7 +1,7 @@ id: a91f4d3c-1b7e-4f2a-9c1d-0e3b5f7c8d9a name: "Tailscale: First-seen actor making configuration changes" description: | - Surfaces actors making their first configuration change in the lookback window. New legitimate admins look identical to compromised credentials - review whether each surfaced actor is expected to have admin rights. + Identifies actors making their first configuration change in the lookback window. New legitimate admins look identical to compromised credentials - review whether each surfaced actor is expected to have admin rights. requiredDataConnectors: - connectorId: TailscaleCCF dataTypes: diff --git a/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleOffHoursConfigChanges.yaml b/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleOffHoursConfigChanges.yaml index 0df6a5d895c..60e0b91791c 100644 --- a/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleOffHoursConfigChanges.yaml +++ b/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleOffHoursConfigChanges.yaml @@ -1,7 +1,7 @@ id: c73f6e5e-3d9a-6f4c-be3f-2a5d7f9eafbc name: "Tailscale: Off-hours configuration changes" description: | - Lists configuration audit events that occurred outside typical business hours (defined as Monday-Friday 08:00-18:00 UTC). Adjust the time window to match your operating region. Useful for spotting impromptu maintenance, account compromise, or insider activity. + Identifies configuration audit events that occurred outside typical business hours (defined as Monday-Friday 08:00-18:00 UTC). Adjust the time window to match your operating region. Useful for spotting impromptu maintenance, account compromise, or insider activity. requiredDataConnectors: - connectorId: TailscaleCCF dataTypes: diff --git a/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleOrphanedUsers.yaml b/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleOrphanedUsers.yaml index 291ebd4b71e..e58252d2216 100644 --- a/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleOrphanedUsers.yaml +++ b/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleOrphanedUsers.yaml @@ -1,7 +1,7 @@ id: a6f7b8c9-6789-0123-45ab-cdef12345006 name: "Tailscale: Users with zero devices" description: | - Lists tailnet users who have no devices currently registered. Orphaned identities are candidates for off-boarding - they retain whatever role/permissions they were granted and can still create auth keys or invite others. Review against your HR / identity-provider join-leaver records. + Identifies tailnet users who have no devices currently registered. Orphaned identities are candidates for off-boarding - they retain whatever role/permissions they were granted and can still create auth keys or invite others. Review against your HR / identity-provider join-leaver records. requiredDataConnectors: - connectorId: TailscaleCCF dataTypes: diff --git a/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleOutdatedClients.yaml b/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleOutdatedClients.yaml index edc1b13e09d..06026b74b41 100644 --- a/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleOutdatedClients.yaml +++ b/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleOutdatedClients.yaml @@ -1,7 +1,7 @@ id: e5f6a7b8-9012-3456-7890-123456780046 name: "Tailscale: Devices with outdated client version" description: | - Lists tailnet devices that report UpdateAvailable=true on the latest snapshot. Tailscale releases security and feature updates regularly; a device showing UpdateAvailable lacks the most recent improvements. Use this list to plan a fleet update, especially before enabling new ACL features that require minimum client versions. + Identifies tailnet devices that report UpdateAvailable=true on the latest snapshot. Tailscale releases security and feature updates regularly; a device showing UpdateAvailable lacks the most recent improvements. Use this list to plan a fleet update, especially before enabling new ACL features that require minimum client versions. requiredDataConnectors: - connectorId: TailscaleCCF dataTypes: diff --git a/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumCrossTagFlowMatrix.yaml b/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumCrossTagFlowMatrix.yaml new file mode 100644 index 00000000000..03c18902fde --- /dev/null +++ b/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumCrossTagFlowMatrix.yaml @@ -0,0 +1,41 @@ +id: a8978f27-3c85-4c29-a45a-c4a5e43fef2d +name: "Tailscale Premium: Cross-tag flow matrix" +description: | + Identifies network flows pivoted by source-tag x destination-tag over the past 7 days, treating untagged user devices as ``. Highlights which tagged services interact - useful for ACL validation, detecting unexpected tag-to-tag traffic, and spotting tag-to-same-tag loops that can indicate worm-style propagation or service-mesh anomalies. Order by Flows to find the heaviest tag pairs; sort by DistinctSrcDevices to find broadly-used services. Requires Tailscale Premium or Enterprise. +requiredDataConnectors: + - connectorId: TailscalePremiumCCF + dataTypes: + - Tailscale_Network_CL +tactics: + - LateralMovement + - Discovery +relevantTechniques: + - T1021 + - T1046 +query: | + Tailscale_Network_CL + | where TimeGenerated > ago(7d) + | extend SrcCategory = case( + isnotempty(SrcTags), tostring(SrcTags), + isnotempty(SrcUser), "", + "") + | extend DstCategory = case( + isnotempty(DstTags), tostring(DstTags), + isnotempty(DstUser), "", + "") + | summarize + Flows = count(), + DistinctSrcDevices = dcount(SrcNodeName), + DistinctDstDevices = dcount(DstNodeName), + FirstSeen = min(TimeGenerated), + LastSeen = max(TimeGenerated) + by SrcCategory, DstCategory + | extend SameTagLoop = SrcCategory == DstCategory and SrcCategory != "" and SrcCategory != "" + | project SrcCategory, DstCategory, Flows, DistinctSrcDevices, DistinctDstDevices, SameTagLoop, FirstSeen, LastSeen + | order by Flows desc +entityMappings: + - entityType: CloudApplication + fieldMappings: + - identifier: Name + columnName: SrcCategory +version: 1.0.0 diff --git a/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumDerpRelayPersistence.yaml b/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumDerpRelayPersistence.yaml new file mode 100644 index 00000000000..30e94f9bbfe --- /dev/null +++ b/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumDerpRelayPersistence.yaml @@ -0,0 +1,37 @@ +id: 20457fba-08e2-42d7-b972-fbe9acf583c8 +name: "Tailscale Premium: Devices with persistent DERP relay usage" +description: | + Identifies devices that have consistently fallen back to DERP relay (IsRelayed = true) over the past 24 hours. Sustained relay usage points to NAT/firewall misconfiguration on the device's network, a tunnel-blocking middlebox, or in rare cases deliberate evasion attempting to obscure direct peer-to-peer paths. Useful for proactive network-hygiene investigation and capacity planning. Requires Tailscale Premium or Enterprise. +requiredDataConnectors: + - connectorId: TailscalePremiumCCF + dataTypes: + - Tailscale_Network_CL +tactics: + - CommandAndControl +relevantTechniques: + - T1572 +query: | + Tailscale_Network_CL + | where TimeGenerated > ago(24h) + | summarize + TotalFlows = count(), + RelayedFlows = countif(IsRelayed), + DistinctDsts = dcount(DstNodeName), + FirstFlow = min(TimeGenerated), + LastFlow = max(TimeGenerated) + by SrcNodeName, SrcUser, SrcOs, SrcTags=tostring(SrcTags) + | where TotalFlows >= 50 + | extend RelayedPct = round(100.0 * RelayedFlows / TotalFlows, 1) + | where RelayedPct >= 30.0 + | project SrcNodeName, SrcUser, SrcOs, SrcTags, TotalFlows, RelayedFlows, RelayedPct, DistinctDsts, FirstFlow, LastFlow + | order by RelayedPct desc, TotalFlows desc +entityMappings: + - entityType: Host + fieldMappings: + - identifier: HostName + columnName: SrcNodeName + - entityType: Account + fieldMappings: + - identifier: FullName + columnName: SrcUser +version: 1.0.0 diff --git a/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumExitNodeUsage.yaml b/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumExitNodeUsage.yaml index 5c2a4122934..f1709e06371 100644 --- a/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumExitNodeUsage.yaml +++ b/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumExitNodeUsage.yaml @@ -1,7 +1,7 @@ id: a37bacc1-7bde-ad8a-f27d-6e9bcdcecadb name: "Tailscale Premium: Exit-node usage patterns" description: | - Summarises traffic leaving the tailnet via exit nodes. Exit node use is typically intentional (regional egress, privacy routing) but unexpected exit-node traffic from a node should be investigated as a potential pivot point or unsanctioned egress. Requires Tailscale Premium or Enterprise. + Identifies traffic leaving the tailnet via exit nodes. Exit node use is typically intentional (regional egress, privacy routing) but unexpected exit-node traffic from a node should be investigated as a potential pivot point or unsanctioned egress. Requires Tailscale Premium or Enterprise. requiredDataConnectors: - connectorId: TailscalePremiumCCF dataTypes: diff --git a/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumNewNodePairs.yaml b/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumNewNodePairs.yaml index b8c9b6be3cf..e4a8500e422 100644 --- a/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumNewNodePairs.yaml +++ b/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumNewNodePairs.yaml @@ -1,7 +1,7 @@ id: e55f8aaf-5fbc-8b6e-d05b-4c7faabcadbe name: "Tailscale Premium: New src->dst node pairs (lateral movement candidates)" description: | - Lists tailnet src->dst pairs observed in the last 24h that were NOT observed in the prior 7-day baseline. Useful for spotting lateral movement to nodes that don't usually talk to each other. Requires Tailscale Premium or Enterprise (network flow logs). + Identifies tailnet src->dst pairs observed in the last 24h that were NOT observed in the prior 7-day baseline. Useful for spotting lateral movement to nodes that don't usually talk to each other. Requires Tailscale Premium or Enterprise (network flow logs). requiredDataConnectors: - connectorId: TailscalePremiumCCF dataTypes: diff --git a/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumOffHoursFlows.yaml b/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumOffHoursFlows.yaml new file mode 100644 index 00000000000..9419376a05e --- /dev/null +++ b/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumOffHoursFlows.yaml @@ -0,0 +1,40 @@ +id: 622ce88a-0838-4bbe-8a00-ab8ac8377f41 +name: "Tailscale Premium: Network flows outside business hours" +description: | + Identifies network flows occurring outside 07:00-19:00 UTC on weekdays, plus all of weekend, over the past 7 days. Filters to virtual/subnet/exit traffic (drops DERP-only keepalive noise). Useful for spotting unattended automation gone wrong, scheduled exfiltration, or unsanctioned after-hours access by humans. The TaggedSource column makes it easy to separate cron-like service traffic (tag:backup, tag:cron) from human user activity. Requires Tailscale Premium or Enterprise. +requiredDataConnectors: + - connectorId: TailscalePremiumCCF + dataTypes: + - Tailscale_Network_CL +tactics: + - Exfiltration + - CommandAndControl +relevantTechniques: + - T1029 + - T1071 +query: | + Tailscale_Network_CL + | where TimeGenerated > ago(7d) + | where HasVirtualTraffic or HasSubnetTraffic or HasExitTraffic + | extend HourUtc = hourofday(TimeGenerated), Dow = dayofweek(TimeGenerated) + | where HourUtc < 7 or HourUtc >= 19 or Dow in (0d, 6d) + | extend TaggedSource = isnotempty(SrcTags) + | summarize + Flows = count(), + FirstFlow = min(TimeGenerated), + LastFlow = max(TimeGenerated), + DistinctHours = dcount(bin(TimeGenerated, 1h)) + by SrcNodeName, SrcUser, SrcTags=tostring(SrcTags), DstNodeName, DstTags=tostring(DstTags), TaggedSource + | where Flows >= 5 + | project SrcNodeName, SrcUser, SrcTags, TaggedSource, DstNodeName, DstTags, Flows, DistinctHours, FirstFlow, LastFlow + | order by Flows desc +entityMappings: + - entityType: Host + fieldMappings: + - identifier: HostName + columnName: SrcNodeName + - entityType: Account + fieldMappings: + - identifier: FullName + columnName: SrcUser +version: 1.0.0 diff --git a/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumPostureInventory.yaml b/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumPostureInventory.yaml index 8181369fa0e..15b14198492 100644 --- a/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumPostureInventory.yaml +++ b/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumPostureInventory.yaml @@ -1,7 +1,7 @@ id: c3d4e5f6-7890-1234-56ab-cdef12345032 name: "Tailscale Premium: Current posture integration inventory" description: | - Lists the current set of device-posture integrations configured on the tailnet (latest snapshot per integration). Useful for compliance attestation - confirms what MDM/EDR systems are connected and what their status is. Compare against the expected baseline to detect drift. Requires Tailscale Premium or Enterprise. + Identifies the current set of device-posture integrations configured on the tailnet (latest snapshot per integration). Useful for compliance attestation - confirms what MDM/EDR systems are connected and what their status is. Compare against the expected baseline to detect drift. Requires Tailscale Premium or Enterprise. requiredDataConnectors: - connectorId: TailscalePremiumCCF dataTypes: diff --git a/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumTaggedServiceFanIn.yaml b/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumTaggedServiceFanIn.yaml new file mode 100644 index 00000000000..ec1e49bf559 --- /dev/null +++ b/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumTaggedServiceFanIn.yaml @@ -0,0 +1,36 @@ +id: f8d4e7bc-3450-4c55-84ac-90e6e9c6b8fe +name: "Tailscale Premium: Tagged services with broad inbound exposure" +description: | + Identifies tagged services (devices with non-empty DstTags) ranked by inbound diversity over 7 days. Baseline: a tag:plex serving the household typically sees connections from 1-3 user devices; a tag:db or tag:internal that receives connections from 10+ distinct users or 3+ OS families may indicate ACL drift, credential sharing, or unauthorised access. Sort by DistinctSrcDevices to surface services with the broadest blast-radius. Requires Tailscale Premium or Enterprise. +requiredDataConnectors: + - connectorId: TailscalePremiumCCF + dataTypes: + - Tailscale_Network_CL +tactics: + - LateralMovement + - InitialAccess +relevantTechniques: + - T1021 + - T1133 +query: | + Tailscale_Network_CL + | where TimeGenerated > ago(7d) + | where isnotempty(DstTags) + | where HasVirtualTraffic or HasSubnetTraffic + | summarize + DistinctSrcUsers = dcount(SrcUser), + DistinctSrcDevices = dcount(SrcNodeName), + DistinctSrcOs = dcount(SrcOs), + Flows = count(), + FirstFlow = min(TimeGenerated), + LastFlow = max(TimeGenerated) + by DstNodeName, DstTags=tostring(DstTags) + | extend ShortDstName = tostring(split(DstNodeName, ".")[0]) + | project ShortDstName, DstTags, DistinctSrcDevices, DistinctSrcUsers, DistinctSrcOs, Flows, FirstFlow, LastFlow + | order by DistinctSrcDevices desc, Flows desc +entityMappings: + - entityType: Host + fieldMappings: + - identifier: HostName + columnName: ShortDstName +version: 1.0.0 diff --git a/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumTopTalkers.yaml b/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumTopTalkers.yaml index f1ed6deff0f..8d4d8455275 100644 --- a/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumTopTalkers.yaml +++ b/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumTopTalkers.yaml @@ -1,7 +1,7 @@ id: f46a9bb0-6acd-9c7f-e16c-5d8abbcdbeca name: "Tailscale Premium: Top talkers by bytes (virtual traffic)" description: | - Ranks tailnet src->dst pairs by total bytes transferred over the last 24h. Useful for capacity planning, identifying data-heavy flows, and spotting unexpected volume that could indicate data staging. Requires Tailscale Premium or Enterprise. + Identifies tailnet src->dst pairs ranked by total bytes transferred over the last 24h. Useful for capacity planning, identifying data-heavy flows, and spotting unexpected volume that could indicate data staging. Requires Tailscale Premium or Enterprise. requiredDataConnectors: - connectorId: TailscalePremiumCCF dataTypes: diff --git a/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumUserMultiDevice.yaml b/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumUserMultiDevice.yaml new file mode 100644 index 00000000000..ee35cccb8ba --- /dev/null +++ b/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumUserMultiDevice.yaml @@ -0,0 +1,43 @@ +id: daac10bd-d842-4122-90cc-9957256f04e3 +name: "Tailscale Premium: Users generating traffic from multiple devices" +description: | + Identifies users (SrcUser) generating tailnet flows from more than one distinct device in the past 24 hours. Normal for a user with phone + laptop. Useful for spotting account compromise (sudden new device for a user), unauthorised device enrollment, or device sharing across users. Cross-reference NewDevicesToday against Tailscale_Devices_CL.Created to confirm whether each device is genuinely new vs long-known. Requires Tailscale Premium or Enterprise. +requiredDataConnectors: + - connectorId: TailscalePremiumCCF + dataTypes: + - Tailscale_Network_CL + - Tailscale_Devices_CL +tactics: + - InitialAccess + - Persistence +relevantTechniques: + - T1078 +query: | + let recent = Tailscale_Network_CL + | where TimeGenerated > ago(24h) + | where isnotempty(SrcUser) + | summarize + Devices = make_set(SrcNodeName, 20), + DeviceCount = dcount(SrcNodeName), + OsTypes = make_set(SrcOs, 10), + FirstFlow = min(TimeGenerated), + LastFlow = max(TimeGenerated), + Flows = count() + by SrcUser + | where DeviceCount >= 2; + let devicesCreatedToday = Tailscale_Devices_CL + | where TimeGenerated > ago(24h) + | summarize arg_max(TimeGenerated, *) by DeviceId + | where Created > ago(24h) + | distinct DeviceName; + recent + | extend NewDevicesToday = set_intersect(Devices, toscalar(devicesCreatedToday | summarize make_set(DeviceName))) + | extend HasNewDevice = array_length(NewDevicesToday) > 0 + | project SrcUser, DeviceCount, Devices, OsTypes, HasNewDevice, NewDevicesToday, Flows, FirstFlow, LastFlow + | order by HasNewDevice desc, DeviceCount desc +entityMappings: + - entityType: Account + fieldMappings: + - identifier: FullName + columnName: SrcUser +version: 1.0.0 diff --git a/Solutions/Tailscale (CCF)/PREMIUM-ENDPOINTS.md b/Solutions/Tailscale (CCF)/PREMIUM-ENDPOINTS.md index 0e25abb8c0a..a4d182b0957 100644 --- a/Solutions/Tailscale (CCF)/PREMIUM-ENDPOINTS.md +++ b/Solutions/Tailscale (CCF)/PREMIUM-ENDPOINTS.md @@ -19,13 +19,20 @@ Endpoints that gave 403 or are explicitly Premium-tier features. These are NOT p | `/user-invites` | needs WRITE `users` scope | 403 with `users:read` | Tailscale requires write scope to read invites (PII concern). Audit log captures invite events (CREATE/UPDATE/DELETE on USER_INVITE targets) so we have visibility without granting elevated scope. | | `/acl` snapshot | `policy_file:read` | 403 with our scopes | Audit log captures full ACL document on every change (Old + New + Actor). Snapshot is redundant. | +## Aliases / paths that 404 because the data lives at the canonical name + +Some external references (third-party MCP servers, blog posts) use names that Tailscale's API returns 404 for at the exact path - but the data is already available via the canonical endpoint, which the connector polls today. Don't add new pollers for these: + +- `/audit-logs` -> canonical path is `/logging/configuration`, polled by both Standard and Premium audit pollers. Returns `{"logs":[...]}` with `start`/`end` query params (verified live, May 2026). +- `/network-flow-logs` -> canonical path is `/logging/network`, polled by the Premium network poller. `/network-logs` is also a working alias for the same data. + ## Endpoints that 404'd on Tailscale's API (tier-independent) -These paths appear in some references (e.g. third-party MCP servers) but Tailscale's actual API returns 404 - feature may have moved, been removed, or never existed at that path: +Genuinely missing paths - no known canonical alternative: - `/services`, `/services/*` - Tailscale Services (newer feature, may require enablement) - `/contacts` - tailnet contact info -- `/status`, `/audit-logs`, `/log-streaming`, `/network-flow-logs`, `/nameservers` - alternate paths used by some clients +- `/status`, `/log-streaming`, `/nameservers` - alternate paths used by some clients If Tailscale ships these endpoints in future, add as needed. diff --git a/Solutions/Tailscale (CCF)/Package/3.0.0.zip b/Solutions/Tailscale (CCF)/Package/3.0.0.zip new file mode 100644 index 0000000000000000000000000000000000000000..08b165c1c3bdf9033f74245b9aeb3375ec612007 GIT binary patch literal 77845 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

hoX_&n}Kg+zjjx1wGDIB73CTfFQN`vcPMH! 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 T!T*;b%*P%6@%Cef{%HRP^E6Ls literal 0 HcmV?d00001 diff --git a/Solutions/Tailscale (CCF)/Package/3.0.5.zip b/Solutions/Tailscale (CCF)/Package/3.0.5.zip deleted file mode 100644 index d4391e580d09a10b08a5da210724806e04de8d36..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 66176 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 F{vTaTPH6xD diff --git a/Solutions/Tailscale (CCF)/Package/createUiDefinition.json b/Solutions/Tailscale (CCF)/Package/createUiDefinition.json index 5250bfcff4e..7a365b1194c 100644 --- a/Solutions/Tailscale (CCF)/Package/createUiDefinition.json +++ b/Solutions/Tailscale (CCF)/Package/createUiDefinition.json @@ -6,7 +6,7 @@ "config": { "isWizard": false, "basics": { - "description": "\n\n**Note:** Please refer to the following before installing the solution: \n\n• Review the solution [Release Notes](https://github.com/Azure/Azure-Sentinel/tree/master/Solutions/Tailscale%20%28CCF%29/ReleaseNotes.md)\n\n • There may be [known issues](https://aka.ms/sentinelsolutionsknownissues) pertaining to this Solution, please refer to them before installing.\n\nThe [Tailscale](https://tailscale.com/) solution for Microsoft Sentinel ingests Tailscale identity, device, configuration, audit and (Premium) network-flow telemetry via OAuth2-secured APIs. Built on the Codeless Connector Framework (CCF) - no Function App or container required.\n\n**Data connectors in this solution (install the one matching your Tailscale plan):**\n- **Tailscale Standard (CCF)** - Configuration audit, devices, users, keys, webhooks, DNS, settings. Use on **Personal (Free), Starter and Premium** tailnets.\n- **Tailscale Premium (CCF)** - Everything in Standard plus network flow logs and posture integrations. Use on **Premium and Enterprise** tailnets for full coverage.\n\n**Pre-requisites:**\n1. Sign in to [Tailscale OAuth settings](https://login.tailscale.com/admin/settings/oauth)\n2. Create an OAuth client with the scopes for your tier (see the README in this solution).\n3. Copy the client ID and client secret (secret shown once).\n4. Note your tailnet name (e.g. `tailb094d7.ts.net`) from the [Keys page](https://login.tailscale.com/admin/settings/keys).\n\n**Data Connectors:** 2, **Workbooks:** 2, **Analytic Rules:** 22, **Hunting Queries:** 17\n\n[Learn more about Microsoft Sentinel](https://aka.ms/azuresentinel) | [Learn more about Solutions](https://aka.ms/azuresentinelsolutionsdoc)", + "description": "\n\n**Note:** Please refer to the following before installing the solution: \n\n• Review the solution [Release Notes](https://github.com/Azure/Azure-Sentinel/tree/master/Solutions/Tailscale%20%28CCF%29/ReleaseNotes.md)\n\n • There may be [known issues](https://aka.ms/sentinelsolutionsknownissues) pertaining to this Solution, please refer to them before installing.\n\nThe [Tailscale](https://tailscale.com/) solution for Microsoft Sentinel ingests Tailscale identity, device, configuration, audit and (Premium) network-flow telemetry via OAuth2-secured APIs. Built on the Codeless Connector Framework (CCF) - no Function App or container required.\n\n**Data connectors in this solution (install the one matching your Tailscale plan):**\n- **Tailscale Standard (CCF)** - Configuration audit, devices, users, keys, webhooks, DNS, settings. Use on **Personal (Free), Starter and Premium** tailnets.\n- **Tailscale Premium (CCF)** - Everything in Standard plus network flow logs and posture integrations. Use on **Premium and Enterprise** tailnets for full coverage.\n\n**Pre-requisites:**\n1. Sign in to [Tailscale OAuth settings](https://login.tailscale.com/admin/settings/oauth)\n2. Create an OAuth client with the scopes for your tier (see the README in this solution).\n3. Copy the client ID and client secret (secret shown once).\n4. Note your tailnet name (e.g. `tailb094d7.ts.net`) from the [Keys page](https://login.tailscale.com/admin/settings/keys).\n\n**Data Connectors:** 2, **Workbooks:** 2, **Analytic Rules:** 24, **Hunting Queries:** 22\n\n[Learn more about Microsoft Sentinel](https://aka.ms/azuresentinel) | [Learn more about Solutions](https://aka.ms/azuresentinelsolutionsdoc)", "subscription": { "resourceProviders": [ "Microsoft.OperationsManagement/solutions", @@ -191,13 +191,13 @@ { "name": "analytic2", "type": "Microsoft.Common.Section", - "label": "Tailscale: Policy file (ACL) modified", + "label": "Tailscale: OAuth client or API key created with write scopes", "elements": [ { "name": "analytic2-text", "type": "Microsoft.Common.TextBlock", "options": { - "text": "Identifies when the tailnet ACL/policy file is modified. Review the diff - incorrect ACLs can silently expand blast radius across the tailnet." + "text": "Detects creation of a Tailscale OAuth client or API access key whose granted scopes include WRITE permissions (anything matching \":write\"). Tokens with write scopes can modify tailnet configuration, manage devices, write ACLs, and revoke keys - high-value adversary targets. Compare against the recent actor history; revoke immediately if unexpected." } } ] @@ -205,13 +205,13 @@ { "name": "analytic3", "type": "Microsoft.Common.Section", - "label": "Tailscale: Auth key created", + "label": "Tailscale: Policy file (ACL) modified", "elements": [ { "name": "analytic3-text", "type": "Microsoft.Common.TextBlock", "options": { - "text": "Identifies when a new Tailscale auth key is generated. Auth keys allow unattended device enrollment into the tailnet - confirm it was expected and revoke if not." + "text": "Identifies when the tailnet ACL/policy file is modified. Review the diff - incorrect ACLs can silently expand blast radius across the tailnet." } } ] @@ -219,13 +219,13 @@ { "name": "analytic4", "type": "Microsoft.Common.Section", - "label": "Tailscale: Exit node advertised or approved", + "label": "Tailscale: Auth key created", "elements": [ { "name": "analytic4-text", "type": "Microsoft.Common.TextBlock", "options": { - "text": "Identifies when a device starts advertising itself as an exit node, or when an admin approves one. Validate the device and operator - rogue exit nodes can intercept tailnet egress." + "text": "Identifies when a new Tailscale auth key is generated. Auth keys allow unattended device enrollment into the tailnet - confirm it was expected and revoke if not." } } ] @@ -233,13 +233,13 @@ { "name": "analytic5", "type": "Microsoft.Common.Section", - "label": "Tailscale: Mass credential revocation in short window", + "label": "Tailscale: Exit node advertised or approved", "elements": [ { "name": "analytic5-text", "type": "Microsoft.Common.TextBlock", "options": { - "text": "Identifies when five or more API keys, OAuth clients, or auth keys are revoked or deleted within one hour. May be routine rotation, or a typical cleanup pattern after credential compromise." + "text": "Identifies when a device starts advertising itself as an exit node, or when an admin approves one. Validate the device and operator - rogue exit nodes can intercept tailnet egress." } } ] @@ -247,13 +247,13 @@ { "name": "analytic6", "type": "Microsoft.Common.Section", - "label": "Tailscale: Device key expiring within 7 days", + "label": "Tailscale: Mass credential revocation in short window", "elements": [ { "name": "analytic6-text", "type": "Microsoft.Common.TextBlock", "options": { - "text": "Identifies tailnet devices whose machine key expires within the next 7 days and where key expiry is not disabled. Surface proactively so renewal can be scheduled rather than forced during an outage." + "text": "Identifies when five or more API keys, OAuth clients, or auth keys are revoked or deleted within one hour. May be routine rotation, or a typical cleanup pattern after credential compromise." } } ] @@ -261,13 +261,13 @@ { "name": "analytic7", "type": "Microsoft.Common.Section", - "label": "Tailscale: Device started advertising subnet routes", + "label": "Tailscale: Device key expiring within 7 days", "elements": [ { "name": "analytic7-text", "type": "Microsoft.Common.TextBlock", "options": { - "text": "Identifies when a tailnet device begins advertising subnet routes (subnet-router capability) not present in the previous snapshot. Unexpected advertisement may indicate a compromised node expanding reachable surface area or an unsanctioned admin change." + "text": "Identifies tailnet devices whose machine key expires within the next 7 days and where key expiry is not disabled. Surface proactively so renewal can be scheduled rather than forced during an outage." } } ] @@ -275,13 +275,13 @@ { "name": "analytic8", "type": "Microsoft.Common.Section", - "label": "Tailscale: User role elevated to admin or owner", + "label": "Tailscale: Device started advertising subnet routes", "elements": [ { "name": "analytic8-text", "type": "Microsoft.Common.TextBlock", "options": { - "text": "Identifies when a user's tailnet role changes from a lower-privilege role to admin, network-admin, or owner between consecutive snapshots. Privilege escalation is a high-value attacker objective and warrants prompt review." + "text": "Identifies when a tailnet device begins advertising subnet routes (subnet-router capability) not present in the previous snapshot. Unexpected advertisement may indicate a compromised node expanding reachable surface area or an unsanctioned admin change." } } ] @@ -289,13 +289,13 @@ { "name": "analytic9", "type": "Microsoft.Common.Section", - "label": "Tailscale: Split-DNS configuration modified", + "label": "Tailscale: User role elevated to admin or owner", "elements": [ { "name": "analytic9-text", "type": "Microsoft.Common.TextBlock", "options": { - "text": "Identifies when the tailnet split-DNS configuration is modified. Split-DNS overrides per-domain resolution within the tailnet - an attacker adding a new domain mapping or changing the resolver IP can hijack DNS for that domain." + "text": "Identifies when a user's tailnet role changes from a lower-privilege role to admin, network-admin, or owner between consecutive snapshots. Privilege escalation is a high-value attacker objective and warrants prompt review." } } ] @@ -303,13 +303,13 @@ { "name": "analytic10", "type": "Microsoft.Common.Section", - "label": "Tailscale: DNS nameservers modified", + "label": "Tailscale: Split-DNS configuration modified", "elements": [ { "name": "analytic10-text", "type": "Microsoft.Common.TextBlock", "options": { - "text": "Identifies when the tailnet's global DNS nameserver list is modified. Adding an attacker-controlled resolver as a tailnet-wide nameserver enables broad DNS hijacking for every device using MagicDNS resolution." + "text": "Identifies when the tailnet split-DNS configuration is modified. Split-DNS overrides per-domain resolution within the tailnet - an attacker adding a new domain mapping or changing the resolver IP can hijack DNS for that domain." } } ] @@ -317,13 +317,13 @@ { "name": "analytic11", "type": "Microsoft.Common.Section", - "label": "Tailscale: MagicDNS disabled", + "label": "Tailscale: DNS nameservers modified", "elements": [ { "name": "analytic11-text", "type": "Microsoft.Common.TextBlock", "options": { - "text": "Identifies when MagicDNS is turned off on the tailnet. Disabling MagicDNS changes DNS behaviour for every device and is occasionally a precursor to wider DNS hijacking - verify the change was intentional." + "text": "Identifies when the tailnet's global DNS nameserver list is modified. Adding an attacker-controlled resolver as a tailnet-wide nameserver enables broad DNS hijacking for every device using MagicDNS resolution." } } ] @@ -331,13 +331,13 @@ { "name": "analytic12", "type": "Microsoft.Common.Section", - "label": "Tailscale: Tailnet lock validation failed", + "label": "Tailscale: MagicDNS disabled", "elements": [ { "name": "analytic12-text", "type": "Microsoft.Common.TextBlock", "options": { - "text": "Identifies tailnet devices with a non-empty TailnetLockError, indicating the device failed tailnet-lock cryptographic validation. Tailnet lock requires new node-keys be co-signed by trusted signers; errors here are the only direct signal of a node-key injection attempt." + "text": "Identifies when MagicDNS is turned off on the tailnet. Disabling MagicDNS changes DNS behaviour for every device and is occasionally a precursor to wider DNS hijacking - verify the change was intentional." } } ] @@ -345,13 +345,13 @@ { "name": "analytic13", "type": "Microsoft.Common.Section", - "label": "Tailscale: Device Tailscale SSH newly enabled", + "label": "Tailscale: Tailnet lock validation failed", "elements": [ { "name": "analytic13-text", "type": "Microsoft.Common.TextBlock", "options": { - "text": "Identifies when Tailscale SSH is enabled on a device that previously did not have it. SSH provides authenticated shell access over the tailnet using Tailscale identity, broadening attack surface if unexpected. Verify and confirm the SSH ACL covers it." + "text": "Identifies tailnet devices with a non-empty TailnetLockError, indicating the device failed tailnet-lock cryptographic validation. Tailnet lock requires new node-keys be co-signed by trusted signers; errors here are the only direct signal of a node-key injection attempt." } } ] @@ -359,13 +359,13 @@ { "name": "analytic14", "type": "Microsoft.Common.Section", - "label": "Tailscale: Unauthorized device connected to control plane", + "label": "Tailscale: Device Tailscale SSH newly enabled", "elements": [ { "name": "analytic14-text", "type": "Microsoft.Common.TextBlock", "options": { - "text": "Identifies devices actively connected to the Tailscale control plane (ConnectedToControl=true) but not yet authorized by an admin (Authorized=false). With device approval enabled, persistent entries warrant review; without approval, persistence may indicate a node-key injection attempt." + "text": "Identifies when Tailscale SSH is enabled on a device that previously did not have it. SSH provides authenticated shell access over the tailnet using Tailscale identity, broadening attack surface if unexpected. Verify and confirm the SSH ACL covers it." } } ] @@ -373,13 +373,13 @@ { "name": "analytic15", "type": "Microsoft.Common.Section", - "label": "Tailscale: External (shared-in) device added", + "label": "Tailscale: Unauthorized device connected to control plane", "elements": [ { "name": "analytic15-text", "type": "Microsoft.Common.TextBlock", "options": { - "text": "Identifies new external (shared-in) devices joining the tailnet that were not present in the prior 24-hour baseline. Each shared-in device expands the trust boundary - confirm the share matches a documented agreement and ACL scope." + "text": "Identifies devices actively connected to the Tailscale control plane (ConnectedToControl=true) but not yet authorized by an admin (Authorized=false). With device approval enabled, persistent entries warrant review; without approval, persistence may indicate a node-key injection attempt." } } ] @@ -387,13 +387,13 @@ { "name": "analytic16", "type": "Microsoft.Common.Section", - "label": "Tailscale Premium: Unexpected exit-node egress", + "label": "Tailscale: External (shared-in) device added", "elements": [ { "name": "analytic16-text", "type": "Microsoft.Common.TextBlock", "options": { - "text": "Identifies when a node sends traffic via an exit node not used in the prior 7-day baseline. First-seen exit destinations from a specific source may indicate unsanctioned egress, a compromised node pivoting, or a policy misconfiguration. Requires Tailscale Premium or Enterprise." + "text": "Identifies new external (shared-in) devices joining the tailnet that were not present in the prior 24-hour baseline. Each shared-in device expands the trust boundary - confirm the share matches a documented agreement and ACL scope." } } ] @@ -401,13 +401,13 @@ { "name": "analytic17", "type": "Microsoft.Common.Section", - "label": "Tailscale Premium: Large outbound transfer over tailnet", + "label": "Tailscale Premium: Unexpected exit-node egress", "elements": [ { "name": "analytic17-text", "type": "Microsoft.Common.TextBlock", "options": { - "text": "Identifies when a single src-dst pair transfers more than 100 MB over the tailnet within a 1-hour window. Large bursts can indicate data staging, exfiltration, or a misconfigured backup. Requires Tailscale Premium or Enterprise." + "text": "Identifies when a node sends traffic via an exit node not used in the prior 7-day baseline. First-seen exit destinations from a specific source may indicate unsanctioned egress, a compromised node pivoting, or a policy misconfiguration. Requires Tailscale Premium or Enterprise." } } ] @@ -415,13 +415,13 @@ { "name": "analytic18", "type": "Microsoft.Common.Section", - "label": "Tailscale Premium: Network flow beaconing detected", + "label": "Tailscale Premium: Large outbound transfer over tailnet", "elements": [ { "name": "analytic18-text", "type": "Microsoft.Common.TextBlock", "options": { - "text": "Identifies when flows between a src-dst pair recur at a regular interval (80%+ of inter-flow gaps cluster on the same delta over 10+ flows). Signature of C2 beaconing or scheduled exfiltration. Requires Tailscale Premium or Enterprise." + "text": "Identifies when a single src-dst pair transfers more than 100 MB over the tailnet within a 1-hour window. Large bursts can indicate data staging, exfiltration, or a misconfigured backup. Requires Tailscale Premium or Enterprise." } } ] @@ -429,13 +429,13 @@ { "name": "analytic19", "type": "Microsoft.Common.Section", - "label": "Tailscale Premium: Mass fan-out from single node", + "label": "Tailscale Premium: Network flow beaconing detected", "elements": [ { "name": "analytic19-text", "type": "Microsoft.Common.TextBlock", "options": { - "text": "Identifies when a single node initiates flows to 25 or more unique destinations within a 15-minute window. Sudden fan-out is consistent with port scanning, lateral discovery, or worm-style propagation. Requires Tailscale Premium or Enterprise." + "text": "Identifies when flows between a src-dst pair recur at a regular interval (80%+ of inter-flow gaps cluster on the same delta over 10+ flows). Signature of C2 beaconing or scheduled exfiltration. Requires Tailscale Premium or Enterprise." } } ] @@ -443,13 +443,13 @@ { "name": "analytic20", "type": "Microsoft.Common.Section", - "label": "Tailscale Premium: Subnet router throughput anomaly", + "label": "Tailscale Premium: Mass fan-out from single node", "elements": [ { "name": "analytic20-text", "type": "Microsoft.Common.TextBlock", "options": { - "text": "Identifies when a subnet router (gateway node bridging the tailnet to an on-prem or cloud subnet) handles 3x or more its 7-day baseline traffic in the last hour. Spikes can indicate exfiltration or scanning. Requires Tailscale Premium or Enterprise." + "text": "Identifies when a single node initiates flows to 25 or more unique destinations within a 15-minute window. Sudden fan-out is consistent with port scanning, lateral discovery, or worm-style propagation. Requires Tailscale Premium or Enterprise." } } ] @@ -457,13 +457,13 @@ { "name": "analytic21", "type": "Microsoft.Common.Section", - "label": "Tailscale Premium: Posture integration disabled or removed", + "label": "Tailscale Premium: Subnet router throughput anomaly", "elements": [ { "name": "analytic21-text", "type": "Microsoft.Common.TextBlock", "options": { - "text": "Identifies when a device-posture integration is disabled or removed from the tailnet. Posture integrations enforce device compliance; removing one disables that enforcement and increases blast radius for compromised endpoints. Requires Tailscale Premium or Enterprise." + "text": "Identifies when a subnet router (gateway node bridging the tailnet to an on-prem or cloud subnet) handles 3x or more its 7-day baseline traffic in the last hour. Spikes can indicate exfiltration or scanning. Requires Tailscale Premium or Enterprise." } } ] @@ -471,16 +471,44 @@ { "name": "analytic22", "type": "Microsoft.Common.Section", - "label": "Tailscale Premium: New posture integration added", + "label": "Tailscale Premium: Posture integration disabled or removed", "elements": [ { "name": "analytic22-text", "type": "Microsoft.Common.TextBlock", + "options": { + "text": "Identifies when a device-posture integration is disabled or removed from the tailnet. Posture integrations enforce device compliance; removing one disables that enforcement and increases blast radius for compromised endpoints. Requires Tailscale Premium or Enterprise." + } + } + ] + }, + { + "name": "analytic23", + "type": "Microsoft.Common.Section", + "label": "Tailscale Premium: New posture integration added", + "elements": [ + { + "name": "analytic23-text", + "type": "Microsoft.Common.TextBlock", "options": { "text": "Identifies when a new device-posture integration is added to the tailnet (Jamf, Kandji, Intune, Kolide, Defender for Endpoint, CrowdStrike, SentinelOne, etc.). Unexpected additions may indicate an attacker establishing a control plane or bypassing compliance gates. Requires Tailscale Premium or Enterprise." } } ] + }, + { + "name": "analytic24", + "type": "Microsoft.Common.Section", + "label": "Tailscale Premium: DERP relay traffic surge", + "elements": [ + { + "name": "analytic24-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "Identifies when a source node has more than 75 percent of its recent flows falling back to a DERP relay (Tailscale's IsRelayed flag, traffic via 127.3.3.40). Sustained high relay rate indicates direct WireGuard peer-to-peer is failing - causes include NAT/firewall changes, a network blocking UDP 41641, or potential evasion attempts. Operational signal but useful for spotting policy drift. Requires Tailscale Premium or Enterprise." + } + } + ] } ] }, @@ -515,7 +543,7 @@ "name": "huntingquery1-text", "type": "Microsoft.Common.TextBlock", "options": { - "text": "Surfaces actors making their first configuration change in the lookback window. New legitimate admins look identical to compromised credentials - review whether each surfaced actor is expected to have admin rights. This hunting query depends on TailscaleCCF data connector (Tailscale_Audit_CL Parser or Table)" + "text": "Identifies actors making their first configuration change in the lookback window. New legitimate admins look identical to compromised credentials - review whether each surfaced actor is expected to have admin rights. This hunting query depends on TailscaleCCF data connector (Tailscale_Audit_CL Parser or Table)" } } ] @@ -543,7 +571,7 @@ "name": "huntingquery3-text", "type": "Microsoft.Common.TextBlock", "options": { - "text": "Lists configuration audit events that occurred outside typical business hours (defined as Monday-Friday 08:00-18:00 UTC). Adjust the time window to match your operating region. Useful for spotting impromptu maintenance, account compromise, or insider activity. This hunting query depends on TailscaleCCF data connector (Tailscale_Audit_CL Parser or Table)" + "text": "Identifies configuration audit events that occurred outside typical business hours (defined as Monday-Friday 08:00-18:00 UTC). Adjust the time window to match your operating region. Useful for spotting impromptu maintenance, account compromise, or insider activity. This hunting query depends on TailscaleCCF data connector (Tailscale_Audit_CL Parser or Table)" } } ] @@ -557,7 +585,7 @@ "name": "huntingquery4-text", "type": "Microsoft.Common.TextBlock", "options": { - "text": "Actors creating multiple auth keys in a short window. A single admin creating many keys for unattended enrollment is normal during a rollout; the same pattern can also indicate token-spraying for persistence. Cross-reference with the Tailscale: Auth key created alert to filter context. This hunting query depends on TailscaleCCF data connector (Tailscale_Audit_CL Parser or Table)" + "text": "Identifies actors creating multiple auth keys in a short window. A single admin creating many keys for unattended enrollment is normal during a rollout; the same pattern can also indicate token-spraying for persistence. Cross-reference with the Tailscale: Auth key created alert to filter context. This hunting query depends on TailscaleCCF data connector (Tailscale_Audit_CL Parser or Table)" } } ] @@ -571,7 +599,7 @@ "name": "huntingquery5-text", "type": "Microsoft.Common.TextBlock", "options": { - "text": "Lists tailnet devices that have not connected to the control plane for at least 30 days. Dormant devices accumulate risk - they may still have valid keys, advertised routes, or tags but no operational oversight. Candidate for removal or key rotation. This hunting query depends on TailscaleCCF data connector (Tailscale_Devices_CL Parser or Table)" + "text": "Identifies tailnet devices that have not connected to the control plane for at least 30 days. Dormant devices accumulate risk - they may still have valid keys, advertised routes, or tags but no operational oversight. Candidate for removal or key rotation. This hunting query depends on TailscaleCCF data connector (Tailscale_Devices_CL Parser or Table)" } } ] @@ -599,7 +627,7 @@ "name": "huntingquery7-text", "type": "Microsoft.Common.TextBlock", "options": { - "text": "Lists tailnet users who have no devices currently registered. Orphaned identities are candidates for off-boarding - they retain whatever role/permissions they were granted and can still create auth keys or invite others. Review against your HR / identity-provider join-leaver records. This hunting query depends on TailscaleCCF data connector (Tailscale_Users_CL Parser or Table)" + "text": "Identifies tailnet users who have no devices currently registered. Orphaned identities are candidates for off-boarding - they retain whatever role/permissions they were granted and can still create auth keys or invite others. Review against your HR / identity-provider join-leaver records. This hunting query depends on TailscaleCCF data connector (Tailscale_Users_CL Parser or Table)" } } ] @@ -627,7 +655,7 @@ "name": "huntingquery9-text", "type": "Microsoft.Common.TextBlock", "options": { - "text": "Inventory of devices that currently have Tailscale SSH enabled. Tailscale SSH delivers SSH access over the tailnet using Tailscale identity (no SSH keys needed) and is governed by the SSH ACL block in the policy file. Cross-reference this list with the SSH ACL to confirm only the intended devices are reachable as SSH targets. This hunting query depends on TailscaleCCF data connector (Tailscale_Devices_CL Parser or Table)" + "text": "Identifies devices that currently have Tailscale SSH enabled. Tailscale SSH delivers SSH access over the tailnet using Tailscale identity (no SSH keys needed) and is governed by the SSH ACL block in the policy file. Cross-reference this list with the SSH ACL to confirm only the intended devices are reachable as SSH targets. This hunting query depends on TailscaleCCF data connector (Tailscale_Devices_CL Parser or Table)" } } ] @@ -641,7 +669,7 @@ "name": "huntingquery10-text", "type": "Microsoft.Common.TextBlock", "options": { - "text": "Inventory of external (shared-in) devices currently active in the tailnet. These devices belong to another tailnet and have been admitted via a Tailscale sharing arrangement. Confirm each entry maps to a documented collaboration and that the corresponding ACL restricts the device to the intended resources. This hunting query depends on TailscaleCCF data connector (Tailscale_Devices_CL Parser or Table)" + "text": "Identifies external (shared-in) devices currently active in the tailnet. These devices belong to another tailnet and have been admitted via a Tailscale sharing arrangement. Confirm each entry maps to a documented collaboration and that the corresponding ACL restricts the device to the intended resources. This hunting query depends on TailscaleCCF data connector (Tailscale_Devices_CL Parser or Table)" } } ] @@ -655,7 +683,7 @@ "name": "huntingquery11-text", "type": "Microsoft.Common.TextBlock", "options": { - "text": "Lists tailnet devices that report UpdateAvailable=true on the latest snapshot. Tailscale releases security and feature updates regularly; a device showing UpdateAvailable lacks the most recent improvements. Use this list to plan a fleet update, especially before enabling new ACL features that require minimum client versions. This hunting query depends on TailscaleCCF data connector (Tailscale_Devices_CL Parser or Table)" + "text": "Identifies tailnet devices that report UpdateAvailable=true on the latest snapshot. Tailscale releases security and feature updates regularly; a device showing UpdateAvailable lacks the most recent improvements. Use this list to plan a fleet update, especially before enabling new ACL features that require minimum client versions. This hunting query depends on TailscaleCCF data connector (Tailscale_Devices_CL Parser or Table)" } } ] @@ -683,7 +711,7 @@ "name": "huntingquery13-text", "type": "Microsoft.Common.TextBlock", "options": { - "text": "Lists tailnet src->dst pairs observed in the last 24h that were NOT observed in the prior 7-day baseline. Useful for spotting lateral movement to nodes that don't usually talk to each other. Requires Tailscale Premium or Enterprise (network flow logs). This hunting query depends on TailscalePremiumCCF data connector (Tailscale_Network_CL Parser or Table)" + "text": "Identifies tailnet src->dst pairs observed in the last 24h that were NOT observed in the prior 7-day baseline. Useful for spotting lateral movement to nodes that don't usually talk to each other. Requires Tailscale Premium or Enterprise (network flow logs). This hunting query depends on TailscalePremiumCCF data connector (Tailscale_Network_CL Parser or Table)" } } ] @@ -697,7 +725,7 @@ "name": "huntingquery14-text", "type": "Microsoft.Common.TextBlock", "options": { - "text": "Ranks tailnet src->dst pairs by total bytes transferred over the last 24h. Useful for capacity planning, identifying data-heavy flows, and spotting unexpected volume that could indicate data staging. Requires Tailscale Premium or Enterprise. This hunting query depends on TailscalePremiumCCF data connector (Tailscale_Network_CL Parser or Table)" + "text": "Identifies tailnet src->dst pairs ranked by total bytes transferred over the last 24h. Useful for capacity planning, identifying data-heavy flows, and spotting unexpected volume that could indicate data staging. Requires Tailscale Premium or Enterprise. This hunting query depends on TailscalePremiumCCF data connector (Tailscale_Network_CL Parser or Table)" } } ] @@ -711,7 +739,7 @@ "name": "huntingquery15-text", "type": "Microsoft.Common.TextBlock", "options": { - "text": "Summarises traffic leaving the tailnet via exit nodes. Exit node use is typically intentional (regional egress, privacy routing) but unexpected exit-node traffic from a node should be investigated as a potential pivot point or unsanctioned egress. Requires Tailscale Premium or Enterprise. This hunting query depends on TailscalePremiumCCF data connector (Tailscale_Network_CL Parser or Table)" + "text": "Identifies traffic leaving the tailnet via exit nodes. Exit node use is typically intentional (regional egress, privacy routing) but unexpected exit-node traffic from a node should be investigated as a potential pivot point or unsanctioned egress. Requires Tailscale Premium or Enterprise. This hunting query depends on TailscalePremiumCCF data connector (Tailscale_Network_CL Parser or Table)" } } ] @@ -739,7 +767,77 @@ "name": "huntingquery17-text", "type": "Microsoft.Common.TextBlock", "options": { - "text": "Lists the current set of device-posture integrations configured on the tailnet (latest snapshot per integration). Useful for compliance attestation - confirms what MDM/EDR systems are connected and what their status is. Compare against the expected baseline to detect drift. Requires Tailscale Premium or Enterprise. This hunting query depends on TailscalePremiumCCF data connector (Tailscale_PostureIntegrations_CL Parser or Table)" + "text": "Identifies the current set of device-posture integrations configured on the tailnet (latest snapshot per integration). Useful for compliance attestation - confirms what MDM/EDR systems are connected and what their status is. Compare against the expected baseline to detect drift. Requires Tailscale Premium or Enterprise. This hunting query depends on TailscalePremiumCCF data connector (Tailscale_PostureIntegrations_CL Parser or Table)" + } + } + ] + }, + { + "name": "huntingquery18", + "type": "Microsoft.Common.Section", + "label": "Tailscale Premium: Devices with persistent DERP relay usage", + "elements": [ + { + "name": "huntingquery18-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "Identifies devices that have consistently fallen back to DERP relay (IsRelayed = true) over the past 24 hours. Sustained relay usage points to NAT/firewall misconfiguration on the device's network, a tunnel-blocking middlebox, or in rare cases deliberate evasion attempting to obscure direct peer-to-peer paths. Useful for proactive network-hygiene investigation and capacity planning. Requires Tailscale Premium or Enterprise. This hunting query depends on TailscalePremiumCCF data connector (Tailscale_Network_CL Parser or Table)" + } + } + ] + }, + { + "name": "huntingquery19", + "type": "Microsoft.Common.Section", + "label": "Tailscale Premium: Tagged services with broad inbound exposure", + "elements": [ + { + "name": "huntingquery19-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "Identifies tagged services (devices with non-empty DstTags) ranked by inbound diversity over 7 days. Baseline: a tag:plex serving the household typically sees connections from 1-3 user devices; a tag:db or tag:internal that receives connections from 10+ distinct users or 3+ OS families may indicate ACL drift, credential sharing, or unauthorised access. Sort by DistinctSrcDevices to surface services with the broadest blast-radius. Requires Tailscale Premium or Enterprise. This hunting query depends on TailscalePremiumCCF data connector (Tailscale_Network_CL Parser or Table)" + } + } + ] + }, + { + "name": "huntingquery20", + "type": "Microsoft.Common.Section", + "label": "Tailscale Premium: Cross-tag flow matrix", + "elements": [ + { + "name": "huntingquery20-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "Identifies network flows pivoted by source-tag x destination-tag over the past 7 days, treating untagged user devices as ``. Highlights which tagged services interact - useful for ACL validation, detecting unexpected tag-to-tag traffic, and spotting tag-to-same-tag loops that can indicate worm-style propagation or service-mesh anomalies. Order by Flows to find the heaviest tag pairs; sort by DistinctSrcDevices to find broadly-used services. Requires Tailscale Premium or Enterprise. This hunting query depends on TailscalePremiumCCF data connector (Tailscale_Network_CL Parser or Table)" + } + } + ] + }, + { + "name": "huntingquery21", + "type": "Microsoft.Common.Section", + "label": "Tailscale Premium: Network flows outside business hours", + "elements": [ + { + "name": "huntingquery21-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "Identifies network flows occurring outside 07:00-19:00 UTC on weekdays, plus all of weekend, over the past 7 days. Filters to virtual/subnet/exit traffic (drops DERP-only keepalive noise). Useful for spotting unattended automation gone wrong, scheduled exfiltration, or unsanctioned after-hours access by humans. The TaggedSource column makes it easy to separate cron-like service traffic (tag:backup, tag:cron) from human user activity. Requires Tailscale Premium or Enterprise. This hunting query depends on TailscalePremiumCCF data connector (Tailscale_Network_CL Parser or Table)" + } + } + ] + }, + { + "name": "huntingquery22", + "type": "Microsoft.Common.Section", + "label": "Tailscale Premium: Users generating traffic from multiple devices", + "elements": [ + { + "name": "huntingquery22-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "Identifies users (SrcUser) generating tailnet flows from more than one distinct device in the past 24 hours. Normal for a user with phone + laptop. Useful for spotting account compromise (sudden new device for a user), unauthorised device enrollment, or device sharing across users. Cross-reference NewDevicesToday against Tailscale_Devices_CL.Created to confirm whether each device is genuinely new vs long-known. Requires Tailscale Premium or Enterprise. This hunting query depends on TailscalePremiumCCF data connector (Tailscale_Network_CL Tailscale_Devices_CL Parser or Table)" } } ] diff --git a/Solutions/Tailscale (CCF)/Package/mainTemplate.json b/Solutions/Tailscale (CCF)/Package/mainTemplate.json index 5bcdc249d94..c653f22c2b6 100644 --- a/Solutions/Tailscale (CCF)/Package/mainTemplate.json +++ b/Solutions/Tailscale (CCF)/Package/mainTemplate.json @@ -28,20 +28,6 @@ "description": "Workspace name for Log Analytics where Microsoft Sentinel is setup" } }, - "resourceGroupName": { - "type": "string", - "defaultValue": "[resourceGroup().name]", - "metadata": { - "description": "resource group name where Microsoft Sentinel is setup" - } - }, - "subscription": { - "type": "string", - "defaultValue": "[last(split(subscription().id, '/'))]", - "metadata": { - "description": "subscription id where Microsoft Sentinel is setup" - } - }, "workbook1-name": { "type": "string", "defaultValue": "Tailscale Operations (Standard)", @@ -63,21 +49,14 @@ "email": "ccfconnectors.county118@passmail.com", "_email": "[variables('email')]", "_solutionName": "Tailscale (CCF)", - "_solutionVersion": "3.0.5", "solutionId": "noodlemctwoodle.azure-sentinel-solution-tailscale-ccf", "_solutionId": "[variables('solutionId')]", "workspaceResourceId": "[resourceId('microsoft.OperationalInsights/Workspaces', parameters('workspace'))]", - "dataConnectorCCPVersion": "3.0.5", + "dataConnectorCCPVersion": "3.0.0", "_dataConnectorContentIdConnectorDefinition1": "TailscaleCCF", - "dataConnectorTemplateNameConnectorDefinition1": "[concat(parameters('workspace'),'-dc-',uniquestring(variables('_dataConnectorContentIdConnectorDefinition1')))]", "_dataConnectorContentIdConnections1": "TailscaleCCFConnections", - "dataConnectorTemplateNameConnections1": "[concat(parameters('workspace'),'-dc-',uniquestring(variables('_dataConnectorContentIdConnections1')))]", - "dataCollectionEndpointId1": "[concat('/subscriptions/',parameters('subscription'),'/resourceGroups/',parameters('resourceGroupName'),'/providers/Microsoft.Insights/dataCollectionEndpoints/',parameters('workspace'))]", "_dataConnectorContentIdConnectorDefinition2": "TailscalePremiumCCF", - "dataConnectorTemplateNameConnectorDefinition2": "[concat(parameters('workspace'),'-dc-',uniquestring(variables('_dataConnectorContentIdConnectorDefinition2')))]", "_dataConnectorContentIdConnections2": "TailscalePremiumCCFConnections", - "dataConnectorTemplateNameConnections2": "[concat(parameters('workspace'),'-dc-',uniquestring(variables('_dataConnectorContentIdConnections2')))]", - "dataCollectionEndpointId2": "[concat('/subscriptions/',parameters('subscription'),'/resourceGroups/',parameters('resourceGroupName'),'/providers/Microsoft.Insights/dataCollectionEndpoints/',parameters('workspace'))]", "analyticRuleObject1": { "analyticRuleVersion1": "1.0.0", "_analyticRulecontentId1": "668b43fd-cf28-961a-85af-957850df5027", @@ -87,150 +66,164 @@ }, "analyticRuleObject2": { "analyticRuleVersion2": "1.0.0", - "_analyticRulecontentId2": "1e7249c2-1a9d-05fd-45cb-c859eef5b8ae", - "analyticRuleId2": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', '1e7249c2-1a9d-05fd-45cb-c859eef5b8ae')]", - "analyticRuleTemplateSpecName2": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('1e7249c2-1a9d-05fd-45cb-c859eef5b8ae')))]", - "_analyticRulecontentProductId2": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','1e7249c2-1a9d-05fd-45cb-c859eef5b8ae','-', '1.0.0')))]" + "_analyticRulecontentId2": "7237a848-30f2-499b-9ad5-024aea1288bd", + "analyticRuleId2": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', '7237a848-30f2-499b-9ad5-024aea1288bd')]", + "analyticRuleTemplateSpecName2": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('7237a848-30f2-499b-9ad5-024aea1288bd')))]", + "_analyticRulecontentProductId2": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','7237a848-30f2-499b-9ad5-024aea1288bd','-', '1.0.0')))]" }, "analyticRuleObject3": { "analyticRuleVersion3": "1.0.0", - "_analyticRulecontentId3": "6b052c8d-5de8-eab0-1956-69a297765a32", - "analyticRuleId3": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', '6b052c8d-5de8-eab0-1956-69a297765a32')]", - "analyticRuleTemplateSpecName3": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('6b052c8d-5de8-eab0-1956-69a297765a32')))]", - "_analyticRulecontentProductId3": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','6b052c8d-5de8-eab0-1956-69a297765a32','-', '1.0.0')))]" + "_analyticRulecontentId3": "1e7249c2-1a9d-05fd-45cb-c859eef5b8ae", + "analyticRuleId3": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', '1e7249c2-1a9d-05fd-45cb-c859eef5b8ae')]", + "analyticRuleTemplateSpecName3": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('1e7249c2-1a9d-05fd-45cb-c859eef5b8ae')))]", + "_analyticRulecontentProductId3": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','1e7249c2-1a9d-05fd-45cb-c859eef5b8ae','-', '1.0.0')))]" }, "analyticRuleObject4": { "analyticRuleVersion4": "1.0.0", - "_analyticRulecontentId4": "f42f2906-c8e6-23d0-e48c-0620e50d5510", - "analyticRuleId4": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', 'f42f2906-c8e6-23d0-e48c-0620e50d5510')]", - "analyticRuleTemplateSpecName4": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('f42f2906-c8e6-23d0-e48c-0620e50d5510')))]", - "_analyticRulecontentProductId4": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','f42f2906-c8e6-23d0-e48c-0620e50d5510','-', '1.0.0')))]" + "_analyticRulecontentId4": "6b052c8d-5de8-eab0-1956-69a297765a32", + "analyticRuleId4": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', '6b052c8d-5de8-eab0-1956-69a297765a32')]", + "analyticRuleTemplateSpecName4": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('6b052c8d-5de8-eab0-1956-69a297765a32')))]", + "_analyticRulecontentProductId4": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','6b052c8d-5de8-eab0-1956-69a297765a32','-', '1.0.0')))]" }, "analyticRuleObject5": { "analyticRuleVersion5": "1.0.0", - "_analyticRulecontentId5": "f817e2fa-6fa0-fc25-5369-cef9b58771af", - "analyticRuleId5": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', 'f817e2fa-6fa0-fc25-5369-cef9b58771af')]", - "analyticRuleTemplateSpecName5": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('f817e2fa-6fa0-fc25-5369-cef9b58771af')))]", - "_analyticRulecontentProductId5": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','f817e2fa-6fa0-fc25-5369-cef9b58771af','-', '1.0.0')))]" + "_analyticRulecontentId5": "f42f2906-c8e6-23d0-e48c-0620e50d5510", + "analyticRuleId5": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', 'f42f2906-c8e6-23d0-e48c-0620e50d5510')]", + "analyticRuleTemplateSpecName5": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('f42f2906-c8e6-23d0-e48c-0620e50d5510')))]", + "_analyticRulecontentProductId5": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','f42f2906-c8e6-23d0-e48c-0620e50d5510','-', '1.0.0')))]" }, "analyticRuleObject6": { "analyticRuleVersion6": "1.0.0", - "_analyticRulecontentId6": "b1a2c3d4-1234-5678-90ab-cdef12345001", - "analyticRuleId6": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', 'b1a2c3d4-1234-5678-90ab-cdef12345001')]", - "analyticRuleTemplateSpecName6": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('b1a2c3d4-1234-5678-90ab-cdef12345001')))]", - "_analyticRulecontentProductId6": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','b1a2c3d4-1234-5678-90ab-cdef12345001','-', '1.0.0')))]" + "_analyticRulecontentId6": "f817e2fa-6fa0-fc25-5369-cef9b58771af", + "analyticRuleId6": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', 'f817e2fa-6fa0-fc25-5369-cef9b58771af')]", + "analyticRuleTemplateSpecName6": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('f817e2fa-6fa0-fc25-5369-cef9b58771af')))]", + "_analyticRulecontentProductId6": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','f817e2fa-6fa0-fc25-5369-cef9b58771af','-', '1.0.0')))]" }, "analyticRuleObject7": { "analyticRuleVersion7": "1.0.0", - "_analyticRulecontentId7": "c2b3d4e5-2345-6789-01ab-cdef12345002", - "analyticRuleId7": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', 'c2b3d4e5-2345-6789-01ab-cdef12345002')]", - "analyticRuleTemplateSpecName7": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('c2b3d4e5-2345-6789-01ab-cdef12345002')))]", - "_analyticRulecontentProductId7": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','c2b3d4e5-2345-6789-01ab-cdef12345002','-', '1.0.0')))]" + "_analyticRulecontentId7": "b1a2c3d4-1234-5678-90ab-cdef12345001", + "analyticRuleId7": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', 'b1a2c3d4-1234-5678-90ab-cdef12345001')]", + "analyticRuleTemplateSpecName7": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('b1a2c3d4-1234-5678-90ab-cdef12345001')))]", + "_analyticRulecontentProductId7": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','b1a2c3d4-1234-5678-90ab-cdef12345001','-', '1.0.0')))]" }, "analyticRuleObject8": { "analyticRuleVersion8": "1.0.0", - "_analyticRulecontentId8": "d3c4e5f6-3456-7890-12ab-cdef12345003", - "analyticRuleId8": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', 'd3c4e5f6-3456-7890-12ab-cdef12345003')]", - "analyticRuleTemplateSpecName8": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('d3c4e5f6-3456-7890-12ab-cdef12345003')))]", - "_analyticRulecontentProductId8": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','d3c4e5f6-3456-7890-12ab-cdef12345003','-', '1.0.0')))]" + "_analyticRulecontentId8": "c2b3d4e5-2345-6789-01ab-cdef12345002", + "analyticRuleId8": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', 'c2b3d4e5-2345-6789-01ab-cdef12345002')]", + "analyticRuleTemplateSpecName8": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('c2b3d4e5-2345-6789-01ab-cdef12345002')))]", + "_analyticRulecontentProductId8": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','c2b3d4e5-2345-6789-01ab-cdef12345002','-', '1.0.0')))]" }, "analyticRuleObject9": { "analyticRuleVersion9": "1.0.0", - "_analyticRulecontentId9": "b4c5d6e7-1234-5678-90ab-cdef12345010", - "analyticRuleId9": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', 'b4c5d6e7-1234-5678-90ab-cdef12345010')]", - "analyticRuleTemplateSpecName9": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('b4c5d6e7-1234-5678-90ab-cdef12345010')))]", - "_analyticRulecontentProductId9": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','b4c5d6e7-1234-5678-90ab-cdef12345010','-', '1.0.0')))]" + "_analyticRulecontentId9": "d3c4e5f6-3456-7890-12ab-cdef12345003", + "analyticRuleId9": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', 'd3c4e5f6-3456-7890-12ab-cdef12345003')]", + "analyticRuleTemplateSpecName9": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('d3c4e5f6-3456-7890-12ab-cdef12345003')))]", + "_analyticRulecontentProductId9": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','d3c4e5f6-3456-7890-12ab-cdef12345003','-', '1.0.0')))]" }, "analyticRuleObject10": { "analyticRuleVersion10": "1.0.0", - "_analyticRulecontentId10": "c5d6e7f8-2345-6789-01ab-cdef12345011", - "analyticRuleId10": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', 'c5d6e7f8-2345-6789-01ab-cdef12345011')]", - "analyticRuleTemplateSpecName10": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('c5d6e7f8-2345-6789-01ab-cdef12345011')))]", - "_analyticRulecontentProductId10": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','c5d6e7f8-2345-6789-01ab-cdef12345011','-', '1.0.0')))]" + "_analyticRulecontentId10": "b4c5d6e7-1234-5678-90ab-cdef12345010", + "analyticRuleId10": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', 'b4c5d6e7-1234-5678-90ab-cdef12345010')]", + "analyticRuleTemplateSpecName10": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('b4c5d6e7-1234-5678-90ab-cdef12345010')))]", + "_analyticRulecontentProductId10": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','b4c5d6e7-1234-5678-90ab-cdef12345010','-', '1.0.0')))]" }, "analyticRuleObject11": { "analyticRuleVersion11": "1.0.0", - "_analyticRulecontentId11": "f8a9b0c1-4567-8901-23ab-cdef12345020", - "analyticRuleId11": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', 'f8a9b0c1-4567-8901-23ab-cdef12345020')]", - "analyticRuleTemplateSpecName11": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('f8a9b0c1-4567-8901-23ab-cdef12345020')))]", - "_analyticRulecontentProductId11": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','f8a9b0c1-4567-8901-23ab-cdef12345020','-', '1.0.0')))]" + "_analyticRulecontentId11": "c5d6e7f8-2345-6789-01ab-cdef12345011", + "analyticRuleId11": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', 'c5d6e7f8-2345-6789-01ab-cdef12345011')]", + "analyticRuleTemplateSpecName11": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('c5d6e7f8-2345-6789-01ab-cdef12345011')))]", + "_analyticRulecontentProductId11": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','c5d6e7f8-2345-6789-01ab-cdef12345011','-', '1.0.0')))]" }, "analyticRuleObject12": { "analyticRuleVersion12": "1.0.0", - "_analyticRulecontentId12": "e9f0a1b2-3456-7890-12cd-ef1234560040", - "analyticRuleId12": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', 'e9f0a1b2-3456-7890-12cd-ef1234560040')]", - "analyticRuleTemplateSpecName12": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('e9f0a1b2-3456-7890-12cd-ef1234560040')))]", - "_analyticRulecontentProductId12": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','e9f0a1b2-3456-7890-12cd-ef1234560040','-', '1.0.0')))]" + "_analyticRulecontentId12": "f8a9b0c1-4567-8901-23ab-cdef12345020", + "analyticRuleId12": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', 'f8a9b0c1-4567-8901-23ab-cdef12345020')]", + "analyticRuleTemplateSpecName12": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('f8a9b0c1-4567-8901-23ab-cdef12345020')))]", + "_analyticRulecontentProductId12": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','f8a9b0c1-4567-8901-23ab-cdef12345020','-', '1.0.0')))]" }, "analyticRuleObject13": { "analyticRuleVersion13": "1.0.0", - "_analyticRulecontentId13": "f0a1b2c3-4567-8901-23de-f12345670041", - "analyticRuleId13": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', 'f0a1b2c3-4567-8901-23de-f12345670041')]", - "analyticRuleTemplateSpecName13": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('f0a1b2c3-4567-8901-23de-f12345670041')))]", - "_analyticRulecontentProductId13": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','f0a1b2c3-4567-8901-23de-f12345670041','-', '1.0.0')))]" + "_analyticRulecontentId13": "e9f0a1b2-3456-7890-12cd-ef1234560040", + "analyticRuleId13": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', 'e9f0a1b2-3456-7890-12cd-ef1234560040')]", + "analyticRuleTemplateSpecName13": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('e9f0a1b2-3456-7890-12cd-ef1234560040')))]", + "_analyticRulecontentProductId13": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','e9f0a1b2-3456-7890-12cd-ef1234560040','-', '1.0.0')))]" }, "analyticRuleObject14": { "analyticRuleVersion14": "1.0.0", - "_analyticRulecontentId14": "a1b2c3d4-5678-9012-3456-789012340042", - "analyticRuleId14": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', 'a1b2c3d4-5678-9012-3456-789012340042')]", - "analyticRuleTemplateSpecName14": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('a1b2c3d4-5678-9012-3456-789012340042')))]", - "_analyticRulecontentProductId14": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','a1b2c3d4-5678-9012-3456-789012340042','-', '1.0.0')))]" + "_analyticRulecontentId14": "f0a1b2c3-4567-8901-23de-f12345670041", + "analyticRuleId14": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', 'f0a1b2c3-4567-8901-23de-f12345670041')]", + "analyticRuleTemplateSpecName14": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('f0a1b2c3-4567-8901-23de-f12345670041')))]", + "_analyticRulecontentProductId14": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','f0a1b2c3-4567-8901-23de-f12345670041','-', '1.0.0')))]" }, "analyticRuleObject15": { "analyticRuleVersion15": "1.0.0", - "_analyticRulecontentId15": "b2c3d4e5-6789-0123-4567-890123450043", - "analyticRuleId15": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', 'b2c3d4e5-6789-0123-4567-890123450043')]", - "analyticRuleTemplateSpecName15": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('b2c3d4e5-6789-0123-4567-890123450043')))]", - "_analyticRulecontentProductId15": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','b2c3d4e5-6789-0123-4567-890123450043','-', '1.0.0')))]" + "_analyticRulecontentId15": "a1b2c3d4-5678-9012-3456-789012340042", + "analyticRuleId15": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', 'a1b2c3d4-5678-9012-3456-789012340042')]", + "analyticRuleTemplateSpecName15": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('a1b2c3d4-5678-9012-3456-789012340042')))]", + "_analyticRulecontentProductId15": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','a1b2c3d4-5678-9012-3456-789012340042','-', '1.0.0')))]" }, "analyticRuleObject16": { "analyticRuleVersion16": "1.0.0", - "_analyticRulecontentId16": "c1d2e3f4-1a2b-3c4d-5e6f-7a8b9c0d1e2f", - "analyticRuleId16": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', 'c1d2e3f4-1a2b-3c4d-5e6f-7a8b9c0d1e2f')]", - "analyticRuleTemplateSpecName16": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('c1d2e3f4-1a2b-3c4d-5e6f-7a8b9c0d1e2f')))]", - "_analyticRulecontentProductId16": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','c1d2e3f4-1a2b-3c4d-5e6f-7a8b9c0d1e2f','-', '1.0.0')))]" + "_analyticRulecontentId16": "b2c3d4e5-6789-0123-4567-890123450043", + "analyticRuleId16": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', 'b2c3d4e5-6789-0123-4567-890123450043')]", + "analyticRuleTemplateSpecName16": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('b2c3d4e5-6789-0123-4567-890123450043')))]", + "_analyticRulecontentProductId16": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','b2c3d4e5-6789-0123-4567-890123450043','-', '1.0.0')))]" }, "analyticRuleObject17": { "analyticRuleVersion17": "1.0.0", - "_analyticRulecontentId17": "d2e3f4a5-2b3c-4d5e-6f7a-8b9c0d1e2f3a", - "analyticRuleId17": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', 'd2e3f4a5-2b3c-4d5e-6f7a-8b9c0d1e2f3a')]", - "analyticRuleTemplateSpecName17": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('d2e3f4a5-2b3c-4d5e-6f7a-8b9c0d1e2f3a')))]", - "_analyticRulecontentProductId17": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','d2e3f4a5-2b3c-4d5e-6f7a-8b9c0d1e2f3a','-', '1.0.0')))]" + "_analyticRulecontentId17": "c1d2e3f4-1a2b-3c4d-5e6f-7a8b9c0d1e2f", + "analyticRuleId17": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', 'c1d2e3f4-1a2b-3c4d-5e6f-7a8b9c0d1e2f')]", + "analyticRuleTemplateSpecName17": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('c1d2e3f4-1a2b-3c4d-5e6f-7a8b9c0d1e2f')))]", + "_analyticRulecontentProductId17": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','c1d2e3f4-1a2b-3c4d-5e6f-7a8b9c0d1e2f','-', '1.0.0')))]" }, "analyticRuleObject18": { "analyticRuleVersion18": "1.0.0", - "_analyticRulecontentId18": "e3f4a5b6-3c4d-5e6f-7a8b-9c0d1e2f3a4b", - "analyticRuleId18": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', 'e3f4a5b6-3c4d-5e6f-7a8b-9c0d1e2f3a4b')]", - "analyticRuleTemplateSpecName18": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('e3f4a5b6-3c4d-5e6f-7a8b-9c0d1e2f3a4b')))]", - "_analyticRulecontentProductId18": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','e3f4a5b6-3c4d-5e6f-7a8b-9c0d1e2f3a4b','-', '1.0.0')))]" + "_analyticRulecontentId18": "d2e3f4a5-2b3c-4d5e-6f7a-8b9c0d1e2f3a", + "analyticRuleId18": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', 'd2e3f4a5-2b3c-4d5e-6f7a-8b9c0d1e2f3a')]", + "analyticRuleTemplateSpecName18": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('d2e3f4a5-2b3c-4d5e-6f7a-8b9c0d1e2f3a')))]", + "_analyticRulecontentProductId18": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','d2e3f4a5-2b3c-4d5e-6f7a-8b9c0d1e2f3a','-', '1.0.0')))]" }, "analyticRuleObject19": { "analyticRuleVersion19": "1.0.0", - "_analyticRulecontentId19": "f4a5b6c7-4d5e-6f7a-8b9c-0d1e2f3a4b5c", - "analyticRuleId19": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', 'f4a5b6c7-4d5e-6f7a-8b9c-0d1e2f3a4b5c')]", - "analyticRuleTemplateSpecName19": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('f4a5b6c7-4d5e-6f7a-8b9c-0d1e2f3a4b5c')))]", - "_analyticRulecontentProductId19": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','f4a5b6c7-4d5e-6f7a-8b9c-0d1e2f3a4b5c','-', '1.0.0')))]" + "_analyticRulecontentId19": "e3f4a5b6-3c4d-5e6f-7a8b-9c0d1e2f3a4b", + "analyticRuleId19": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', 'e3f4a5b6-3c4d-5e6f-7a8b-9c0d1e2f3a4b')]", + "analyticRuleTemplateSpecName19": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('e3f4a5b6-3c4d-5e6f-7a8b-9c0d1e2f3a4b')))]", + "_analyticRulecontentProductId19": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','e3f4a5b6-3c4d-5e6f-7a8b-9c0d1e2f3a4b','-', '1.0.0')))]" }, "analyticRuleObject20": { "analyticRuleVersion20": "1.0.0", - "_analyticRulecontentId20": "a5b6c7d8-5e6f-7a8b-9c0d-1e2f3a4b5c6d", - "analyticRuleId20": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', 'a5b6c7d8-5e6f-7a8b-9c0d-1e2f3a4b5c6d')]", - "analyticRuleTemplateSpecName20": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('a5b6c7d8-5e6f-7a8b-9c0d-1e2f3a4b5c6d')))]", - "_analyticRulecontentProductId20": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','a5b6c7d8-5e6f-7a8b-9c0d-1e2f3a4b5c6d','-', '1.0.0')))]" + "_analyticRulecontentId20": "f4a5b6c7-4d5e-6f7a-8b9c-0d1e2f3a4b5c", + "analyticRuleId20": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', 'f4a5b6c7-4d5e-6f7a-8b9c-0d1e2f3a4b5c')]", + "analyticRuleTemplateSpecName20": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('f4a5b6c7-4d5e-6f7a-8b9c-0d1e2f3a4b5c')))]", + "_analyticRulecontentProductId20": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','f4a5b6c7-4d5e-6f7a-8b9c-0d1e2f3a4b5c','-', '1.0.0')))]" }, "analyticRuleObject21": { "analyticRuleVersion21": "1.0.0", - "_analyticRulecontentId21": "a1b2c3d4-5678-9012-34ab-cdef12345030", - "analyticRuleId21": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', 'a1b2c3d4-5678-9012-34ab-cdef12345030')]", - "analyticRuleTemplateSpecName21": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('a1b2c3d4-5678-9012-34ab-cdef12345030')))]", - "_analyticRulecontentProductId21": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','a1b2c3d4-5678-9012-34ab-cdef12345030','-', '1.0.0')))]" + "_analyticRulecontentId21": "a5b6c7d8-5e6f-7a8b-9c0d-1e2f3a4b5c6d", + "analyticRuleId21": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', 'a5b6c7d8-5e6f-7a8b-9c0d-1e2f3a4b5c6d')]", + "analyticRuleTemplateSpecName21": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('a5b6c7d8-5e6f-7a8b-9c0d-1e2f3a4b5c6d')))]", + "_analyticRulecontentProductId21": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','a5b6c7d8-5e6f-7a8b-9c0d-1e2f3a4b5c6d','-', '1.0.0')))]" }, "analyticRuleObject22": { "analyticRuleVersion22": "1.0.0", - "_analyticRulecontentId22": "b2c3d4e5-6789-0123-45ab-cdef12345031", - "analyticRuleId22": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', 'b2c3d4e5-6789-0123-45ab-cdef12345031')]", - "analyticRuleTemplateSpecName22": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('b2c3d4e5-6789-0123-45ab-cdef12345031')))]", - "_analyticRulecontentProductId22": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','b2c3d4e5-6789-0123-45ab-cdef12345031','-', '1.0.0')))]" + "_analyticRulecontentId22": "a1b2c3d4-5678-9012-34ab-cdef12345030", + "analyticRuleId22": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', 'a1b2c3d4-5678-9012-34ab-cdef12345030')]", + "analyticRuleTemplateSpecName22": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('a1b2c3d4-5678-9012-34ab-cdef12345030')))]", + "_analyticRulecontentProductId22": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','a1b2c3d4-5678-9012-34ab-cdef12345030','-', '1.0.0')))]" + }, + "analyticRuleObject23": { + "analyticRuleVersion23": "1.0.0", + "_analyticRulecontentId23": "b2c3d4e5-6789-0123-45ab-cdef12345031", + "analyticRuleId23": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', 'b2c3d4e5-6789-0123-45ab-cdef12345031')]", + "analyticRuleTemplateSpecName23": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('b2c3d4e5-6789-0123-45ab-cdef12345031')))]", + "_analyticRulecontentProductId23": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','b2c3d4e5-6789-0123-45ab-cdef12345031','-', '1.0.0')))]" + }, + "analyticRuleObject24": { + "analyticRuleVersion24": "1.0.0", + "_analyticRulecontentId24": "0a1c8d12-e7d3-4890-8b89-8d6dbc1be2f0", + "analyticRuleId24": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', '0a1c8d12-e7d3-4890-8b89-8d6dbc1be2f0')]", + "analyticRuleTemplateSpecName24": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('0a1c8d12-e7d3-4890-8b89-8d6dbc1be2f0')))]", + "_analyticRulecontentProductId24": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','0a1c8d12-e7d3-4890-8b89-8d6dbc1be2f0','-', '1.0.0')))]" }, "huntingQueryObject1": { "huntingQueryVersion1": "1.0.0", @@ -317,6 +310,31 @@ "_huntingQuerycontentId17": "c3d4e5f6-7890-1234-56ab-cdef12345032", "huntingQueryTemplateSpecName17": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-hq-',uniquestring('c3d4e5f6-7890-1234-56ab-cdef12345032')))]" }, + "huntingQueryObject18": { + "huntingQueryVersion18": "1.0.0", + "_huntingQuerycontentId18": "20457fba-08e2-42d7-b972-fbe9acf583c8", + "huntingQueryTemplateSpecName18": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-hq-',uniquestring('20457fba-08e2-42d7-b972-fbe9acf583c8')))]" + }, + "huntingQueryObject19": { + "huntingQueryVersion19": "1.0.0", + "_huntingQuerycontentId19": "f8d4e7bc-3450-4c55-84ac-90e6e9c6b8fe", + "huntingQueryTemplateSpecName19": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-hq-',uniquestring('f8d4e7bc-3450-4c55-84ac-90e6e9c6b8fe')))]" + }, + "huntingQueryObject20": { + "huntingQueryVersion20": "1.0.0", + "_huntingQuerycontentId20": "a8978f27-3c85-4c29-a45a-c4a5e43fef2d", + "huntingQueryTemplateSpecName20": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-hq-',uniquestring('a8978f27-3c85-4c29-a45a-c4a5e43fef2d')))]" + }, + "huntingQueryObject21": { + "huntingQueryVersion21": "1.0.0", + "_huntingQuerycontentId21": "622ce88a-0838-4bbe-8a00-ab8ac8377f41", + "huntingQueryTemplateSpecName21": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-hq-',uniquestring('622ce88a-0838-4bbe-8a00-ab8ac8377f41')))]" + }, + "huntingQueryObject22": { + "huntingQueryVersion22": "1.0.0", + "_huntingQuerycontentId22": "daac10bd-d842-4122-90cc-9957256f04e3", + "huntingQueryTemplateSpecName22": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-hq-',uniquestring('daac10bd-d842-4122-90cc-9957256f04e3')))]" + }, "workbookVersion1": "1.0.0", "workbookContentId1": "TailscaleStandardOperationsWorkbook", "workbookId1": "[resourceId('Microsoft.Insights/workbooks', variables('workbookContentId1'))]", @@ -544,7 +562,7 @@ "email": "[variables('_email')]" }, "support": { - "name": "Community", + "name": "Tailscale (CCF)", "tier": "Community", "email": "ccfconnectors.county118@passmail.com", "link": "https://github.com/Azure/Azure-Sentinel/issues" @@ -579,6 +597,14 @@ "name": "eventGroupID", "type": "string" }, + { + "name": "type", + "type": "string" + }, + { + "name": "actionDetails", + "type": "string" + }, { "name": "actor", "type": "dynamic" @@ -914,7 +940,7 @@ "destinations": [ "sentinelWorkspace" ], - "transformKql": "source | extend TimeGenerated = eventTime | extend EventTime = eventTime | extend EventGroupID = eventGroupID | extend Actor = actor | extend Action = action | extend Target = target | extend Origin = origin | extend New = new | extend Old = old | project TimeGenerated, EventTime, EventGroupID, Actor, Action, Target, Origin, New, Old", + "transformKql": "source | extend TimeGenerated = eventTime | extend EventTime = eventTime | extend EventGroupID = eventGroupID | extend EventType = type | extend ActionDetails = actionDetails | extend Actor = actor | extend Action = action | extend Target = target | extend Origin = origin | extend New = new | extend Old = old | project TimeGenerated, EventTime, EventGroupID, EventType, ActionDetails, Actor, Action, Target, Origin, New, Old", "outputStream": "Custom-Tailscale_Audit_CL" }, { @@ -1022,6 +1048,14 @@ "name": "EventGroupID", "type": "string" }, + { + "name": "EventType", + "type": "string" + }, + { + "name": "ActionDetails", + "type": "string" + }, { "name": "Actor", "type": "dynamic" @@ -1459,10 +1493,58 @@ "name": "SrcNode", "type": "dynamic" }, + { + "name": "SrcUser", + "type": "string" + }, + { + "name": "SrcNodeName", + "type": "string" + }, + { + "name": "SrcOs", + "type": "string" + }, + { + "name": "SrcTags", + "type": "dynamic" + }, + { + "name": "SrcAddresses", + "type": "dynamic" + }, { "name": "DstNodes", "type": "dynamic" }, + { + "name": "DstCount", + "type": "int" + }, + { + "name": "DstNodeId", + "type": "string" + }, + { + "name": "DstNodeName", + "type": "string" + }, + { + "name": "DstUser", + "type": "string" + }, + { + "name": "DstOs", + "type": "string" + }, + { + "name": "DstTags", + "type": "dynamic" + }, + { + "name": "DstAddresses", + "type": "dynamic" + }, { "name": "VirtualTraffic", "type": "dynamic" @@ -1478,6 +1560,26 @@ { "name": "PhysicalTraffic", "type": "dynamic" + }, + { + "name": "HasVirtualTraffic", + "type": "boolean" + }, + { + "name": "HasSubnetTraffic", + "type": "boolean" + }, + { + "name": "HasExitTraffic", + "type": "boolean" + }, + { + "name": "HasPhysicalTraffic", + "type": "boolean" + }, + { + "name": "IsRelayed", + "type": "boolean" } ] }, @@ -1736,7 +1838,7 @@ "email": "[variables('_email')]" }, "support": { - "name": "Community", + "name": "Tailscale (CCF)", "tier": "Community", "email": "ccfconnectors.county118@passmail.com", "link": "https://github.com/Azure/Azure-Sentinel/issues" @@ -1831,7 +1933,7 @@ "email": "[variables('_email')]" }, "support": { - "name": "Community", + "name": "Tailscale (CCF)", "tier": "Community", "email": "ccfconnectors.county118@passmail.com", "link": "https://github.com/Azure/Azure-Sentinel/issues" @@ -2469,7 +2571,7 @@ "email": "[variables('_email')]" }, "support": { - "name": "Community", + "name": "Tailscale (CCF)", "tier": "Community", "email": "ccfconnectors.county118@passmail.com", "link": "https://github.com/Azure/Azure-Sentinel/issues" @@ -2504,6 +2606,14 @@ "name": "eventGroupID", "type": "string" }, + { + "name": "type", + "type": "string" + }, + { + "name": "actionDetails", + "type": "string" + }, { "name": "actor", "type": "dynamic" @@ -2762,11 +2872,19 @@ } ] }, - "Custom-Tailscale_DnsNameservers_CL": { + "Custom-Tailscale_DnsConfig_CL": { "columns": [ { "name": "dns", "type": "dynamic" + }, + { + "name": "magicDNS", + "type": "boolean" + }, + { + "name": "searchPaths", + "type": "dynamic" } ] }, @@ -2806,22 +2924,6 @@ } ] }, - "Custom-Tailscale_DnsPreferences_CL": { - "columns": [ - { - "name": "magicDNS", - "type": "boolean" - } - ] - }, - "Custom-Tailscale_DnsSearchPaths_CL": { - "columns": [ - { - "name": "searchPaths", - "type": "dynamic" - } - ] - }, "Custom-Tailscale_Network_CL": { "columns": [ { @@ -2915,7 +3017,7 @@ "destinations": [ "sentinelWorkspace" ], - "transformKql": "source | extend TimeGenerated = eventTime | extend EventTime = eventTime | extend EventGroupID = eventGroupID | extend Actor = actor | extend Action = action | extend Target = target | extend Origin = origin | extend New = new | extend Old = old | project TimeGenerated, EventTime, EventGroupID, Actor, Action, Target, Origin, New, Old", + "transformKql": "source | extend TimeGenerated = eventTime | extend EventTime = eventTime | extend EventGroupID = eventGroupID | extend EventType = type | extend ActionDetails = actionDetails | extend Actor = actor | extend Action = action | extend Target = target | extend Origin = origin | extend New = new | extend Old = old | project TimeGenerated, EventTime, EventGroupID, EventType, ActionDetails, Actor, Action, Target, Origin, New, Old", "outputStream": "Custom-Tailscale_Audit_CL" }, { @@ -2960,12 +3062,12 @@ }, { "streams": [ - "Custom-Tailscale_DnsNameservers_CL" + "Custom-Tailscale_DnsConfig_CL" ], "destinations": [ "sentinelWorkspace" ], - "transformKql": "source | extend TimeGenerated = now() | extend ConfigType = 'nameservers' | extend Nameservers = dns | project TimeGenerated, ConfigType, Nameservers", + "transformKql": "source | extend TimeGenerated = now() | extend ConfigType = case(isnotnull(dns), 'nameservers', isnotnull(searchPaths), 'searchpaths', 'preferences') | extend Nameservers = dns | extend MagicDNS = magicDNS | extend SearchPaths = searchPaths | project TimeGenerated, ConfigType, Nameservers, MagicDNS, SearchPaths", "outputStream": "Custom-Tailscale_Dns_CL" }, { @@ -2978,26 +3080,6 @@ "transformKql": "source | extend TimeGenerated = now() | extend DevicesApprovalOn = devicesApprovalOn | extend DevicesAutoUpdatesOn = devicesAutoUpdatesOn | extend DevicesKeyDurationDays = devicesKeyDurationDays | extend UsersApprovalOn = usersApprovalOn | extend UsersRoleAllowedToJoinExternalTailnets = usersRoleAllowedToJoinExternalTailnets | extend NetworkFlowLoggingOn = networkFlowLoggingOn | extend RegionalRoutingOn = regionalRoutingOn | extend PostureIdentityCollectionOn = postureIdentityCollectionOn | project TimeGenerated, DevicesApprovalOn, DevicesAutoUpdatesOn, DevicesKeyDurationDays, UsersApprovalOn, UsersRoleAllowedToJoinExternalTailnets, NetworkFlowLoggingOn, RegionalRoutingOn, PostureIdentityCollectionOn", "outputStream": "Custom-Tailscale_Settings_CL" }, - { - "streams": [ - "Custom-Tailscale_DnsPreferences_CL" - ], - "destinations": [ - "sentinelWorkspace" - ], - "transformKql": "source | extend TimeGenerated = now() | extend ConfigType = 'preferences' | extend MagicDNS = magicDNS | project TimeGenerated, ConfigType, MagicDNS", - "outputStream": "Custom-Tailscale_Dns_CL" - }, - { - "streams": [ - "Custom-Tailscale_DnsSearchPaths_CL" - ], - "destinations": [ - "sentinelWorkspace" - ], - "transformKql": "source | extend TimeGenerated = now() | extend ConfigType = 'searchpaths' | extend SearchPaths = searchPaths | project TimeGenerated, ConfigType, SearchPaths", - "outputStream": "Custom-Tailscale_Dns_CL" - }, { "streams": [ "Custom-Tailscale_Network_CL" @@ -3005,7 +3087,7 @@ "destinations": [ "sentinelWorkspace" ], - "transformKql": "source | extend TimeGenerated = logged | extend NodeId = nodeId | extend FlowStart = start | extend FlowEnd = end | extend SrcNode = srcNode | extend DstNodes = dstNodes | extend VirtualTraffic = virtualTraffic | extend SubnetTraffic = subnetTraffic | extend ExitTraffic = exitTraffic | extend PhysicalTraffic = physicalTraffic | project TimeGenerated, NodeId, FlowStart, FlowEnd, SrcNode, DstNodes, VirtualTraffic, SubnetTraffic, ExitTraffic, PhysicalTraffic", + "transformKql": "source | extend TimeGenerated = logged | extend NodeId = nodeId | extend FlowStart = start | extend FlowEnd = end | extend SrcNode = srcNode | extend SrcUser = tostring(srcNode.user) | extend SrcNodeName = tostring(srcNode.name) | extend SrcOs = tostring(srcNode.os) | extend SrcTags = srcNode.tags | extend SrcAddresses = srcNode.addresses | extend DstNodes = dstNodes | extend DstCount = toint(array_length(dstNodes)) | extend DstNodeId = tostring(dstNodes[0].nodeId) | extend DstNodeName = tostring(dstNodes[0].name) | extend DstUser = tostring(dstNodes[0].user) | extend DstOs = tostring(dstNodes[0].os) | extend DstTags = dstNodes[0].tags | extend DstAddresses = dstNodes[0].addresses | extend VirtualTraffic = virtualTraffic | extend SubnetTraffic = subnetTraffic | extend ExitTraffic = exitTraffic | extend PhysicalTraffic = physicalTraffic | extend HasVirtualTraffic = array_length(virtualTraffic) > 0 | extend HasSubnetTraffic = array_length(subnetTraffic) > 0 | extend HasExitTraffic = array_length(exitTraffic) > 0 | extend HasPhysicalTraffic = array_length(physicalTraffic) > 0 | extend IsRelayed = tostring(physicalTraffic) contains '127.3.3.40' | project TimeGenerated, NodeId, FlowStart, FlowEnd, SrcNode, SrcUser, SrcNodeName, SrcOs, SrcTags, SrcAddresses, DstNodes, DstCount, DstNodeId, DstNodeName, DstUser, DstOs, DstTags, DstAddresses, VirtualTraffic, SubnetTraffic, ExitTraffic, PhysicalTraffic, HasVirtualTraffic, HasSubnetTraffic, HasExitTraffic, HasPhysicalTraffic, IsRelayed", "outputStream": "Custom-Tailscale_Network_CL" }, { @@ -3043,6 +3125,14 @@ "name": "EventGroupID", "type": "string" }, + { + "name": "EventType", + "type": "string" + }, + { + "name": "ActionDetails", + "type": "string" + }, { "name": "Actor", "type": "dynamic" @@ -3445,10 +3535,58 @@ "name": "SrcNode", "type": "dynamic" }, + { + "name": "SrcUser", + "type": "string" + }, + { + "name": "SrcNodeName", + "type": "string" + }, + { + "name": "SrcOs", + "type": "string" + }, + { + "name": "SrcTags", + "type": "dynamic" + }, + { + "name": "SrcAddresses", + "type": "dynamic" + }, { "name": "DstNodes", "type": "dynamic" }, + { + "name": "DstCount", + "type": "int" + }, + { + "name": "DstNodeId", + "type": "string" + }, + { + "name": "DstNodeName", + "type": "string" + }, + { + "name": "DstUser", + "type": "string" + }, + { + "name": "DstOs", + "type": "string" + }, + { + "name": "DstTags", + "type": "dynamic" + }, + { + "name": "DstAddresses", + "type": "dynamic" + }, { "name": "VirtualTraffic", "type": "dynamic" @@ -3464,6 +3602,26 @@ { "name": "PhysicalTraffic", "type": "dynamic" + }, + { + "name": "HasVirtualTraffic", + "type": "boolean" + }, + { + "name": "HasSubnetTraffic", + "type": "boolean" + }, + { + "name": "HasExitTraffic", + "type": "boolean" + }, + { + "name": "HasPhysicalTraffic", + "type": "boolean" + }, + { + "name": "IsRelayed", + "type": "boolean" } ] }, @@ -3775,7 +3933,7 @@ "email": "[variables('_email')]" }, "support": { - "name": "Community", + "name": "Tailscale (CCF)", "tier": "Community", "email": "ccfconnectors.county118@passmail.com", "link": "https://github.com/Azure/Azure-Sentinel/issues" @@ -3870,7 +4028,7 @@ "email": "[variables('_email')]" }, "support": { - "name": "Community", + "name": "Tailscale (CCF)", "tier": "Community", "email": "ccfconnectors.county118@passmail.com", "link": "https://github.com/Azure/Azure-Sentinel/issues" @@ -4153,7 +4311,7 @@ "dcrConfig": { "dataCollectionEndpoint": "[[parameters('dcrConfig').dataCollectionEndpoint]", "dataCollectionRuleImmutableId": "[[parameters('dcrConfig').dataCollectionRuleImmutableId]", - "streamName": "Custom-Tailscale_DnsNameservers_CL" + "streamName": "Custom-Tailscale_DnsConfig_CL" }, "auth": { "type": "OAuth2", @@ -4196,7 +4354,7 @@ "dcrConfig": { "dataCollectionEndpoint": "[[parameters('dcrConfig').dataCollectionEndpoint]", "dataCollectionRuleImmutableId": "[[parameters('dcrConfig').dataCollectionRuleImmutableId]", - "streamName": "Custom-Tailscale_DnsPreferences_CL" + "streamName": "Custom-Tailscale_DnsConfig_CL" }, "auth": { "type": "OAuth2", @@ -4239,7 +4397,7 @@ "dcrConfig": { "dataCollectionEndpoint": "[[parameters('dcrConfig').dataCollectionEndpoint]", "dataCollectionRuleImmutableId": "[[parameters('dcrConfig').dataCollectionRuleImmutableId]", - "streamName": "Custom-Tailscale_DnsSearchPaths_CL" + "streamName": "Custom-Tailscale_DnsConfig_CL" }, "auth": { "type": "OAuth2", @@ -4376,7 +4534,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleNewAPIaccesstokenorOAuthclientcreated_AnalyticalRules Analytics Rule with template version 3.0.5", + "description": "TailscaleNewAPIaccesstokenorOAuthclientcreated_AnalyticalRules Analytics Rule with template version 3.0.1", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject1').analyticRuleVersion1]", @@ -4404,10 +4562,10 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscaleCCF", "dataTypes": [ "Tailscale_Audit_CL" - ] + ], + "connectorId": "TailscaleCCF" } ], "tactics": [ @@ -4432,11 +4590,11 @@ "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { - "enabled": true, - "lookbackDuration": "5h", - "matchingMethod": "AllEntities", + "groupByEntities": [], "reopenClosedIncident": false, - "groupByEntities": [] + "matchingMethod": "AllEntities", + "lookbackDuration": "5h", + "enabled": true } } } @@ -4461,7 +4619,7 @@ "email": "[variables('_email')]" }, "support": { - "name": "Community", + "name": "Tailscale (CCF)", "tier": "Community", "email": "ccfconnectors.county118@passmail.com", "link": "https://github.com/Azure/Azure-Sentinel/issues" @@ -4492,7 +4650,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscalePolicyfileACLmodified_AnalyticalRules Analytics Rule with template version 3.0.5", + "description": "TailscaleOAuthClientCreatedWithWriteScopes_AnalyticalRules Analytics Rule with template version 3.0.1", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject2').analyticRuleVersion2]", @@ -4506,13 +4664,13 @@ "kind": "Scheduled", "location": "[parameters('workspace-location')]", "properties": { - "description": "Identifies when the tailnet ACL/policy file is modified. Review the diff - incorrect ACLs can silently expand blast radius across the tailnet.", - "displayName": "Tailscale: Policy file (ACL) modified", + "description": "Detects creation of a Tailscale OAuth client or API access key whose granted scopes include WRITE permissions (anything matching \":write\"). Tokens with write scopes can modify tailnet configuration, manage devices, write ACLs, and revoke keys - high-value adversary targets. Compare against the recent actor history; revoke immediately if unexpected.", + "displayName": "Tailscale: OAuth client or API key created with write scopes", "enabled": false, - "query": "Tailscale_Audit_CL\n| where Action == \"UPDATE\"\n| where tostring(Target.type) == \"TAILNET\"\n| where tostring(Target.property) == \"ACL\"\n| extend ActorLogin = tostring(Actor.loginName)\n| project TimeGenerated, ActorLogin, Action, Target, Origin, Old, New\n", + "query": "Tailscale_Audit_CL\n| where EventType == \"CONFIG\"\n| where Action == \"CREATE\"\n| where tostring(Target.type) in (\"API_KEY\", \"OAUTH_CLIENT\")\n| extend Scopes = extract(@\"scopes\\s*-\\s*(.+)$\", 1, ActionDetails)\n| where Scopes contains \":write\"\n| extend WriteScopes = extract_all(@\"([a-zA-Z_]+:write)\", Scopes)\n| extend ActorLogin = tostring(Actor.loginName)\n| extend ActorType = tostring(Actor.type)\n| extend TargetName = tostring(Target.name)\n| extend TargetId = tostring(Target.id)\n| extend TargetType = tostring(Target.type)\n| project TimeGenerated, ActorLogin, ActorType, Action, TargetType, TargetName, TargetId, WriteScopes, Scopes, Origin, ActionDetails\n", "queryFrequency": "PT15M", "queryPeriod": "PT15M", - "severity": "Medium", + "severity": "High", "suppressionDuration": "PT1H", "suppressionEnabled": false, "triggerOperator": "GreaterThan", @@ -4520,18 +4678,25 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscaleCCF", "dataTypes": [ "Tailscale_Audit_CL" - ] + ], + "connectorId": "TailscaleCCF" + }, + { + "dataTypes": [ + "Tailscale_Audit_CL" + ], + "connectorId": "TailscalePremiumCCF" } ], "tactics": [ - "DefenseEvasion", - "Persistence" + "Persistence", + "PrivilegeEscalation" ], "techniques": [ - "T1556" + "T1098", + "T1136" ], "entityMappings": [ { @@ -4547,11 +4712,11 @@ "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { - "enabled": true, - "lookbackDuration": "5h", - "matchingMethod": "AllEntities", + "groupByEntities": [], "reopenClosedIncident": false, - "groupByEntities": [] + "matchingMethod": "AllEntities", + "lookbackDuration": "PT5H", + "enabled": true } } } @@ -4576,7 +4741,7 @@ "email": "[variables('_email')]" }, "support": { - "name": "Community", + "name": "Tailscale (CCF)", "tier": "Community", "email": "ccfconnectors.county118@passmail.com", "link": "https://github.com/Azure/Azure-Sentinel/issues" @@ -4592,7 +4757,7 @@ "contentSchemaVersion": "3.0.0", "contentId": "[variables('analyticRuleObject2')._analyticRulecontentId2]", "contentKind": "AnalyticsRule", - "displayName": "Tailscale: Policy file (ACL) modified", + "displayName": "Tailscale: OAuth client or API key created with write scopes", "contentProductId": "[variables('analyticRuleObject2')._analyticRulecontentProductId2]", "id": "[variables('analyticRuleObject2')._analyticRulecontentProductId2]", "version": "[variables('analyticRuleObject2').analyticRuleVersion2]" @@ -4607,7 +4772,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleAuthkeycreated_AnalyticalRules Analytics Rule with template version 3.0.5", + "description": "TailscalePolicyfileACLmodified_AnalyticalRules Analytics Rule with template version 3.0.1", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject3').analyticRuleVersion3]", @@ -4621,13 +4786,13 @@ "kind": "Scheduled", "location": "[parameters('workspace-location')]", "properties": { - "description": "Identifies when a new Tailscale auth key is generated. Auth keys allow unattended device enrollment into the tailnet - confirm it was expected and revoke if not.", - "displayName": "Tailscale: Auth key created", + "description": "Identifies when the tailnet ACL/policy file is modified. Review the diff - incorrect ACLs can silently expand blast radius across the tailnet.", + "displayName": "Tailscale: Policy file (ACL) modified", "enabled": false, - "query": "Tailscale_Audit_CL\n | where Action == \"CREATE\"\n | where tostring(Target.type) == \"AUTH_KEY\"\n | extend ActorLogin = tostring(Actor.loginName)\n | extend KeyDescription = tostring(New.description)\n | extend Reusable = tostring(New.reusable)\n | extend Ephemeral = tostring(New.ephemeral)\n | project TimeGenerated, ActorLogin, KeyDescription, Reusable, Ephemeral, Origin, New\n", + "query": "Tailscale_Audit_CL\n| where Action == \"UPDATE\"\n| where tostring(Target.type) == \"TAILNET\"\n| where tostring(Target.property) == \"ACL\"\n| extend ActorLogin = tostring(Actor.loginName)\n| project TimeGenerated, ActorLogin, Action, Target, Origin, Old, New\n", "queryFrequency": "PT15M", "queryPeriod": "PT15M", - "severity": "Low", + "severity": "Medium", "suppressionDuration": "PT1H", "suppressionEnabled": false, "triggerOperator": "GreaterThan", @@ -4635,17 +4800,18 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscaleCCF", "dataTypes": [ "Tailscale_Audit_CL" - ] + ], + "connectorId": "TailscaleCCF" } ], "tactics": [ + "DefenseEvasion", "Persistence" ], "techniques": [ - "T1098" + "T1556" ], "entityMappings": [ { @@ -4661,11 +4827,11 @@ "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { - "enabled": true, - "lookbackDuration": "5h", - "matchingMethod": "AllEntities", + "groupByEntities": [], "reopenClosedIncident": false, - "groupByEntities": [] + "matchingMethod": "AllEntities", + "lookbackDuration": "5h", + "enabled": true } } } @@ -4690,7 +4856,7 @@ "email": "[variables('_email')]" }, "support": { - "name": "Community", + "name": "Tailscale (CCF)", "tier": "Community", "email": "ccfconnectors.county118@passmail.com", "link": "https://github.com/Azure/Azure-Sentinel/issues" @@ -4706,7 +4872,7 @@ "contentSchemaVersion": "3.0.0", "contentId": "[variables('analyticRuleObject3')._analyticRulecontentId3]", "contentKind": "AnalyticsRule", - "displayName": "Tailscale: Auth key created", + "displayName": "Tailscale: Policy file (ACL) modified", "contentProductId": "[variables('analyticRuleObject3')._analyticRulecontentProductId3]", "id": "[variables('analyticRuleObject3')._analyticRulecontentProductId3]", "version": "[variables('analyticRuleObject3').analyticRuleVersion3]" @@ -4721,7 +4887,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleExitnodeadvertisedorapproved_AnalyticalRules Analytics Rule with template version 3.0.5", + "description": "TailscaleAuthkeycreated_AnalyticalRules Analytics Rule with template version 3.0.1", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject4').analyticRuleVersion4]", @@ -4735,12 +4901,12 @@ "kind": "Scheduled", "location": "[parameters('workspace-location')]", "properties": { - "description": "Identifies when a device starts advertising itself as an exit node, or when an admin approves one. Validate the device and operator - rogue exit nodes can intercept tailnet egress.", - "displayName": "Tailscale: Exit node advertised or approved", + "description": "Identifies when a new Tailscale auth key is generated. Auth keys allow unattended device enrollment into the tailnet - confirm it was expected and revoke if not.", + "displayName": "Tailscale: Auth key created", "enabled": false, - "query": "Tailscale_Audit_CL\n | where Action contains \"EXIT\" or tostring(New.advertisedExitNode) == \"true\" or tostring(New.allowedExitNode) == \"true\"\n | extend ActorLogin = tostring(Actor.loginName)\n | extend NodeName = tostring(Target.name)\n | extend NodeId = tostring(Target.id)\n | project TimeGenerated, ActorLogin, Action, NodeName, NodeId, Origin, New, Old\n", - "queryFrequency": "PT30M", - "queryPeriod": "PT30M", + "query": "Tailscale_Audit_CL\n | where Action == \"CREATE\"\n | where tostring(Target.type) == \"AUTH_KEY\"\n | extend ActorLogin = tostring(Actor.loginName)\n | extend KeyDescription = tostring(New.description)\n | extend Reusable = tostring(New.reusable)\n | extend Ephemeral = tostring(New.ephemeral)\n | project TimeGenerated, ActorLogin, KeyDescription, Reusable, Ephemeral, Origin, New\n", + "queryFrequency": "PT15M", + "queryPeriod": "PT15M", "severity": "Low", "suppressionDuration": "PT1H", "suppressionEnabled": false, @@ -4749,18 +4915,17 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscaleCCF", "dataTypes": [ "Tailscale_Audit_CL" - ] + ], + "connectorId": "TailscaleCCF" } ], "tactics": [ - "CommandAndControl", - "Exfiltration" + "Persistence" ], "techniques": [ - "T1090" + "T1098" ], "entityMappings": [ { @@ -4771,25 +4936,16 @@ } ], "entityType": "Account" - }, - { - "fieldMappings": [ - { - "columnName": "NodeName", - "identifier": "HostName" - } - ], - "entityType": "Host" } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { - "enabled": true, - "lookbackDuration": "5h", - "matchingMethod": "AllEntities", + "groupByEntities": [], "reopenClosedIncident": false, - "groupByEntities": [] + "matchingMethod": "AllEntities", + "lookbackDuration": "5h", + "enabled": true } } } @@ -4814,7 +4970,7 @@ "email": "[variables('_email')]" }, "support": { - "name": "Community", + "name": "Tailscale (CCF)", "tier": "Community", "email": "ccfconnectors.county118@passmail.com", "link": "https://github.com/Azure/Azure-Sentinel/issues" @@ -4830,7 +4986,7 @@ "contentSchemaVersion": "3.0.0", "contentId": "[variables('analyticRuleObject4')._analyticRulecontentId4]", "contentKind": "AnalyticsRule", - "displayName": "Tailscale: Exit node advertised or approved", + "displayName": "Tailscale: Auth key created", "contentProductId": "[variables('analyticRuleObject4')._analyticRulecontentProductId4]", "id": "[variables('analyticRuleObject4')._analyticRulecontentProductId4]", "version": "[variables('analyticRuleObject4').analyticRuleVersion4]" @@ -4845,7 +5001,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleMasscredentialrevocationinshortwindow_AnalyticalRules Analytics Rule with template version 3.0.5", + "description": "TailscaleExitnodeadvertisedorapproved_AnalyticalRules Analytics Rule with template version 3.0.1", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject5').analyticRuleVersion5]", @@ -4859,13 +5015,13 @@ "kind": "Scheduled", "location": "[parameters('workspace-location')]", "properties": { - "description": "Identifies when five or more API keys, OAuth clients, or auth keys are revoked or deleted within one hour. May be routine rotation, or a typical cleanup pattern after credential compromise.", - "displayName": "Tailscale: Mass credential revocation in short window", + "description": "Identifies when a device starts advertising itself as an exit node, or when an admin approves one. Validate the device and operator - rogue exit nodes can intercept tailnet egress.", + "displayName": "Tailscale: Exit node advertised or approved", "enabled": false, - "query": "Tailscale_Audit_CL\n | where Action in (\"REVOKE\", \"DELETE\")\n | where tostring(Target.type) in (\"API_KEY\", \"OAUTH_CLIENT\", \"AUTH_KEY\")\n | extend ActorLogin = tostring(Actor.loginName)\n | summarize\n RevokedCount = count(),\n TargetTypes = make_set(tostring(Target.type)),\n TargetIds = make_set(tostring(Target.id)),\n FirstEvent = min(TimeGenerated),\n LastEvent = max(TimeGenerated)\n by ActorLogin, bin(TimeGenerated, 1h)\n | where RevokedCount >= 5\n | project TimeGenerated = LastEvent, ActorLogin, RevokedCount, TargetTypes, TargetIds, FirstEvent, LastEvent\n", - "queryFrequency": "PT1H", - "queryPeriod": "PT1H", - "severity": "High", + "query": "Tailscale_Audit_CL\n | where Action contains \"EXIT\" or tostring(New.advertisedExitNode) == \"true\" or tostring(New.allowedExitNode) == \"true\"\n | extend ActorLogin = tostring(Actor.loginName)\n | extend NodeName = tostring(Target.name)\n | extend NodeId = tostring(Target.id)\n | project TimeGenerated, ActorLogin, Action, NodeName, NodeId, Origin, New, Old\n", + "queryFrequency": "PT30M", + "queryPeriod": "PT30M", + "severity": "Low", "suppressionDuration": "PT1H", "suppressionEnabled": false, "triggerOperator": "GreaterThan", @@ -4873,18 +5029,18 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscaleCCF", "dataTypes": [ "Tailscale_Audit_CL" - ] + ], + "connectorId": "TailscaleCCF" } ], "tactics": [ - "DefenseEvasion", - "Impact" + "CommandAndControl", + "Exfiltration" ], "techniques": [ - "T1070" + "T1090" ], "entityMappings": [ { @@ -4895,16 +5051,25 @@ } ], "entityType": "Account" + }, + { + "fieldMappings": [ + { + "columnName": "NodeName", + "identifier": "HostName" + } + ], + "entityType": "Host" } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { - "enabled": true, - "lookbackDuration": "5h", - "matchingMethod": "AllEntities", + "groupByEntities": [], "reopenClosedIncident": false, - "groupByEntities": [] + "matchingMethod": "AllEntities", + "lookbackDuration": "5h", + "enabled": true } } } @@ -4929,7 +5094,7 @@ "email": "[variables('_email')]" }, "support": { - "name": "Community", + "name": "Tailscale (CCF)", "tier": "Community", "email": "ccfconnectors.county118@passmail.com", "link": "https://github.com/Azure/Azure-Sentinel/issues" @@ -4945,7 +5110,7 @@ "contentSchemaVersion": "3.0.0", "contentId": "[variables('analyticRuleObject5')._analyticRulecontentId5]", "contentKind": "AnalyticsRule", - "displayName": "Tailscale: Mass credential revocation in short window", + "displayName": "Tailscale: Exit node advertised or approved", "contentProductId": "[variables('analyticRuleObject5')._analyticRulecontentProductId5]", "id": "[variables('analyticRuleObject5')._analyticRulecontentProductId5]", "version": "[variables('analyticRuleObject5').analyticRuleVersion5]" @@ -4960,7 +5125,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleDeviceKeyExpiringSoon_AnalyticalRules Analytics Rule with template version 3.0.5", + "description": "TailscaleMasscredentialrevocationinshortwindow_AnalyticalRules Analytics Rule with template version 3.0.1", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject6').analyticRuleVersion6]", @@ -4974,13 +5139,13 @@ "kind": "Scheduled", "location": "[parameters('workspace-location')]", "properties": { - "description": "Identifies tailnet devices whose machine key expires within the next 7 days and where key expiry is not disabled. Surface proactively so renewal can be scheduled rather than forced during an outage.", - "displayName": "Tailscale: Device key expiring within 7 days", + "description": "Identifies when five or more API keys, OAuth clients, or auth keys are revoked or deleted within one hour. May be routine rotation, or a typical cleanup pattern after credential compromise.", + "displayName": "Tailscale: Mass credential revocation in short window", "enabled": false, - "query": "Tailscale_Devices_CL\n| where TimeGenerated > ago(6h)\n| summarize arg_max(TimeGenerated, *) by DeviceId\n| where KeyExpiryDisabled == false\n| where isnotnull(Expires)\n| where Expires between (now() .. now() + 7d)\n| extend DaysToExpiry = datetime_diff('day', Expires, now())\n| project TimeGenerated, DeviceName, Hostname, User, Os, DaysToExpiry, Expires, LastSeen\n", - "queryFrequency": "PT6H", - "queryPeriod": "PT6H", - "severity": "Medium", + "query": "Tailscale_Audit_CL\n | where Action in (\"REVOKE\", \"DELETE\")\n | where tostring(Target.type) in (\"API_KEY\", \"OAUTH_CLIENT\", \"AUTH_KEY\")\n | extend ActorLogin = tostring(Actor.loginName)\n | summarize\n RevokedCount = count(),\n TargetTypes = make_set(tostring(Target.type)),\n TargetIds = make_set(tostring(Target.id)),\n FirstEvent = min(TimeGenerated),\n LastEvent = max(TimeGenerated)\n by ActorLogin, bin(TimeGenerated, 1h)\n | where RevokedCount >= 5\n | project TimeGenerated = LastEvent, ActorLogin, RevokedCount, TargetTypes, TargetIds, FirstEvent, LastEvent\n", + "queryFrequency": "PT1H", + "queryPeriod": "PT1H", + "severity": "High", "suppressionDuration": "PT1H", "suppressionEnabled": false, "triggerOperator": "GreaterThan", @@ -4988,32 +5153,24 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscaleCCF", "dataTypes": [ - "Tailscale_Devices_CL" - ] + "Tailscale_Audit_CL" + ], + "connectorId": "TailscaleCCF" } ], "tactics": [ - "InitialAccess" + "DefenseEvasion", + "Impact" ], "techniques": [ - "T1078" + "T1070" ], "entityMappings": [ { "fieldMappings": [ { - "columnName": "Hostname", - "identifier": "HostName" - } - ], - "entityType": "Host" - }, - { - "fieldMappings": [ - { - "columnName": "User", + "columnName": "ActorLogin", "identifier": "FullName" } ], @@ -5023,11 +5180,11 @@ "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { - "enabled": true, - "lookbackDuration": "1d", - "matchingMethod": "AllEntities", + "groupByEntities": [], "reopenClosedIncident": false, - "groupByEntities": [] + "matchingMethod": "AllEntities", + "lookbackDuration": "5h", + "enabled": true } } } @@ -5052,7 +5209,7 @@ "email": "[variables('_email')]" }, "support": { - "name": "Community", + "name": "Tailscale (CCF)", "tier": "Community", "email": "ccfconnectors.county118@passmail.com", "link": "https://github.com/Azure/Azure-Sentinel/issues" @@ -5068,7 +5225,7 @@ "contentSchemaVersion": "3.0.0", "contentId": "[variables('analyticRuleObject6')._analyticRulecontentId6]", "contentKind": "AnalyticsRule", - "displayName": "Tailscale: Device key expiring within 7 days", + "displayName": "Tailscale: Mass credential revocation in short window", "contentProductId": "[variables('analyticRuleObject6')._analyticRulecontentProductId6]", "id": "[variables('analyticRuleObject6')._analyticRulecontentProductId6]", "version": "[variables('analyticRuleObject6').analyticRuleVersion6]" @@ -5083,7 +5240,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleDeviceAdvertisingSubnetRoutes_AnalyticalRules Analytics Rule with template version 3.0.5", + "description": "TailscaleDeviceKeyExpiringSoon_AnalyticalRules Analytics Rule with template version 3.0.1", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject7').analyticRuleVersion7]", @@ -5097,12 +5254,12 @@ "kind": "Scheduled", "location": "[parameters('workspace-location')]", "properties": { - "description": "Identifies when a tailnet device begins advertising subnet routes (subnet-router capability) not present in the previous snapshot. Unexpected advertisement may indicate a compromised node expanding reachable surface area or an unsanctioned admin change.", - "displayName": "Tailscale: Device started advertising subnet routes", + "description": "Identifies tailnet devices whose machine key expires within the next 7 days and where key expiry is not disabled. Surface proactively so renewal can be scheduled rather than forced during an outage.", + "displayName": "Tailscale: Device key expiring within 7 days", "enabled": false, - "query": "let recent =\n Tailscale_Devices_CL\n | where TimeGenerated > ago(1h)\n | summarize arg_max(TimeGenerated, *) by DeviceId\n | where array_length(AdvertisedRoutes) > 0;\nlet baseline =\n Tailscale_Devices_CL\n | where TimeGenerated between (ago(1d + 1h) .. ago(1h))\n | summarize arg_max(TimeGenerated, *) by DeviceId\n | where array_length(AdvertisedRoutes) > 0\n | distinct DeviceId;\nrecent\n| join kind=leftanti baseline on DeviceId\n| project TimeGenerated, DeviceId, DeviceName, Hostname, User, AdvertisedRoutes, EnabledRoutes, LastSeen\n", - "queryFrequency": "PT1H", - "queryPeriod": "P1D", + "query": "Tailscale_Devices_CL\n| where TimeGenerated > ago(6h)\n| summarize arg_max(TimeGenerated, *) by DeviceId\n| where KeyExpiryDisabled == false\n| where isnotnull(Expires)\n| where Expires between (now() .. now() + 7d)\n| extend DaysToExpiry = datetime_diff('day', Expires, now())\n| project TimeGenerated, DeviceName, Hostname, User, Os, DaysToExpiry, Expires, LastSeen\n", + "queryFrequency": "PT6H", + "queryPeriod": "PT6H", "severity": "Medium", "suppressionDuration": "PT1H", "suppressionEnabled": false, @@ -5111,19 +5268,17 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscaleCCF", "dataTypes": [ "Tailscale_Devices_CL" - ] + ], + "connectorId": "TailscaleCCF" } ], "tactics": [ - "LateralMovement", - "Persistence" + "InitialAccess" ], "techniques": [ - "T1021", - "T1556" + "T1078" ], "entityMappings": [ { @@ -5148,11 +5303,11 @@ "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { - "enabled": true, - "lookbackDuration": "1d", - "matchingMethod": "AllEntities", + "groupByEntities": [], "reopenClosedIncident": false, - "groupByEntities": [] + "matchingMethod": "AllEntities", + "lookbackDuration": "1d", + "enabled": true } } } @@ -5177,7 +5332,7 @@ "email": "[variables('_email')]" }, "support": { - "name": "Community", + "name": "Tailscale (CCF)", "tier": "Community", "email": "ccfconnectors.county118@passmail.com", "link": "https://github.com/Azure/Azure-Sentinel/issues" @@ -5193,7 +5348,7 @@ "contentSchemaVersion": "3.0.0", "contentId": "[variables('analyticRuleObject7')._analyticRulecontentId7]", "contentKind": "AnalyticsRule", - "displayName": "Tailscale: Device started advertising subnet routes", + "displayName": "Tailscale: Device key expiring within 7 days", "contentProductId": "[variables('analyticRuleObject7')._analyticRulecontentProductId7]", "id": "[variables('analyticRuleObject7')._analyticRulecontentProductId7]", "version": "[variables('analyticRuleObject7').analyticRuleVersion7]" @@ -5208,7 +5363,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleUserRoleElevated_AnalyticalRules Analytics Rule with template version 3.0.5", + "description": "TailscaleDeviceAdvertisingSubnetRoutes_AnalyticalRules Analytics Rule with template version 3.0.1", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject8').analyticRuleVersion8]", @@ -5222,13 +5377,13 @@ "kind": "Scheduled", "location": "[parameters('workspace-location')]", "properties": { - "description": "Identifies when a user's tailnet role changes from a lower-privilege role to admin, network-admin, or owner between consecutive snapshots. Privilege escalation is a high-value attacker objective and warrants prompt review.", - "displayName": "Tailscale: User role elevated to admin or owner", + "description": "Identifies when a tailnet device begins advertising subnet routes (subnet-router capability) not present in the previous snapshot. Unexpected advertisement may indicate a compromised node expanding reachable surface area or an unsanctioned admin change.", + "displayName": "Tailscale: Device started advertising subnet routes", "enabled": false, - "query": "let elevated = dynamic([\"admin\", \"network-admin\", \"owner\"]);\nlet recent =\n Tailscale_Users_CL\n | where TimeGenerated > ago(1h)\n | summarize arg_max(TimeGenerated, *) by UserId\n | where Role in (elevated)\n | project UserId, LoginName, RoleNow = Role;\nlet prior =\n Tailscale_Users_CL\n | where TimeGenerated between (ago(2d) .. ago(1h))\n | summarize arg_max(TimeGenerated, *) by UserId\n | project UserId, RolePrior = Role;\nrecent\n| join kind=inner prior on UserId\n| where RoleNow != RolePrior\n| where RolePrior !in (elevated)\n| project TimeGenerated = now(), UserId, LoginName, RolePrior, RoleNow\n", + "query": "let recent =\n Tailscale_Devices_CL\n | where TimeGenerated > ago(1h)\n | summarize arg_max(TimeGenerated, *) by DeviceId\n | where array_length(AdvertisedRoutes) > 0;\nlet baseline =\n Tailscale_Devices_CL\n | where TimeGenerated between (ago(1d + 1h) .. ago(1h))\n | summarize arg_max(TimeGenerated, *) by DeviceId\n | where array_length(AdvertisedRoutes) > 0\n | distinct DeviceId;\nrecent\n| join kind=leftanti baseline on DeviceId\n| project TimeGenerated, DeviceId, DeviceName, Hostname, User, AdvertisedRoutes, EnabledRoutes, LastSeen\n", "queryFrequency": "PT1H", - "queryPeriod": "P2D", - "severity": "High", + "queryPeriod": "P1D", + "severity": "Medium", "suppressionDuration": "PT1H", "suppressionEnabled": false, "triggerOperator": "GreaterThan", @@ -5236,25 +5391,34 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscaleCCF", "dataTypes": [ - "Tailscale_Users_CL" - ] + "Tailscale_Devices_CL" + ], + "connectorId": "TailscaleCCF" } ], "tactics": [ - "PrivilegeEscalation", + "LateralMovement", "Persistence" ], "techniques": [ - "T1078", - "T1098" + "T1021", + "T1556" ], "entityMappings": [ { "fieldMappings": [ { - "columnName": "LoginName", + "columnName": "Hostname", + "identifier": "HostName" + } + ], + "entityType": "Host" + }, + { + "fieldMappings": [ + { + "columnName": "User", "identifier": "FullName" } ], @@ -5264,11 +5428,11 @@ "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { - "enabled": true, - "lookbackDuration": "1d", - "matchingMethod": "AllEntities", + "groupByEntities": [], "reopenClosedIncident": false, - "groupByEntities": [] + "matchingMethod": "AllEntities", + "lookbackDuration": "1d", + "enabled": true } } } @@ -5293,7 +5457,7 @@ "email": "[variables('_email')]" }, "support": { - "name": "Community", + "name": "Tailscale (CCF)", "tier": "Community", "email": "ccfconnectors.county118@passmail.com", "link": "https://github.com/Azure/Azure-Sentinel/issues" @@ -5309,7 +5473,7 @@ "contentSchemaVersion": "3.0.0", "contentId": "[variables('analyticRuleObject8')._analyticRulecontentId8]", "contentKind": "AnalyticsRule", - "displayName": "Tailscale: User role elevated to admin or owner", + "displayName": "Tailscale: Device started advertising subnet routes", "contentProductId": "[variables('analyticRuleObject8')._analyticRulecontentProductId8]", "id": "[variables('analyticRuleObject8')._analyticRulecontentProductId8]", "version": "[variables('analyticRuleObject8').analyticRuleVersion8]" @@ -5324,7 +5488,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleSplitDnsModified_AnalyticalRules Analytics Rule with template version 3.0.5", + "description": "TailscaleUserRoleElevated_AnalyticalRules Analytics Rule with template version 3.0.1", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject9').analyticRuleVersion9]", @@ -5338,12 +5502,12 @@ "kind": "Scheduled", "location": "[parameters('workspace-location')]", "properties": { - "description": "Identifies when the tailnet split-DNS configuration is modified. Split-DNS overrides per-domain resolution within the tailnet - an attacker adding a new domain mapping or changing the resolver IP can hijack DNS for that domain.", - "displayName": "Tailscale: Split-DNS configuration modified", + "description": "Identifies when a user's tailnet role changes from a lower-privilege role to admin, network-admin, or owner between consecutive snapshots. Privilege escalation is a high-value attacker objective and warrants prompt review.", + "displayName": "Tailscale: User role elevated to admin or owner", "enabled": false, - "query": "Tailscale_Audit_CL\n| where Action == \"UPDATE\"\n| where tostring(Target.type) == \"TAILNET\"\n| where tostring(Target.property) == \"DNS_SPLIT_DNS\"\n| extend ActorLogin = tostring(Actor.loginName)\n| extend OldDomains = bag_keys(Old)\n| extend NewDomains = bag_keys(New)\n| extend AddedDomains = set_difference(NewDomains, OldDomains)\n| extend RemovedDomains = set_difference(OldDomains, NewDomains)\n| project TimeGenerated, ActorLogin, AddedDomains, RemovedDomains, Old, New, Origin\n", - "queryFrequency": "PT15M", - "queryPeriod": "PT15M", + "query": "let elevated = dynamic([\"admin\", \"network-admin\", \"owner\"]);\nlet recent =\n Tailscale_Users_CL\n | where TimeGenerated > ago(1h)\n | summarize arg_max(TimeGenerated, *) by UserId\n | where Role in (elevated)\n | project UserId, LoginName, RoleNow = Role;\nlet prior =\n Tailscale_Users_CL\n | where TimeGenerated between (ago(2d) .. ago(1h))\n | summarize arg_max(TimeGenerated, *) by UserId\n | project UserId, RolePrior = Role;\nrecent\n| join kind=inner prior on UserId\n| where RoleNow != RolePrior\n| where RolePrior !in (elevated)\n| project TimeGenerated = now(), UserId, LoginName, RolePrior, RoleNow\n", + "queryFrequency": "PT1H", + "queryPeriod": "P2D", "severity": "High", "suppressionDuration": "PT1H", "suppressionEnabled": false, @@ -5352,25 +5516,25 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscaleCCF", "dataTypes": [ - "Tailscale_Audit_CL" - ] + "Tailscale_Users_CL" + ], + "connectorId": "TailscaleCCF" } ], "tactics": [ - "DefenseEvasion", - "CommandAndControl" + "PrivilegeEscalation", + "Persistence" ], "techniques": [ - "T1556", - "T1568" + "T1078", + "T1098" ], "entityMappings": [ { "fieldMappings": [ { - "columnName": "ActorLogin", + "columnName": "LoginName", "identifier": "FullName" } ], @@ -5380,11 +5544,11 @@ "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { - "enabled": true, - "lookbackDuration": "1d", - "matchingMethod": "AllEntities", + "groupByEntities": [], "reopenClosedIncident": false, - "groupByEntities": [] + "matchingMethod": "AllEntities", + "lookbackDuration": "1d", + "enabled": true } } } @@ -5409,7 +5573,7 @@ "email": "[variables('_email')]" }, "support": { - "name": "Community", + "name": "Tailscale (CCF)", "tier": "Community", "email": "ccfconnectors.county118@passmail.com", "link": "https://github.com/Azure/Azure-Sentinel/issues" @@ -5425,7 +5589,7 @@ "contentSchemaVersion": "3.0.0", "contentId": "[variables('analyticRuleObject9')._analyticRulecontentId9]", "contentKind": "AnalyticsRule", - "displayName": "Tailscale: Split-DNS configuration modified", + "displayName": "Tailscale: User role elevated to admin or owner", "contentProductId": "[variables('analyticRuleObject9')._analyticRulecontentProductId9]", "id": "[variables('analyticRuleObject9')._analyticRulecontentProductId9]", "version": "[variables('analyticRuleObject9').analyticRuleVersion9]" @@ -5440,7 +5604,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleDnsNameserversModified_AnalyticalRules Analytics Rule with template version 3.0.5", + "description": "TailscaleSplitDnsModified_AnalyticalRules Analytics Rule with template version 3.0.1", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject10').analyticRuleVersion10]", @@ -5454,10 +5618,10 @@ "kind": "Scheduled", "location": "[parameters('workspace-location')]", "properties": { - "description": "Identifies when the tailnet's global DNS nameserver list is modified. Adding an attacker-controlled resolver as a tailnet-wide nameserver enables broad DNS hijacking for every device using MagicDNS resolution.", - "displayName": "Tailscale: DNS nameservers modified", + "description": "Identifies when the tailnet split-DNS configuration is modified. Split-DNS overrides per-domain resolution within the tailnet - an attacker adding a new domain mapping or changing the resolver IP can hijack DNS for that domain.", + "displayName": "Tailscale: Split-DNS configuration modified", "enabled": false, - "query": "Tailscale_Audit_CL\n| where Action == \"UPDATE\"\n| where tostring(Target.type) == \"TAILNET\"\n| where tostring(Target.property) == \"DNS_NAMESERVERS\"\n| extend ActorLogin = tostring(Actor.loginName)\n| extend OldServers = Old.dns\n| extend NewServers = New.dns\n| extend Added = set_difference(NewServers, OldServers)\n| extend Removed = set_difference(OldServers, NewServers)\n| project TimeGenerated, ActorLogin, OldServers, NewServers, Added, Removed, Origin\n", + "query": "Tailscale_Audit_CL\n| where Action == \"UPDATE\"\n| where tostring(Target.type) == \"TAILNET\"\n| where tostring(Target.property) == \"DNS_SPLIT_DNS\"\n| extend ActorLogin = tostring(Actor.loginName)\n| extend OldDomains = bag_keys(Old)\n| extend NewDomains = bag_keys(New)\n| extend AddedDomains = set_difference(NewDomains, OldDomains)\n| extend RemovedDomains = set_difference(OldDomains, NewDomains)\n| project TimeGenerated, ActorLogin, AddedDomains, RemovedDomains, Old, New, Origin\n", "queryFrequency": "PT15M", "queryPeriod": "PT15M", "severity": "High", @@ -5468,10 +5632,10 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscaleCCF", "dataTypes": [ "Tailscale_Audit_CL" - ] + ], + "connectorId": "TailscaleCCF" } ], "tactics": [ @@ -5496,11 +5660,11 @@ "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { - "enabled": true, - "lookbackDuration": "1d", - "matchingMethod": "AllEntities", + "groupByEntities": [], "reopenClosedIncident": false, - "groupByEntities": [] + "matchingMethod": "AllEntities", + "lookbackDuration": "1d", + "enabled": true } } } @@ -5525,7 +5689,7 @@ "email": "[variables('_email')]" }, "support": { - "name": "Community", + "name": "Tailscale (CCF)", "tier": "Community", "email": "ccfconnectors.county118@passmail.com", "link": "https://github.com/Azure/Azure-Sentinel/issues" @@ -5541,7 +5705,7 @@ "contentSchemaVersion": "3.0.0", "contentId": "[variables('analyticRuleObject10')._analyticRulecontentId10]", "contentKind": "AnalyticsRule", - "displayName": "Tailscale: DNS nameservers modified", + "displayName": "Tailscale: Split-DNS configuration modified", "contentProductId": "[variables('analyticRuleObject10')._analyticRulecontentProductId10]", "id": "[variables('analyticRuleObject10')._analyticRulecontentProductId10]", "version": "[variables('analyticRuleObject10').analyticRuleVersion10]" @@ -5556,7 +5720,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleMagicDnsDisabled_AnalyticalRules Analytics Rule with template version 3.0.5", + "description": "TailscaleDnsNameserversModified_AnalyticalRules Analytics Rule with template version 3.0.1", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject11').analyticRuleVersion11]", @@ -5570,13 +5734,13 @@ "kind": "Scheduled", "location": "[parameters('workspace-location')]", "properties": { - "description": "Identifies when MagicDNS is turned off on the tailnet. Disabling MagicDNS changes DNS behaviour for every device and is occasionally a precursor to wider DNS hijacking - verify the change was intentional.", - "displayName": "Tailscale: MagicDNS disabled", + "description": "Identifies when the tailnet's global DNS nameserver list is modified. Adding an attacker-controlled resolver as a tailnet-wide nameserver enables broad DNS hijacking for every device using MagicDNS resolution.", + "displayName": "Tailscale: DNS nameservers modified", "enabled": false, - "query": "Tailscale_Audit_CL\n| where Action == \"UPDATE\"\n| where tostring(Target.type) == \"TAILNET\"\n| where tostring(Target.property) == \"MAGICDNS_PREFERENCES\"\n| extend ActorLogin = tostring(Actor.loginName)\n| extend OldMagicDns = tobool(Old.magicDNS)\n| extend NewMagicDns = tobool(New.magicDNS)\n| where OldMagicDns == true and NewMagicDns == false\n| project TimeGenerated, ActorLogin, OldMagicDns, NewMagicDns, Origin\n", + "query": "Tailscale_Audit_CL\n| where Action == \"UPDATE\"\n| where tostring(Target.type) == \"TAILNET\"\n| where tostring(Target.property) == \"DNS_NAMESERVERS\"\n| extend ActorLogin = tostring(Actor.loginName)\n| extend OldServers = Old.dns\n| extend NewServers = New.dns\n| extend Added = set_difference(NewServers, OldServers)\n| extend Removed = set_difference(OldServers, NewServers)\n| project TimeGenerated, ActorLogin, OldServers, NewServers, Added, Removed, Origin\n", "queryFrequency": "PT15M", "queryPeriod": "PT15M", - "severity": "Medium", + "severity": "High", "suppressionDuration": "PT1H", "suppressionEnabled": false, "triggerOperator": "GreaterThan", @@ -5584,17 +5748,19 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscaleCCF", "dataTypes": [ "Tailscale_Audit_CL" - ] + ], + "connectorId": "TailscaleCCF" } ], "tactics": [ - "DefenseEvasion" + "DefenseEvasion", + "CommandAndControl" ], "techniques": [ - "T1556" + "T1556", + "T1568" ], "entityMappings": [ { @@ -5610,11 +5776,11 @@ "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { - "enabled": true, - "lookbackDuration": "1d", - "matchingMethod": "AllEntities", + "groupByEntities": [], "reopenClosedIncident": false, - "groupByEntities": [] + "matchingMethod": "AllEntities", + "lookbackDuration": "1d", + "enabled": true } } } @@ -5639,7 +5805,7 @@ "email": "[variables('_email')]" }, "support": { - "name": "Community", + "name": "Tailscale (CCF)", "tier": "Community", "email": "ccfconnectors.county118@passmail.com", "link": "https://github.com/Azure/Azure-Sentinel/issues" @@ -5655,7 +5821,7 @@ "contentSchemaVersion": "3.0.0", "contentId": "[variables('analyticRuleObject11')._analyticRulecontentId11]", "contentKind": "AnalyticsRule", - "displayName": "Tailscale: MagicDNS disabled", + "displayName": "Tailscale: DNS nameservers modified", "contentProductId": "[variables('analyticRuleObject11')._analyticRulecontentProductId11]", "id": "[variables('analyticRuleObject11')._analyticRulecontentProductId11]", "version": "[variables('analyticRuleObject11').analyticRuleVersion11]" @@ -5670,7 +5836,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleTailnetLockValidationFailed_AnalyticalRules Analytics Rule with template version 3.0.5", + "description": "TailscaleMagicDnsDisabled_AnalyticalRules Analytics Rule with template version 3.0.1", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject12').analyticRuleVersion12]", @@ -5684,13 +5850,13 @@ "kind": "Scheduled", "location": "[parameters('workspace-location')]", "properties": { - "description": "Identifies tailnet devices with a non-empty TailnetLockError, indicating the device failed tailnet-lock cryptographic validation. Tailnet lock requires new node-keys be co-signed by trusted signers; errors here are the only direct signal of a node-key injection attempt.", - "displayName": "Tailscale: Tailnet lock validation failed", + "description": "Identifies when MagicDNS is turned off on the tailnet. Disabling MagicDNS changes DNS behaviour for every device and is occasionally a precursor to wider DNS hijacking - verify the change was intentional.", + "displayName": "Tailscale: MagicDNS disabled", "enabled": false, - "query": "Tailscale_Devices_CL\n| where TimeGenerated > ago(1h)\n| summarize arg_max(TimeGenerated, *) by DeviceId\n| where isnotempty(TailnetLockError)\n| project TimeGenerated, DeviceName, Hostname, User, Os, ClientVersion, TailnetLockError, TailnetLockKey, LastSeen, Authorized\n", - "queryFrequency": "PT1H", - "queryPeriod": "PT1H", - "severity": "High", + "query": "Tailscale_Audit_CL\n| where Action == \"UPDATE\"\n| where tostring(Target.type) == \"TAILNET\"\n| where tostring(Target.property) == \"MAGICDNS_PREFERENCES\"\n| extend ActorLogin = tostring(Actor.loginName)\n| extend OldMagicDns = tobool(Old.magicDNS)\n| extend NewMagicDns = tobool(New.magicDNS)\n| where OldMagicDns == true and NewMagicDns == false\n| project TimeGenerated, ActorLogin, OldMagicDns, NewMagicDns, Origin\n", + "queryFrequency": "PT15M", + "queryPeriod": "PT15M", + "severity": "Medium", "suppressionDuration": "PT1H", "suppressionEnabled": false, "triggerOperator": "GreaterThan", @@ -5698,34 +5864,23 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscaleCCF", "dataTypes": [ - "Tailscale_Devices_CL" - ] + "Tailscale_Audit_CL" + ], + "connectorId": "TailscaleCCF" } ], "tactics": [ - "DefenseEvasion", - "InitialAccess" + "DefenseEvasion" ], "techniques": [ - "T1556", - "T1078" + "T1556" ], "entityMappings": [ { "fieldMappings": [ { - "columnName": "Hostname", - "identifier": "HostName" - } - ], - "entityType": "Host" - }, - { - "fieldMappings": [ - { - "columnName": "User", + "columnName": "ActorLogin", "identifier": "FullName" } ], @@ -5735,11 +5890,11 @@ "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { - "enabled": true, - "lookbackDuration": "1d", - "matchingMethod": "AllEntities", + "groupByEntities": [], "reopenClosedIncident": false, - "groupByEntities": [] + "matchingMethod": "AllEntities", + "lookbackDuration": "1d", + "enabled": true } } } @@ -5764,7 +5919,7 @@ "email": "[variables('_email')]" }, "support": { - "name": "Community", + "name": "Tailscale (CCF)", "tier": "Community", "email": "ccfconnectors.county118@passmail.com", "link": "https://github.com/Azure/Azure-Sentinel/issues" @@ -5780,7 +5935,7 @@ "contentSchemaVersion": "3.0.0", "contentId": "[variables('analyticRuleObject12')._analyticRulecontentId12]", "contentKind": "AnalyticsRule", - "displayName": "Tailscale: Tailnet lock validation failed", + "displayName": "Tailscale: MagicDNS disabled", "contentProductId": "[variables('analyticRuleObject12')._analyticRulecontentProductId12]", "id": "[variables('analyticRuleObject12')._analyticRulecontentProductId12]", "version": "[variables('analyticRuleObject12').analyticRuleVersion12]" @@ -5795,7 +5950,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleDeviceSshNewlyEnabled_AnalyticalRules Analytics Rule with template version 3.0.5", + "description": "TailscaleTailnetLockValidationFailed_AnalyticalRules Analytics Rule with template version 3.0.1", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject13').analyticRuleVersion13]", @@ -5809,13 +5964,13 @@ "kind": "Scheduled", "location": "[parameters('workspace-location')]", "properties": { - "description": "Identifies when Tailscale SSH is enabled on a device that previously did not have it. SSH provides authenticated shell access over the tailnet using Tailscale identity, broadening attack surface if unexpected. Verify and confirm the SSH ACL covers it.", - "displayName": "Tailscale: Device Tailscale SSH newly enabled", + "description": "Identifies tailnet devices with a non-empty TailnetLockError, indicating the device failed tailnet-lock cryptographic validation. Tailnet lock requires new node-keys be co-signed by trusted signers; errors here are the only direct signal of a node-key injection attempt.", + "displayName": "Tailscale: Tailnet lock validation failed", "enabled": false, - "query": "let recent =\n Tailscale_Devices_CL\n | where TimeGenerated > ago(1h)\n | summarize arg_max(TimeGenerated, *) by DeviceId\n | where SshEnabled == true\n | project DeviceId, DeviceName, Hostname, User, Os, ClientVersion, LastSeen;\nlet prior =\n Tailscale_Devices_CL\n | where TimeGenerated between (ago(2d) .. ago(1h))\n | summarize arg_max(TimeGenerated, *) by DeviceId\n | where SshEnabled == true\n | distinct DeviceId;\nrecent\n| join kind=leftanti prior on DeviceId\n| project TimeGenerated = now(), DeviceName, Hostname, User, Os, ClientVersion, LastSeen\n", + "query": "Tailscale_Devices_CL\n| where TimeGenerated > ago(1h)\n| summarize arg_max(TimeGenerated, *) by DeviceId\n| where isnotempty(TailnetLockError)\n| project TimeGenerated, DeviceName, Hostname, User, Os, ClientVersion, TailnetLockError, TailnetLockKey, LastSeen, Authorized\n", "queryFrequency": "PT1H", - "queryPeriod": "P2D", - "severity": "Medium", + "queryPeriod": "PT1H", + "severity": "High", "suppressionDuration": "PT1H", "suppressionEnabled": false, "triggerOperator": "GreaterThan", @@ -5823,19 +5978,19 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscaleCCF", "dataTypes": [ "Tailscale_Devices_CL" - ] + ], + "connectorId": "TailscaleCCF" } ], "tactics": [ - "Persistence", - "LateralMovement" + "DefenseEvasion", + "InitialAccess" ], "techniques": [ - "T1021", - "T1098" + "T1556", + "T1078" ], "entityMappings": [ { @@ -5860,11 +6015,11 @@ "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { - "enabled": true, - "lookbackDuration": "1d", - "matchingMethod": "AllEntities", + "groupByEntities": [], "reopenClosedIncident": false, - "groupByEntities": [] + "matchingMethod": "AllEntities", + "lookbackDuration": "1d", + "enabled": true } } } @@ -5889,7 +6044,7 @@ "email": "[variables('_email')]" }, "support": { - "name": "Community", + "name": "Tailscale (CCF)", "tier": "Community", "email": "ccfconnectors.county118@passmail.com", "link": "https://github.com/Azure/Azure-Sentinel/issues" @@ -5905,7 +6060,7 @@ "contentSchemaVersion": "3.0.0", "contentId": "[variables('analyticRuleObject13')._analyticRulecontentId13]", "contentKind": "AnalyticsRule", - "displayName": "Tailscale: Device Tailscale SSH newly enabled", + "displayName": "Tailscale: Tailnet lock validation failed", "contentProductId": "[variables('analyticRuleObject13')._analyticRulecontentProductId13]", "id": "[variables('analyticRuleObject13')._analyticRulecontentProductId13]", "version": "[variables('analyticRuleObject13').analyticRuleVersion13]" @@ -5920,7 +6075,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleUnauthorizedDeviceConnected_AnalyticalRules Analytics Rule with template version 3.0.5", + "description": "TailscaleDeviceSshNewlyEnabled_AnalyticalRules Analytics Rule with template version 3.0.1", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject14').analyticRuleVersion14]", @@ -5934,13 +6089,13 @@ "kind": "Scheduled", "location": "[parameters('workspace-location')]", "properties": { - "description": "Identifies devices actively connected to the Tailscale control plane (ConnectedToControl=true) but not yet authorized by an admin (Authorized=false). With device approval enabled, persistent entries warrant review; without approval, persistence may indicate a node-key injection attempt.", - "displayName": "Tailscale: Unauthorized device connected to control plane", + "description": "Identifies when Tailscale SSH is enabled on a device that previously did not have it. SSH provides authenticated shell access over the tailnet using Tailscale identity, broadening attack surface if unexpected. Verify and confirm the SSH ACL covers it.", + "displayName": "Tailscale: Device Tailscale SSH newly enabled", "enabled": false, - "query": "Tailscale_Devices_CL\n| where TimeGenerated > ago(1h)\n| summarize arg_max(TimeGenerated, *) by DeviceId\n| where Authorized == false and ConnectedToControl == true\n| project TimeGenerated, DeviceName, Hostname, User, Os, ClientVersion, Created, LastSeen, MachineKey, NodeKey\n", + "query": "let recent =\n Tailscale_Devices_CL\n | where TimeGenerated > ago(1h)\n | summarize arg_max(TimeGenerated, *) by DeviceId\n | where SshEnabled == true\n | project DeviceId, DeviceName, Hostname, User, Os, ClientVersion, LastSeen;\nlet prior =\n Tailscale_Devices_CL\n | where TimeGenerated between (ago(2d) .. ago(1h))\n | summarize arg_max(TimeGenerated, *) by DeviceId\n | where SshEnabled == true\n | distinct DeviceId;\nrecent\n| join kind=leftanti prior on DeviceId\n| project TimeGenerated = now(), DeviceName, Hostname, User, Os, ClientVersion, LastSeen\n", "queryFrequency": "PT1H", - "queryPeriod": "PT1H", - "severity": "High", + "queryPeriod": "P2D", + "severity": "Medium", "suppressionDuration": "PT1H", "suppressionEnabled": false, "triggerOperator": "GreaterThan", @@ -5948,18 +6103,18 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscaleCCF", "dataTypes": [ "Tailscale_Devices_CL" - ] + ], + "connectorId": "TailscaleCCF" } ], "tactics": [ - "InitialAccess", - "Persistence" + "Persistence", + "LateralMovement" ], "techniques": [ - "T1078", + "T1021", "T1098" ], "entityMappings": [ @@ -5985,11 +6140,11 @@ "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { - "enabled": true, - "lookbackDuration": "1d", - "matchingMethod": "AllEntities", + "groupByEntities": [], "reopenClosedIncident": false, - "groupByEntities": [] + "matchingMethod": "AllEntities", + "lookbackDuration": "1d", + "enabled": true } } } @@ -6014,7 +6169,7 @@ "email": "[variables('_email')]" }, "support": { - "name": "Community", + "name": "Tailscale (CCF)", "tier": "Community", "email": "ccfconnectors.county118@passmail.com", "link": "https://github.com/Azure/Azure-Sentinel/issues" @@ -6030,7 +6185,7 @@ "contentSchemaVersion": "3.0.0", "contentId": "[variables('analyticRuleObject14')._analyticRulecontentId14]", "contentKind": "AnalyticsRule", - "displayName": "Tailscale: Unauthorized device connected to control plane", + "displayName": "Tailscale: Device Tailscale SSH newly enabled", "contentProductId": "[variables('analyticRuleObject14')._analyticRulecontentProductId14]", "id": "[variables('analyticRuleObject14')._analyticRulecontentProductId14]", "version": "[variables('analyticRuleObject14').analyticRuleVersion14]" @@ -6045,7 +6200,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleExternalDeviceAdded_AnalyticalRules Analytics Rule with template version 3.0.5", + "description": "TailscaleUnauthorizedDeviceConnected_AnalyticalRules Analytics Rule with template version 3.0.1", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject15').analyticRuleVersion15]", @@ -6059,13 +6214,13 @@ "kind": "Scheduled", "location": "[parameters('workspace-location')]", "properties": { - "description": "Identifies new external (shared-in) devices joining the tailnet that were not present in the prior 24-hour baseline. Each shared-in device expands the trust boundary - confirm the share matches a documented agreement and ACL scope.", - "displayName": "Tailscale: External (shared-in) device added", + "description": "Identifies devices actively connected to the Tailscale control plane (ConnectedToControl=true) but not yet authorized by an admin (Authorized=false). With device approval enabled, persistent entries warrant review; without approval, persistence may indicate a node-key injection attempt.", + "displayName": "Tailscale: Unauthorized device connected to control plane", "enabled": false, - "query": "let recent =\n Tailscale_Devices_CL\n | where TimeGenerated > ago(1h)\n | summarize arg_max(TimeGenerated, *) by DeviceId\n | where IsExternal == true\n | project DeviceId, DeviceName, Hostname, User, Os, ClientVersion, Created, LastSeen, Tags;\nlet prior =\n Tailscale_Devices_CL\n | where TimeGenerated between (ago(2d) .. ago(1h))\n | summarize arg_max(TimeGenerated, *) by DeviceId\n | where IsExternal == true\n | distinct DeviceId;\nrecent\n| join kind=leftanti prior on DeviceId\n| project TimeGenerated = now(), DeviceName, Hostname, User, Os, ClientVersion, Created, LastSeen, Tags\n", + "query": "Tailscale_Devices_CL\n| where TimeGenerated > ago(1h)\n| summarize arg_max(TimeGenerated, *) by DeviceId\n| where Authorized == false and ConnectedToControl == true\n| project TimeGenerated, DeviceName, Hostname, User, Os, ClientVersion, Created, LastSeen, MachineKey, NodeKey\n", "queryFrequency": "PT1H", - "queryPeriod": "P2D", - "severity": "Medium", + "queryPeriod": "PT1H", + "severity": "High", "suppressionDuration": "PT1H", "suppressionEnabled": false, "triggerOperator": "GreaterThan", @@ -6073,17 +6228,19 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscaleCCF", "dataTypes": [ "Tailscale_Devices_CL" - ] + ], + "connectorId": "TailscaleCCF" } ], "tactics": [ - "InitialAccess" + "InitialAccess", + "Persistence" ], "techniques": [ - "T1078" + "T1078", + "T1098" ], "entityMappings": [ { @@ -6108,11 +6265,11 @@ "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { - "enabled": true, - "lookbackDuration": "1d", - "matchingMethod": "AllEntities", + "groupByEntities": [], "reopenClosedIncident": false, - "groupByEntities": [] + "matchingMethod": "AllEntities", + "lookbackDuration": "1d", + "enabled": true } } } @@ -6137,7 +6294,7 @@ "email": "[variables('_email')]" }, "support": { - "name": "Community", + "name": "Tailscale (CCF)", "tier": "Community", "email": "ccfconnectors.county118@passmail.com", "link": "https://github.com/Azure/Azure-Sentinel/issues" @@ -6153,7 +6310,7 @@ "contentSchemaVersion": "3.0.0", "contentId": "[variables('analyticRuleObject15')._analyticRulecontentId15]", "contentKind": "AnalyticsRule", - "displayName": "Tailscale: External (shared-in) device added", + "displayName": "Tailscale: Unauthorized device connected to control plane", "contentProductId": "[variables('analyticRuleObject15')._analyticRulecontentProductId15]", "id": "[variables('analyticRuleObject15')._analyticRulecontentProductId15]", "version": "[variables('analyticRuleObject15').analyticRuleVersion15]" @@ -6168,7 +6325,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscalePremiumUnexpectedExitNodeEgress_AnalyticalRules Analytics Rule with template version 3.0.5", + "description": "TailscaleExternalDeviceAdded_AnalyticalRules Analytics Rule with template version 3.0.1", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject16').analyticRuleVersion16]", @@ -6182,12 +6339,12 @@ "kind": "Scheduled", "location": "[parameters('workspace-location')]", "properties": { - "description": "Identifies when a node sends traffic via an exit node not used in the prior 7-day baseline. First-seen exit destinations from a specific source may indicate unsanctioned egress, a compromised node pivoting, or a policy misconfiguration. Requires Tailscale Premium or Enterprise.", - "displayName": "Tailscale Premium: Unexpected exit-node egress", + "description": "Identifies new external (shared-in) devices joining the tailnet that were not present in the prior 24-hour baseline. Each shared-in device expands the trust boundary - confirm the share matches a documented agreement and ACL scope.", + "displayName": "Tailscale: External (shared-in) device added", "enabled": false, - "query": "let baseline = 7d;\nlet recent = 1h;\nlet recentEgress =\n Tailscale_Network_CL\n | where TimeGenerated > ago(recent)\n | where array_length(ExitTraffic) > 0\n | mv-expand t = ExitTraffic\n | extend Src = tostring(t.src), ExitDst = tostring(t.dst), SrcNodeName = tostring(SrcNode.name), Bytes = tolong(t.txBytes) + tolong(t.rxBytes)\n | summarize FirstSeen = min(TimeGenerated), TotalBytes = sum(Bytes) by NodeId, SrcNodeName, Src, ExitDst;\nlet baselineEgress =\n Tailscale_Network_CL\n | where TimeGenerated between (ago(baseline + recent) .. ago(recent))\n | where array_length(ExitTraffic) > 0\n | mv-expand t = ExitTraffic\n | extend Src = tostring(t.src), ExitDst = tostring(t.dst)\n | distinct NodeId, Src, ExitDst;\nrecentEgress\n| join kind=leftanti baselineEgress on NodeId, Src, ExitDst\n| extend TotalMB = round(TotalBytes / 1024.0 / 1024.0, 2)\n", + "query": "let recent =\n Tailscale_Devices_CL\n | where TimeGenerated > ago(1h)\n | summarize arg_max(TimeGenerated, *) by DeviceId\n | where IsExternal == true\n | project DeviceId, DeviceName, Hostname, User, Os, ClientVersion, Created, LastSeen, Tags;\nlet prior =\n Tailscale_Devices_CL\n | where TimeGenerated between (ago(2d) .. ago(1h))\n | summarize arg_max(TimeGenerated, *) by DeviceId\n | where IsExternal == true\n | distinct DeviceId;\nrecent\n| join kind=leftanti prior on DeviceId\n| project TimeGenerated = now(), DeviceName, Hostname, User, Os, ClientVersion, Created, LastSeen, Tags\n", "queryFrequency": "PT1H", - "queryPeriod": "PT1H", + "queryPeriod": "P2D", "severity": "Medium", "suppressionDuration": "PT1H", "suppressionEnabled": false, @@ -6196,25 +6353,23 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscalePremiumCCF", "dataTypes": [ - "Tailscale_Network_CL" - ] + "Tailscale_Devices_CL" + ], + "connectorId": "TailscaleCCF" } ], "tactics": [ - "CommandAndControl", - "Exfiltration" + "InitialAccess" ], "techniques": [ - "T1090", - "T1041" + "T1078" ], "entityMappings": [ { "fieldMappings": [ { - "columnName": "SrcNodeName", + "columnName": "Hostname", "identifier": "HostName" } ], @@ -6223,21 +6378,21 @@ { "fieldMappings": [ { - "columnName": "Src", - "identifier": "Address" + "columnName": "User", + "identifier": "FullName" } ], - "entityType": "IP" + "entityType": "Account" } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { - "enabled": true, - "lookbackDuration": "5h", - "matchingMethod": "AllEntities", + "groupByEntities": [], "reopenClosedIncident": false, - "groupByEntities": [] + "matchingMethod": "AllEntities", + "lookbackDuration": "1d", + "enabled": true } } } @@ -6262,7 +6417,7 @@ "email": "[variables('_email')]" }, "support": { - "name": "Community", + "name": "Tailscale (CCF)", "tier": "Community", "email": "ccfconnectors.county118@passmail.com", "link": "https://github.com/Azure/Azure-Sentinel/issues" @@ -6278,7 +6433,7 @@ "contentSchemaVersion": "3.0.0", "contentId": "[variables('analyticRuleObject16')._analyticRulecontentId16]", "contentKind": "AnalyticsRule", - "displayName": "Tailscale Premium: Unexpected exit-node egress", + "displayName": "Tailscale: External (shared-in) device added", "contentProductId": "[variables('analyticRuleObject16')._analyticRulecontentProductId16]", "id": "[variables('analyticRuleObject16')._analyticRulecontentProductId16]", "version": "[variables('analyticRuleObject16').analyticRuleVersion16]" @@ -6293,7 +6448,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscalePremiumLargeOutboundTransfer_AnalyticalRules Analytics Rule with template version 3.0.5", + "description": "TailscalePremiumUnexpectedExitNodeEgress_AnalyticalRules Analytics Rule with template version 3.0.1", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject17').analyticRuleVersion17]", @@ -6307,10 +6462,10 @@ "kind": "Scheduled", "location": "[parameters('workspace-location')]", "properties": { - "description": "Identifies when a single src-dst pair transfers more than 100 MB over the tailnet within a 1-hour window. Large bursts can indicate data staging, exfiltration, or a misconfigured backup. Requires Tailscale Premium or Enterprise.", - "displayName": "Tailscale Premium: Large outbound transfer over tailnet", + "description": "Identifies when a node sends traffic via an exit node not used in the prior 7-day baseline. First-seen exit destinations from a specific source may indicate unsanctioned egress, a compromised node pivoting, or a policy misconfiguration. Requires Tailscale Premium or Enterprise.", + "displayName": "Tailscale Premium: Unexpected exit-node egress", "enabled": false, - "query": "let bytesThreshold = 100 * 1024 * 1024;\nTailscale_Network_CL\n| where TimeGenerated > ago(1h)\n| mv-expand t = VirtualTraffic\n| extend Src = tostring(t.src), Dst = tostring(t.dst), Proto = toint(t.proto), TxBytes = tolong(t.txBytes), RxBytes = tolong(t.rxBytes), Bytes = tolong(t.txBytes) + tolong(t.rxBytes)\n| summarize TotalBytes = sum(Bytes), TotalPackets = sum(tolong(t.txPkts) + tolong(t.rxPkts)), SrcNodeName = take_any(tostring(SrcNode.name)) by NodeId, Src, Dst, Proto\n| where TotalBytes > bytesThreshold\n| extend TotalMB = round(TotalBytes / 1024.0 / 1024.0, 2)\n| order by TotalBytes desc\n", + "query": "let baseline = 7d;\nlet recent = 1h;\nlet recentEgress =\n Tailscale_Network_CL\n | where TimeGenerated > ago(recent)\n | where HasExitTraffic\n | mv-expand t = ExitTraffic\n | extend Src = tostring(t.src), ExitDst = tostring(t.dst), Bytes = tolong(t.txBytes) + tolong(t.rxBytes)\n | summarize FirstSeen = min(TimeGenerated), TotalBytes = sum(Bytes) by NodeId, SrcNodeName, SrcUser, SrcOs, Src, ExitDst;\nlet baselineEgress =\n Tailscale_Network_CL\n | where TimeGenerated between (ago(baseline + recent) .. ago(recent))\n | where HasExitTraffic\n | mv-expand t = ExitTraffic\n | extend Src = tostring(t.src), ExitDst = tostring(t.dst)\n | distinct NodeId, Src, ExitDst;\nrecentEgress\n| join kind=leftanti baselineEgress on NodeId, Src, ExitDst\n| extend TotalMB = round(TotalBytes / 1024.0 / 1024.0, 2)\n", "queryFrequency": "PT1H", "queryPeriod": "PT1H", "severity": "Medium", @@ -6321,19 +6476,19 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscalePremiumCCF", "dataTypes": [ "Tailscale_Network_CL" - ] + ], + "connectorId": "TailscalePremiumCCF" } ], "tactics": [ - "Exfiltration", - "Collection" + "CommandAndControl", + "Exfiltration" ], "techniques": [ - "T1041", - "T1020" + "T1090", + "T1041" ], "entityMappings": [ { @@ -6345,6 +6500,15 @@ ], "entityType": "Host" }, + { + "fieldMappings": [ + { + "columnName": "SrcUser", + "identifier": "FullName" + } + ], + "entityType": "Account" + }, { "fieldMappings": [ { @@ -6358,11 +6522,11 @@ "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { - "enabled": true, - "lookbackDuration": "5h", - "matchingMethod": "AllEntities", + "groupByEntities": [], "reopenClosedIncident": false, - "groupByEntities": [] + "matchingMethod": "AllEntities", + "lookbackDuration": "5h", + "enabled": true } } } @@ -6387,7 +6551,7 @@ "email": "[variables('_email')]" }, "support": { - "name": "Community", + "name": "Tailscale (CCF)", "tier": "Community", "email": "ccfconnectors.county118@passmail.com", "link": "https://github.com/Azure/Azure-Sentinel/issues" @@ -6403,7 +6567,7 @@ "contentSchemaVersion": "3.0.0", "contentId": "[variables('analyticRuleObject17')._analyticRulecontentId17]", "contentKind": "AnalyticsRule", - "displayName": "Tailscale Premium: Large outbound transfer over tailnet", + "displayName": "Tailscale Premium: Unexpected exit-node egress", "contentProductId": "[variables('analyticRuleObject17')._analyticRulecontentProductId17]", "id": "[variables('analyticRuleObject17')._analyticRulecontentProductId17]", "version": "[variables('analyticRuleObject17').analyticRuleVersion17]" @@ -6418,7 +6582,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscalePremiumBeaconingDetected_AnalyticalRules Analytics Rule with template version 3.0.5", + "description": "TailscalePremiumLargeOutboundTransfer_AnalyticalRules Analytics Rule with template version 3.0.1", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject18').analyticRuleVersion18]", @@ -6432,12 +6596,12 @@ "kind": "Scheduled", "location": "[parameters('workspace-location')]", "properties": { - "description": "Identifies when flows between a src-dst pair recur at a regular interval (80%+ of inter-flow gaps cluster on the same delta over 10+ flows). Signature of C2 beaconing or scheduled exfiltration. Requires Tailscale Premium or Enterprise.", - "displayName": "Tailscale Premium: Network flow beaconing detected", + "description": "Identifies when a single src-dst pair transfers more than 100 MB over the tailnet within a 1-hour window. Large bursts can indicate data staging, exfiltration, or a misconfigured backup. Requires Tailscale Premium or Enterprise.", + "displayName": "Tailscale Premium: Large outbound transfer over tailnet", "enabled": false, - "query": "let lookback = 2d;\nlet minFlows = 10;\nlet beaconPercentThreshold = 80.0;\nTailscale_Network_CL\n| where TimeGenerated > ago(lookback)\n| mv-expand t = VirtualTraffic\n| extend Src = tostring(t.src), Dst = tostring(t.dst), Proto = toint(t.proto)\n| project TimeGenerated, Src, Dst, Proto\n| sort by Src asc, Dst asc, Proto asc, TimeGenerated asc\n| serialize\n| extend NextTime = next(TimeGenerated), NextSrc = next(Src), NextDst = next(Dst), NextProto = next(Proto)\n| where Src == NextSrc and Dst == NextDst and Proto == NextProto\n| extend DeltaSec = datetime_diff('second', NextTime, TimeGenerated)\n| where DeltaSec > 5\n| summarize DeltaCount = count() by Src, Dst, Proto, DeltaSec\n| summarize (MostFrequentDeltaCount, MostFrequentDeltaSec) = arg_max(DeltaCount, DeltaSec), TotalFlows = sum(DeltaCount) by Src, Dst, Proto\n| where TotalFlows >= minFlows\n| extend BeaconPercent = round(MostFrequentDeltaCount * 100.0 / TotalFlows, 1)\n| where BeaconPercent >= beaconPercentThreshold\n", + "query": "let bytesThreshold = 100 * 1024 * 1024;\nTailscale_Network_CL\n| where TimeGenerated > ago(1h)\n| where HasVirtualTraffic\n| mv-expand t = VirtualTraffic\n| extend Src = tostring(t.src), Dst = tostring(t.dst), Proto = toint(t.proto), Bytes = tolong(t.txBytes) + tolong(t.rxBytes), Pkts = tolong(t.txPkts) + tolong(t.rxPkts)\n| summarize TotalBytes = sum(Bytes), TotalPackets = sum(Pkts) by NodeId, SrcNodeName, SrcUser, DstNodeName, DstUser, Src, Dst, Proto\n| where TotalBytes > bytesThreshold\n| extend TotalMB = round(TotalBytes / 1024.0 / 1024.0, 2)\n| order by TotalBytes desc\n", "queryFrequency": "PT1H", - "queryPeriod": "P2D", + "queryPeriod": "PT1H", "severity": "Medium", "suppressionDuration": "PT1H", "suppressionEnabled": false, @@ -6446,22 +6610,48 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscalePremiumCCF", "dataTypes": [ "Tailscale_Network_CL" - ] + ], + "connectorId": "TailscalePremiumCCF" } ], "tactics": [ - "CommandAndControl", - "Exfiltration" + "Exfiltration", + "Collection" ], "techniques": [ - "T1071", - "T1095", - "T1029" + "T1041", + "T1020" ], "entityMappings": [ + { + "fieldMappings": [ + { + "columnName": "SrcNodeName", + "identifier": "HostName" + } + ], + "entityType": "Host" + }, + { + "fieldMappings": [ + { + "columnName": "DstNodeName", + "identifier": "HostName" + } + ], + "entityType": "Host" + }, + { + "fieldMappings": [ + { + "columnName": "SrcUser", + "identifier": "FullName" + } + ], + "entityType": "Account" + }, { "fieldMappings": [ { @@ -6475,11 +6665,11 @@ "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { - "enabled": true, - "lookbackDuration": "1d", - "matchingMethod": "AllEntities", + "groupByEntities": [], "reopenClosedIncident": false, - "groupByEntities": [] + "matchingMethod": "AllEntities", + "lookbackDuration": "5h", + "enabled": true } } } @@ -6504,7 +6694,7 @@ "email": "[variables('_email')]" }, "support": { - "name": "Community", + "name": "Tailscale (CCF)", "tier": "Community", "email": "ccfconnectors.county118@passmail.com", "link": "https://github.com/Azure/Azure-Sentinel/issues" @@ -6520,7 +6710,7 @@ "contentSchemaVersion": "3.0.0", "contentId": "[variables('analyticRuleObject18')._analyticRulecontentId18]", "contentKind": "AnalyticsRule", - "displayName": "Tailscale Premium: Network flow beaconing detected", + "displayName": "Tailscale Premium: Large outbound transfer over tailnet", "contentProductId": "[variables('analyticRuleObject18')._analyticRulecontentProductId18]", "id": "[variables('analyticRuleObject18')._analyticRulecontentProductId18]", "version": "[variables('analyticRuleObject18').analyticRuleVersion18]" @@ -6535,7 +6725,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscalePremiumMassFanOut_AnalyticalRules Analytics Rule with template version 3.0.5", + "description": "TailscalePremiumBeaconingDetected_AnalyticalRules Analytics Rule with template version 3.0.1", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject19').analyticRuleVersion19]", @@ -6549,13 +6739,13 @@ "kind": "Scheduled", "location": "[parameters('workspace-location')]", "properties": { - "description": "Identifies when a single node initiates flows to 25 or more unique destinations within a 15-minute window. Sudden fan-out is consistent with port scanning, lateral discovery, or worm-style propagation. Requires Tailscale Premium or Enterprise.", - "displayName": "Tailscale Premium: Mass fan-out from single node", + "description": "Identifies when flows between a src-dst pair recur at a regular interval (80%+ of inter-flow gaps cluster on the same delta over 10+ flows). Signature of C2 beaconing or scheduled exfiltration. Requires Tailscale Premium or Enterprise.", + "displayName": "Tailscale Premium: Network flow beaconing detected", "enabled": false, - "query": "let dstThreshold = 25;\nTailscale_Network_CL\n| where TimeGenerated > ago(15m)\n| mv-expand t = VirtualTraffic\n| extend Src = tostring(t.src), Dst = tostring(t.dst)\n| summarize UniqueDestinations = dcount(Dst), SrcNodeName = take_any(tostring(SrcNode.name)), TopDestinations = make_set(Dst, 25) by NodeId, Src\n| where UniqueDestinations >= dstThreshold\n| order by UniqueDestinations desc\n", - "queryFrequency": "PT15M", - "queryPeriod": "PT15M", - "severity": "High", + "query": "let lookback = 2d;\nlet minFlows = 10;\nlet beaconPercentThreshold = 80.0;\nTailscale_Network_CL\n| where TimeGenerated > ago(lookback)\n| where HasVirtualTraffic\n| mv-expand t = VirtualTraffic\n| extend Src = tostring(t.src), Dst = tostring(t.dst), Proto = toint(t.proto)\n| project TimeGenerated, Src, Dst, Proto, SrcNodeName, SrcUser, DstNodeName, DstUser\n| sort by Src asc, Dst asc, Proto asc, TimeGenerated asc\n| serialize\n| extend NextTime = next(TimeGenerated), NextSrc = next(Src), NextDst = next(Dst), NextProto = next(Proto)\n| where Src == NextSrc and Dst == NextDst and Proto == NextProto\n| extend DeltaSec = datetime_diff('second', NextTime, TimeGenerated)\n| where DeltaSec > 5\n| summarize DeltaCount = count() by Src, Dst, Proto, DeltaSec, SrcNodeName, SrcUser, DstNodeName, DstUser\n| summarize (MostFrequentDeltaCount, MostFrequentDeltaSec) = arg_max(DeltaCount, DeltaSec), TotalFlows = sum(DeltaCount) by Src, Dst, Proto, SrcNodeName, SrcUser, DstNodeName, DstUser\n| where TotalFlows >= minFlows\n| extend BeaconPercent = round(MostFrequentDeltaCount * 100.0 / TotalFlows, 1)\n| where BeaconPercent >= beaconPercentThreshold\n", + "queryFrequency": "PT1H", + "queryPeriod": "P2D", + "severity": "Medium", "suppressionDuration": "PT1H", "suppressionEnabled": false, "triggerOperator": "GreaterThan", @@ -6563,20 +6753,20 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscalePremiumCCF", "dataTypes": [ "Tailscale_Network_CL" - ] + ], + "connectorId": "TailscalePremiumCCF" } ], "tactics": [ - "Discovery", - "LateralMovement" + "CommandAndControl", + "Exfiltration" ], "techniques": [ - "T1018", - "T1021", - "T1046" + "T1071", + "T1095", + "T1029" ], "entityMappings": [ { @@ -6588,6 +6778,24 @@ ], "entityType": "Host" }, + { + "fieldMappings": [ + { + "columnName": "DstNodeName", + "identifier": "HostName" + } + ], + "entityType": "Host" + }, + { + "fieldMappings": [ + { + "columnName": "SrcUser", + "identifier": "FullName" + } + ], + "entityType": "Account" + }, { "fieldMappings": [ { @@ -6601,11 +6809,11 @@ "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { - "enabled": true, - "lookbackDuration": "5h", - "matchingMethod": "AllEntities", + "groupByEntities": [], "reopenClosedIncident": false, - "groupByEntities": [] + "matchingMethod": "AllEntities", + "lookbackDuration": "1d", + "enabled": true } } } @@ -6630,7 +6838,7 @@ "email": "[variables('_email')]" }, "support": { - "name": "Community", + "name": "Tailscale (CCF)", "tier": "Community", "email": "ccfconnectors.county118@passmail.com", "link": "https://github.com/Azure/Azure-Sentinel/issues" @@ -6646,7 +6854,7 @@ "contentSchemaVersion": "3.0.0", "contentId": "[variables('analyticRuleObject19')._analyticRulecontentId19]", "contentKind": "AnalyticsRule", - "displayName": "Tailscale Premium: Mass fan-out from single node", + "displayName": "Tailscale Premium: Network flow beaconing detected", "contentProductId": "[variables('analyticRuleObject19')._analyticRulecontentProductId19]", "id": "[variables('analyticRuleObject19')._analyticRulecontentProductId19]", "version": "[variables('analyticRuleObject19').analyticRuleVersion19]" @@ -6661,7 +6869,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscalePremiumSubnetRouterThroughputAnomaly_AnalyticalRules Analytics Rule with template version 3.0.5", + "description": "TailscalePremiumMassFanOut_AnalyticalRules Analytics Rule with template version 3.0.1", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject20').analyticRuleVersion20]", @@ -6675,13 +6883,13 @@ "kind": "Scheduled", "location": "[parameters('workspace-location')]", "properties": { - "description": "Identifies when a subnet router (gateway node bridging the tailnet to an on-prem or cloud subnet) handles 3x or more its 7-day baseline traffic in the last hour. Spikes can indicate exfiltration or scanning. Requires Tailscale Premium or Enterprise.", - "displayName": "Tailscale Premium: Subnet router throughput anomaly", + "description": "Identifies when a single node initiates flows to 25 or more unique destinations within a 15-minute window. Sudden fan-out is consistent with port scanning, lateral discovery, or worm-style propagation. Requires Tailscale Premium or Enterprise.", + "displayName": "Tailscale Premium: Mass fan-out from single node", "enabled": false, - "query": "let baselineDays = 7d;\nlet recent = 1h;\nlet multiplier = 3.0;\nlet recentTraffic =\n Tailscale_Network_CL\n | where TimeGenerated > ago(recent)\n | where array_length(SubnetTraffic) > 0\n | mv-expand t = SubnetTraffic\n | extend Bytes = tolong(t.txBytes) + tolong(t.rxBytes), SrcNodeName = tostring(SrcNode.name)\n | summarize RecentBytes = sum(Bytes) by NodeId, SrcNodeName;\nlet baseline =\n Tailscale_Network_CL\n | where TimeGenerated between (ago(baselineDays + recent) .. ago(recent))\n | where array_length(SubnetTraffic) > 0\n | mv-expand t = SubnetTraffic\n | extend Bytes = tolong(t.txBytes) + tolong(t.rxBytes)\n | summarize TotalBaselineBytes = sum(Bytes) by NodeId\n | extend BaselineHourlyBytes = TotalBaselineBytes / 168.0;\nrecentTraffic\n| join kind=inner baseline on NodeId\n| where RecentBytes > BaselineHourlyBytes * multiplier\n| extend Multiplier = round(RecentBytes / BaselineHourlyBytes, 1), RecentMB = round(RecentBytes / 1024.0 / 1024.0, 2), BaselineHourlyMB = round(BaselineHourlyBytes / 1024.0 / 1024.0, 2)\n| project NodeId, SrcNodeName, RecentMB, BaselineHourlyMB, Multiplier\n| order by Multiplier desc\n", - "queryFrequency": "PT1H", - "queryPeriod": "P8D", - "severity": "Low", + "query": "let dstThreshold = 25;\nTailscale_Network_CL\n| where TimeGenerated > ago(15m)\n| where HasVirtualTraffic\n| mv-expand t = VirtualTraffic\n| extend Src = tostring(t.src), Dst = tostring(t.dst)\n| summarize UniqueDestinations = dcount(Dst), TopDestinations = make_set(Dst, 25) by NodeId, SrcNodeName, SrcUser, SrcOs, SrcTags=tostring(SrcTags), Src\n| where UniqueDestinations >= dstThreshold\n| order by UniqueDestinations desc\n", + "queryFrequency": "PT15M", + "queryPeriod": "PT15M", + "severity": "High", "suppressionDuration": "PT1H", "suppressionEnabled": false, "triggerOperator": "GreaterThan", @@ -6689,19 +6897,20 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscalePremiumCCF", "dataTypes": [ "Tailscale_Network_CL" - ] + ], + "connectorId": "TailscalePremiumCCF" } ], "tactics": [ - "Exfiltration", - "CommandAndControl" + "Discovery", + "LateralMovement" ], "techniques": [ - "T1572", - "T1041" + "T1018", + "T1021", + "T1046" ], "entityMappings": [ { @@ -6712,16 +6921,34 @@ } ], "entityType": "Host" + }, + { + "fieldMappings": [ + { + "columnName": "SrcUser", + "identifier": "FullName" + } + ], + "entityType": "Account" + }, + { + "fieldMappings": [ + { + "columnName": "Src", + "identifier": "Address" + } + ], + "entityType": "IP" } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { - "enabled": true, - "lookbackDuration": "1d", - "matchingMethod": "AllEntities", + "groupByEntities": [], "reopenClosedIncident": false, - "groupByEntities": [] + "matchingMethod": "AllEntities", + "lookbackDuration": "5h", + "enabled": true } } } @@ -6746,7 +6973,7 @@ "email": "[variables('_email')]" }, "support": { - "name": "Community", + "name": "Tailscale (CCF)", "tier": "Community", "email": "ccfconnectors.county118@passmail.com", "link": "https://github.com/Azure/Azure-Sentinel/issues" @@ -6762,7 +6989,7 @@ "contentSchemaVersion": "3.0.0", "contentId": "[variables('analyticRuleObject20')._analyticRulecontentId20]", "contentKind": "AnalyticsRule", - "displayName": "Tailscale Premium: Subnet router throughput anomaly", + "displayName": "Tailscale Premium: Mass fan-out from single node", "contentProductId": "[variables('analyticRuleObject20')._analyticRulecontentProductId20]", "id": "[variables('analyticRuleObject20')._analyticRulecontentProductId20]", "version": "[variables('analyticRuleObject20').analyticRuleVersion20]" @@ -6777,7 +7004,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscalePremiumPostureIntegrationDisabled_AnalyticalRules Analytics Rule with template version 3.0.5", + "description": "TailscalePremiumSubnetRouterThroughputAnomaly_AnalyticalRules Analytics Rule with template version 3.0.1", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject21').analyticRuleVersion21]", @@ -6791,13 +7018,13 @@ "kind": "Scheduled", "location": "[parameters('workspace-location')]", "properties": { - "description": "Identifies when a device-posture integration is disabled or removed from the tailnet. Posture integrations enforce device compliance; removing one disables that enforcement and increases blast radius for compromised endpoints. Requires Tailscale Premium or Enterprise.", - "displayName": "Tailscale Premium: Posture integration disabled or removed", + "description": "Identifies when a subnet router (gateway node bridging the tailnet to an on-prem or cloud subnet) handles 3x or more its 7-day baseline traffic in the last hour. Spikes can indicate exfiltration or scanning. Requires Tailscale Premium or Enterprise.", + "displayName": "Tailscale Premium: Subnet router throughput anomaly", "enabled": false, - "query": "Tailscale_Audit_CL\n| where Action in (\"DELETE\", \"UPDATE\")\n| where tostring(Target.type) == \"POSTURE_INTEGRATION\"\n or tostring(Target.type) startswith \"POSTURE\"\n| extend ActorLogin = tostring(Actor.loginName)\n| extend Provider = tostring(Target.name)\n| project TimeGenerated, ActorLogin, Action, Provider, Target, Old, New, Origin\n", - "queryFrequency": "PT15M", - "queryPeriod": "PT15M", - "severity": "High", + "query": "let baselineDays = 7d;\nlet recent = 1h;\nlet multiplier = 3.0;\nlet recentTraffic =\n Tailscale_Network_CL\n | where TimeGenerated > ago(recent)\n | where HasSubnetTraffic\n | mv-expand t = SubnetTraffic\n | extend Bytes = tolong(t.txBytes) + tolong(t.rxBytes)\n | summarize RecentBytes = sum(Bytes) by NodeId, SrcNodeName, SrcUser, SrcOs, SrcTags=tostring(SrcTags);\nlet baseline =\n Tailscale_Network_CL\n | where TimeGenerated between (ago(baselineDays + recent) .. ago(recent))\n | where HasSubnetTraffic\n | mv-expand t = SubnetTraffic\n | extend Bytes = tolong(t.txBytes) + tolong(t.rxBytes)\n | summarize TotalBaselineBytes = sum(Bytes) by NodeId\n | extend BaselineHourlyBytes = TotalBaselineBytes / 168.0;\nrecentTraffic\n| join kind=inner baseline on NodeId\n| where RecentBytes > BaselineHourlyBytes * multiplier\n| extend Multiplier = round(RecentBytes / BaselineHourlyBytes, 1), RecentMB = round(RecentBytes / 1024.0 / 1024.0, 2), BaselineHourlyMB = round(BaselineHourlyBytes / 1024.0 / 1024.0, 2)\n| project NodeId, SrcNodeName, SrcUser, SrcOs, SrcTags, RecentMB, BaselineHourlyMB, Multiplier\n| order by Multiplier desc\n", + "queryFrequency": "PT1H", + "queryPeriod": "P8D", + "severity": "Low", "suppressionDuration": "PT1H", "suppressionEnabled": false, "triggerOperator": "GreaterThan", @@ -6805,25 +7032,34 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscalePremiumCCF", "dataTypes": [ - "Tailscale_Audit_CL" - ] + "Tailscale_Network_CL" + ], + "connectorId": "TailscalePremiumCCF" } ], "tactics": [ - "DefenseEvasion", - "Persistence" + "Exfiltration", + "CommandAndControl" ], "techniques": [ - "T1562", - "T1556" + "T1572", + "T1041" ], "entityMappings": [ { "fieldMappings": [ { - "columnName": "ActorLogin", + "columnName": "SrcNodeName", + "identifier": "HostName" + } + ], + "entityType": "Host" + }, + { + "fieldMappings": [ + { + "columnName": "SrcUser", "identifier": "FullName" } ], @@ -6833,11 +7069,11 @@ "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { - "enabled": true, - "lookbackDuration": "1d", - "matchingMethod": "AllEntities", + "groupByEntities": [], "reopenClosedIncident": false, - "groupByEntities": [] + "matchingMethod": "AllEntities", + "lookbackDuration": "1d", + "enabled": true } } } @@ -6862,7 +7098,7 @@ "email": "[variables('_email')]" }, "support": { - "name": "Community", + "name": "Tailscale (CCF)", "tier": "Community", "email": "ccfconnectors.county118@passmail.com", "link": "https://github.com/Azure/Azure-Sentinel/issues" @@ -6878,7 +7114,7 @@ "contentSchemaVersion": "3.0.0", "contentId": "[variables('analyticRuleObject21')._analyticRulecontentId21]", "contentKind": "AnalyticsRule", - "displayName": "Tailscale Premium: Posture integration disabled or removed", + "displayName": "Tailscale Premium: Subnet router throughput anomaly", "contentProductId": "[variables('analyticRuleObject21')._analyticRulecontentProductId21]", "id": "[variables('analyticRuleObject21')._analyticRulecontentProductId21]", "version": "[variables('analyticRuleObject21').analyticRuleVersion21]" @@ -6893,7 +7129,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscalePremiumNewPostureIntegration_AnalyticalRules Analytics Rule with template version 3.0.5", + "description": "TailscalePremiumPostureIntegrationDisabled_AnalyticalRules Analytics Rule with template version 3.0.1", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject22').analyticRuleVersion22]", @@ -6907,13 +7143,13 @@ "kind": "Scheduled", "location": "[parameters('workspace-location')]", "properties": { - "description": "Identifies when a new device-posture integration is added to the tailnet (Jamf, Kandji, Intune, Kolide, Defender for Endpoint, CrowdStrike, SentinelOne, etc.). Unexpected additions may indicate an attacker establishing a control plane or bypassing compliance gates. Requires Tailscale Premium or Enterprise.", - "displayName": "Tailscale Premium: New posture integration added", + "description": "Identifies when a device-posture integration is disabled or removed from the tailnet. Posture integrations enforce device compliance; removing one disables that enforcement and increases blast radius for compromised endpoints. Requires Tailscale Premium or Enterprise.", + "displayName": "Tailscale Premium: Posture integration disabled or removed", "enabled": false, - "query": "Tailscale_Audit_CL\n| where Action == \"CREATE\"\n| where tostring(Target.type) == \"POSTURE_INTEGRATION\"\n or tostring(Target.type) startswith \"POSTURE\"\n| extend ActorLogin = tostring(Actor.loginName)\n| extend Provider = tostring(Target.name)\n| project TimeGenerated, ActorLogin, Provider, Target, New, Origin\n", + "query": "Tailscale_Audit_CL\n| where Action in (\"DELETE\", \"UPDATE\")\n| where tostring(Target.type) == \"POSTURE_INTEGRATION\"\n or tostring(Target.type) startswith \"POSTURE\"\n| extend ActorLogin = tostring(Actor.loginName)\n| extend Provider = tostring(Target.name)\n| project TimeGenerated, ActorLogin, Action, Provider, Target, Old, New, Origin\n", "queryFrequency": "PT15M", "queryPeriod": "PT15M", - "severity": "Medium", + "severity": "High", "suppressionDuration": "PT1H", "suppressionEnabled": false, "triggerOperator": "GreaterThan", @@ -6921,17 +7157,19 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscalePremiumCCF", "dataTypes": [ "Tailscale_Audit_CL" - ] + ], + "connectorId": "TailscalePremiumCCF" } ], "tactics": [ + "DefenseEvasion", "Persistence" ], "techniques": [ - "T1098" + "T1562", + "T1556" ], "entityMappings": [ { @@ -6947,11 +7185,11 @@ "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { - "enabled": true, - "lookbackDuration": "1d", - "matchingMethod": "AllEntities", + "groupByEntities": [], "reopenClosedIncident": false, - "groupByEntities": [] + "matchingMethod": "AllEntities", + "lookbackDuration": "1d", + "enabled": true } } } @@ -6976,7 +7214,7 @@ "email": "[variables('_email')]" }, "support": { - "name": "Community", + "name": "Tailscale (CCF)", "tier": "Community", "email": "ccfconnectors.county118@passmail.com", "link": "https://github.com/Azure/Azure-Sentinel/issues" @@ -6992,7 +7230,7 @@ "contentSchemaVersion": "3.0.0", "contentId": "[variables('analyticRuleObject22')._analyticRulecontentId22]", "contentKind": "AnalyticsRule", - "displayName": "Tailscale Premium: New posture integration added", + "displayName": "Tailscale Premium: Posture integration disabled or removed", "contentProductId": "[variables('analyticRuleObject22')._analyticRulecontentProductId22]", "id": "[variables('analyticRuleObject22')._analyticRulecontentProductId22]", "version": "[variables('analyticRuleObject22').analyticRuleVersion22]" @@ -7001,42 +7239,704 @@ { "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", "apiVersion": "2023-04-01-preview", - "name": "[variables('huntingQueryObject1').huntingQueryTemplateSpecName1]", + "name": "[variables('analyticRuleObject23').analyticRuleTemplateSpecName23]", "location": "[parameters('workspace-location')]", "dependsOn": [ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleFirstSeenActor_HuntingQueries Hunting Query with template version 3.0.5", + "description": "TailscalePremiumNewPostureIntegration_AnalyticalRules Analytics Rule with template version 3.0.1", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "[variables('huntingQueryObject1').huntingQueryVersion1]", + "contentVersion": "[variables('analyticRuleObject23').analyticRuleVersion23]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.SecurityInsights/AlertRuleTemplates", + "name": "[variables('analyticRuleObject23')._analyticRulecontentId23]", + "apiVersion": "2023-02-01-preview", + "kind": "Scheduled", + "location": "[parameters('workspace-location')]", + "properties": { + "description": "Identifies when a new device-posture integration is added to the tailnet (Jamf, Kandji, Intune, Kolide, Defender for Endpoint, CrowdStrike, SentinelOne, etc.). Unexpected additions may indicate an attacker establishing a control plane or bypassing compliance gates. Requires Tailscale Premium or Enterprise.", + "displayName": "Tailscale Premium: New posture integration added", + "enabled": false, + "query": "Tailscale_Audit_CL\n| where Action == \"CREATE\"\n| where tostring(Target.type) == \"POSTURE_INTEGRATION\"\n or tostring(Target.type) startswith \"POSTURE\"\n| extend ActorLogin = tostring(Actor.loginName)\n| extend Provider = tostring(Target.name)\n| project TimeGenerated, ActorLogin, Provider, Target, New, Origin\n", + "queryFrequency": "PT15M", + "queryPeriod": "PT15M", + "severity": "Medium", + "suppressionDuration": "PT1H", + "suppressionEnabled": false, + "triggerOperator": "GreaterThan", + "triggerThreshold": 0, + "status": "Available", + "requiredDataConnectors": [ + { + "dataTypes": [ + "Tailscale_Audit_CL" + ], + "connectorId": "TailscalePremiumCCF" + } + ], + "tactics": [ + "Persistence" + ], + "techniques": [ + "T1098" + ], + "entityMappings": [ + { + "fieldMappings": [ + { + "columnName": "ActorLogin", + "identifier": "FullName" + } + ], + "entityType": "Account" + } + ], + "incidentConfiguration": { + "createIncident": true, + "groupingConfiguration": { + "groupByEntities": [], + "reopenClosedIncident": false, + "matchingMethod": "AllEntities", + "lookbackDuration": "1d", + "enabled": true + } + } + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('AnalyticsRule-', last(split(variables('analyticRuleObject23').analyticRuleId23,'/'))))]", + "properties": { + "description": "Tailscale (CCF) Analytics Rule 23", + "parentId": "[variables('analyticRuleObject23').analyticRuleId23]", + "contentId": "[variables('analyticRuleObject23')._analyticRulecontentId23]", + "kind": "AnalyticsRule", + "version": "[variables('analyticRuleObject23').analyticRuleVersion23]", + "source": { + "kind": "Solution", + "name": "Tailscale (CCF)", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "noodlemctwoodle", + "email": "[variables('_email')]" + }, + "support": { + "name": "Tailscale (CCF)", + "tier": "Community", + "email": "ccfconnectors.county118@passmail.com", + "link": "https://github.com/Azure/Azure-Sentinel/issues" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('analyticRuleObject23')._analyticRulecontentId23]", + "contentKind": "AnalyticsRule", + "displayName": "Tailscale Premium: New posture integration added", + "contentProductId": "[variables('analyticRuleObject23')._analyticRulecontentProductId23]", + "id": "[variables('analyticRuleObject23')._analyticRulecontentProductId23]", + "version": "[variables('analyticRuleObject23').analyticRuleVersion23]" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('analyticRuleObject24').analyticRuleTemplateSpecName24]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "TailscalePremiumDerpRelaySurge_AnalyticalRules Analytics Rule with template version 3.0.1", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('analyticRuleObject24').analyticRuleVersion24]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.SecurityInsights/AlertRuleTemplates", + "name": "[variables('analyticRuleObject24')._analyticRulecontentId24]", + "apiVersion": "2023-02-01-preview", + "kind": "Scheduled", + "location": "[parameters('workspace-location')]", + "properties": { + "description": "Identifies when a source node has more than 75 percent of its recent flows falling back to a DERP relay (Tailscale's IsRelayed flag, traffic via 127.3.3.40). Sustained high relay rate indicates direct WireGuard peer-to-peer is failing - causes include NAT/firewall changes, a network blocking UDP 41641, or potential evasion attempts. Operational signal but useful for spotting policy drift. Requires Tailscale Premium or Enterprise.", + "displayName": "Tailscale Premium: DERP relay traffic surge", + "enabled": false, + "query": "let minFlows = 20;\nlet relayedPctThreshold = 75.0;\nTailscale_Network_CL\n| where TimeGenerated > ago(15m)\n| summarize\n TotalFlows = count(),\n RelayedFlows = countif(IsRelayed)\n by SrcNodeName, SrcUser, SrcOs, SrcTags=tostring(SrcTags)\n| where TotalFlows >= minFlows\n| extend RelayedPct = round(100.0 * RelayedFlows / TotalFlows, 1)\n| where RelayedPct > relayedPctThreshold\n| project SrcNodeName, SrcUser, SrcOs, SrcTags, TotalFlows, RelayedFlows, RelayedPct\n| order by RelayedPct desc\n", + "queryFrequency": "PT15M", + "queryPeriod": "PT15M", + "severity": "Low", + "suppressionDuration": "PT1H", + "suppressionEnabled": false, + "triggerOperator": "GreaterThan", + "triggerThreshold": 0, + "status": "Available", + "requiredDataConnectors": [ + { + "dataTypes": [ + "Tailscale_Network_CL" + ], + "connectorId": "TailscalePremiumCCF" + } + ], + "tactics": [ + "CommandAndControl" + ], + "techniques": [ + "T1572" + ], + "entityMappings": [ + { + "fieldMappings": [ + { + "columnName": "SrcNodeName", + "identifier": "HostName" + } + ], + "entityType": "Host" + }, + { + "fieldMappings": [ + { + "columnName": "SrcUser", + "identifier": "FullName" + } + ], + "entityType": "Account" + } + ], + "incidentConfiguration": { + "createIncident": true, + "groupingConfiguration": { + "groupByEntities": [], + "reopenClosedIncident": false, + "matchingMethod": "AllEntities", + "lookbackDuration": "PT6H", + "enabled": true + } + } + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('AnalyticsRule-', last(split(variables('analyticRuleObject24').analyticRuleId24,'/'))))]", + "properties": { + "description": "Tailscale (CCF) Analytics Rule 24", + "parentId": "[variables('analyticRuleObject24').analyticRuleId24]", + "contentId": "[variables('analyticRuleObject24')._analyticRulecontentId24]", + "kind": "AnalyticsRule", + "version": "[variables('analyticRuleObject24').analyticRuleVersion24]", + "source": { + "kind": "Solution", + "name": "Tailscale (CCF)", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "noodlemctwoodle", + "email": "[variables('_email')]" + }, + "support": { + "name": "Tailscale (CCF)", + "tier": "Community", + "email": "ccfconnectors.county118@passmail.com", + "link": "https://github.com/Azure/Azure-Sentinel/issues" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('analyticRuleObject24')._analyticRulecontentId24]", + "contentKind": "AnalyticsRule", + "displayName": "Tailscale Premium: DERP relay traffic surge", + "contentProductId": "[variables('analyticRuleObject24')._analyticRulecontentProductId24]", + "id": "[variables('analyticRuleObject24')._analyticRulecontentProductId24]", + "version": "[variables('analyticRuleObject24').analyticRuleVersion24]" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('huntingQueryObject1').huntingQueryTemplateSpecName1]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "TailscaleFirstSeenActor_HuntingQueries Hunting Query with template version 3.0.1", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('huntingQueryObject1').huntingQueryVersion1]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.OperationalInsights/savedSearches", + "apiVersion": "2025-07-01", + "name": "Tailscale_(CCF)_Hunting_Query_1", + "location": "[parameters('workspace-location')]", + "properties": { + "eTag": "*", + "displayName": "Tailscale: First-seen actor making configuration changes", + "category": "Hunting Queries", + "query": "let lookback = 14d;\nlet baselineWindow = 14d;\nTailscale_Audit_CL\n| where TimeGenerated > ago(lookback)\n| extend ActorLogin = tostring(Actor.loginName)\n| where isnotempty(ActorLogin)\n| summarize FirstSeen = min(TimeGenerated), Actions = make_set(Action), Targets = make_set(tostring(Target.type)), TotalEvents = count() by ActorLogin\n| join kind=leftanti (\n Tailscale_Audit_CL\n | where TimeGenerated between (ago(lookback + baselineWindow) .. ago(lookback))\n | extend ActorLogin = tostring(Actor.loginName)\n | distinct ActorLogin\n) on ActorLogin\n| order by FirstSeen asc\n", + "version": 2, + "tags": [ + { + "name": "description", + "value": "Identifies actors making their first configuration change in the lookback window. New legitimate admins look identical to compromised credentials - review whether each surfaced actor is expected to have admin rights." + }, + { + "name": "tactics", + "value": "InitialAccess,Persistence" + }, + { + "name": "techniques", + "value": "T1078" + } + ] + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject1')._huntingQuerycontentId1),'/'))))]", + "properties": { + "description": "Tailscale (CCF) Hunting Query 1", + "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject1')._huntingQuerycontentId1)]", + "contentId": "[variables('huntingQueryObject1')._huntingQuerycontentId1]", + "kind": "HuntingQuery", + "version": "[variables('huntingQueryObject1').huntingQueryVersion1]", + "source": { + "kind": "Solution", + "name": "Tailscale (CCF)", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "noodlemctwoodle", + "email": "[variables('_email')]" + }, + "support": { + "name": "Tailscale (CCF)", + "tier": "Community", + "email": "ccfconnectors.county118@passmail.com", + "link": "https://github.com/Azure/Azure-Sentinel/issues" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('huntingQueryObject1')._huntingQuerycontentId1]", + "contentKind": "HuntingQuery", + "displayName": "Tailscale: First-seen actor making configuration changes", + "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject1')._huntingQuerycontentId1,'-', '1.0.0')))]", + "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject1')._huntingQuerycontentId1,'-', '1.0.0')))]", + "version": "1.0.0" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('huntingQueryObject2').huntingQueryTemplateSpecName2]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "TailscaleACLPolicyChurn_HuntingQueries Hunting Query with template version 3.0.1", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('huntingQueryObject2').huntingQueryVersion2]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.OperationalInsights/savedSearches", + "apiVersion": "2025-07-01", + "name": "Tailscale_(CCF)_Hunting_Query_2", + "location": "[parameters('workspace-location')]", + "properties": { + "eTag": "*", + "displayName": "Tailscale: ACL policy churn", + "category": "Hunting Queries", + "query": "let bucket = 30m;\nlet churnThreshold = 3;\nTailscale_Audit_CL\n| where Action == \"UPDATE\"\n| where tostring(Target.type) == \"TAILNET\"\n| where tostring(Target.property) == \"ACL\"\n| extend ActorLogin = tostring(Actor.loginName)\n| summarize EditCount = count(), Editors = make_set(ActorLogin), FirstEdit = min(TimeGenerated), LastEdit = max(TimeGenerated)\n by bin(TimeGenerated, bucket)\n| where EditCount >= churnThreshold\n| order by EditCount desc\n", + "version": 2, + "tags": [ + { + "name": "description", + "value": "Identifies short windows where the tailnet ACL policy file was rewritten multiple times. Iterative policy edits during an incident can indicate rule-hunting by an attacker or a misconfiguration spiral; legitimate ACL refactors usually land as a single change." + }, + { + "name": "tactics", + "value": "DefenseEvasion,PrivilegeEscalation" + }, + { + "name": "techniques", + "value": "T1098,T1556" + } + ] + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject2')._huntingQuerycontentId2),'/'))))]", + "properties": { + "description": "Tailscale (CCF) Hunting Query 2", + "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject2')._huntingQuerycontentId2)]", + "contentId": "[variables('huntingQueryObject2')._huntingQuerycontentId2]", + "kind": "HuntingQuery", + "version": "[variables('huntingQueryObject2').huntingQueryVersion2]", + "source": { + "kind": "Solution", + "name": "Tailscale (CCF)", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "noodlemctwoodle", + "email": "[variables('_email')]" + }, + "support": { + "name": "Tailscale (CCF)", + "tier": "Community", + "email": "ccfconnectors.county118@passmail.com", + "link": "https://github.com/Azure/Azure-Sentinel/issues" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('huntingQueryObject2')._huntingQuerycontentId2]", + "contentKind": "HuntingQuery", + "displayName": "Tailscale: ACL policy churn", + "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject2')._huntingQuerycontentId2,'-', '1.0.0')))]", + "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject2')._huntingQuerycontentId2,'-', '1.0.0')))]", + "version": "1.0.0" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('huntingQueryObject3').huntingQueryTemplateSpecName3]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "TailscaleOffHoursConfigChanges_HuntingQueries Hunting Query with template version 3.0.1", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('huntingQueryObject3').huntingQueryVersion3]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.OperationalInsights/savedSearches", + "apiVersion": "2025-07-01", + "name": "Tailscale_(CCF)_Hunting_Query_3", + "location": "[parameters('workspace-location')]", + "properties": { + "eTag": "*", + "displayName": "Tailscale: Off-hours configuration changes", + "category": "Hunting Queries", + "query": "let businessStartUtc = 8;\nlet businessEndUtc = 18;\nTailscale_Audit_CL\n| extend HourOfDay = datetime_part(\"hour\", TimeGenerated), DayOfWeek = dayofweek(TimeGenerated)\n| where DayOfWeek in (0d, 6d) // Sunday and Saturday\n or HourOfDay < businessStartUtc\n or HourOfDay >= businessEndUtc\n| extend ActorLogin = tostring(Actor.loginName)\n| extend TargetType = tostring(Target.type)\n| project TimeGenerated, ActorLogin, Action, TargetType, Target, Origin\n| order by TimeGenerated desc\n", + "version": 2, + "tags": [ + { + "name": "description", + "value": "Identifies configuration audit events that occurred outside typical business hours (defined as Monday-Friday 08:00-18:00 UTC). Adjust the time window to match your operating region. Useful for spotting impromptu maintenance, account compromise, or insider activity." + }, + { + "name": "tactics", + "value": "InitialAccess,Persistence" + }, + { + "name": "techniques", + "value": "T1078" + } + ] + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject3')._huntingQuerycontentId3),'/'))))]", + "properties": { + "description": "Tailscale (CCF) Hunting Query 3", + "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject3')._huntingQuerycontentId3)]", + "contentId": "[variables('huntingQueryObject3')._huntingQuerycontentId3]", + "kind": "HuntingQuery", + "version": "[variables('huntingQueryObject3').huntingQueryVersion3]", + "source": { + "kind": "Solution", + "name": "Tailscale (CCF)", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "noodlemctwoodle", + "email": "[variables('_email')]" + }, + "support": { + "name": "Tailscale (CCF)", + "tier": "Community", + "email": "ccfconnectors.county118@passmail.com", + "link": "https://github.com/Azure/Azure-Sentinel/issues" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('huntingQueryObject3')._huntingQuerycontentId3]", + "contentKind": "HuntingQuery", + "displayName": "Tailscale: Off-hours configuration changes", + "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject3')._huntingQuerycontentId3,'-', '1.0.0')))]", + "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject3')._huntingQuerycontentId3,'-', '1.0.0')))]", + "version": "1.0.0" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('huntingQueryObject4').huntingQueryTemplateSpecName4]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "TailscaleAuthKeySprawl_HuntingQueries Hunting Query with template version 3.0.1", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('huntingQueryObject4').huntingQueryVersion4]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.OperationalInsights/savedSearches", + "apiVersion": "2025-07-01", + "name": "Tailscale_(CCF)_Hunting_Query_4", + "location": "[parameters('workspace-location')]", + "properties": { + "eTag": "*", + "displayName": "Tailscale: Auth key sprawl", + "category": "Hunting Queries", + "query": "let bucket = 1h;\nlet sprawlThreshold = 5;\nTailscale_Audit_CL\n| where Action == \"CREATE\"\n| where tostring(Target.type) == \"AUTH_KEY\"\n| extend ActorLogin = tostring(Actor.loginName)\n| summarize KeysCreated = count(), KeyIDs = make_set(tostring(Target.id)), Reusable = make_set(tostring(New.reusable)), Ephemeral = make_set(tostring(New.ephemeral))\n by bin(TimeGenerated, bucket), ActorLogin\n| where KeysCreated >= sprawlThreshold\n| order by KeysCreated desc\n", + "version": 2, + "tags": [ + { + "name": "description", + "value": "Identifies actors creating multiple auth keys in a short window. A single admin creating many keys for unattended enrollment is normal during a rollout; the same pattern can also indicate token-spraying for persistence. Cross-reference with the Tailscale: Auth key created alert to filter context." + }, + { + "name": "tactics", + "value": "Persistence,CredentialAccess" + }, + { + "name": "techniques", + "value": "T1098,T1136" + } + ] + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject4')._huntingQuerycontentId4),'/'))))]", + "properties": { + "description": "Tailscale (CCF) Hunting Query 4", + "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject4')._huntingQuerycontentId4)]", + "contentId": "[variables('huntingQueryObject4')._huntingQuerycontentId4]", + "kind": "HuntingQuery", + "version": "[variables('huntingQueryObject4').huntingQueryVersion4]", + "source": { + "kind": "Solution", + "name": "Tailscale (CCF)", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "noodlemctwoodle", + "email": "[variables('_email')]" + }, + "support": { + "name": "Tailscale (CCF)", + "tier": "Community", + "email": "ccfconnectors.county118@passmail.com", + "link": "https://github.com/Azure/Azure-Sentinel/issues" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('huntingQueryObject4')._huntingQuerycontentId4]", + "contentKind": "HuntingQuery", + "displayName": "Tailscale: Auth key sprawl", + "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject4')._huntingQuerycontentId4,'-', '1.0.0')))]", + "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject4')._huntingQuerycontentId4,'-', '1.0.0')))]", + "version": "1.0.0" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('huntingQueryObject5').huntingQueryTemplateSpecName5]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "TailscaleDormantDevices_HuntingQueries Hunting Query with template version 3.0.1", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('huntingQueryObject5').huntingQueryVersion5]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.OperationalInsights/savedSearches", + "apiVersion": "2025-07-01", + "name": "Tailscale_(CCF)_Hunting_Query_5", + "location": "[parameters('workspace-location')]", + "properties": { + "eTag": "*", + "displayName": "Tailscale: Devices not seen in 30+ days", + "category": "Hunting Queries", + "query": "Tailscale_Devices_CL\n| summarize arg_max(TimeGenerated, *) by DeviceId\n| where LastSeen < ago(30d)\n| extend DaysSinceSeen = datetime_diff('day', now(), LastSeen)\n| project DeviceName, Hostname, User, Os, ClientVersion, LastSeen, DaysSinceSeen, Tags, AdvertisedRoutes\n| order by DaysSinceSeen desc\n", + "version": 2, + "tags": [ + { + "name": "description", + "value": "Identifies tailnet devices that have not connected to the control plane for at least 30 days. Dormant devices accumulate risk - they may still have valid keys, advertised routes, or tags but no operational oversight. Candidate for removal or key rotation." + }, + { + "name": "tactics", + "value": "Discovery" + }, + { + "name": "techniques", + "value": "T1078" + } + ] + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject5')._huntingQuerycontentId5),'/'))))]", + "properties": { + "description": "Tailscale (CCF) Hunting Query 5", + "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject5')._huntingQuerycontentId5)]", + "contentId": "[variables('huntingQueryObject5')._huntingQuerycontentId5]", + "kind": "HuntingQuery", + "version": "[variables('huntingQueryObject5').huntingQueryVersion5]", + "source": { + "kind": "Solution", + "name": "Tailscale (CCF)", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "noodlemctwoodle", + "email": "[variables('_email')]" + }, + "support": { + "name": "Tailscale (CCF)", + "tier": "Community", + "email": "ccfconnectors.county118@passmail.com", + "link": "https://github.com/Azure/Azure-Sentinel/issues" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('huntingQueryObject5')._huntingQuerycontentId5]", + "contentKind": "HuntingQuery", + "displayName": "Tailscale: Devices not seen in 30+ days", + "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject5')._huntingQuerycontentId5,'-', '1.0.0')))]", + "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject5')._huntingQuerycontentId5,'-', '1.0.0')))]", + "version": "1.0.0" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('huntingQueryObject6').huntingQueryTemplateSpecName6]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "TailscaleAuthKeysNoExpiry_HuntingQueries Hunting Query with template version 3.0.1", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('huntingQueryObject6').huntingQueryVersion6]", "parameters": {}, "variables": {}, "resources": [ { "type": "Microsoft.OperationalInsights/savedSearches", "apiVersion": "2025-07-01", - "name": "Tailscale_(CCF)_Hunting_Query_1", + "name": "Tailscale_(CCF)_Hunting_Query_6", "location": "[parameters('workspace-location')]", "properties": { "eTag": "*", - "displayName": "Tailscale: First-seen actor making configuration changes", + "displayName": "Tailscale: Auth keys with no expiry", "category": "Hunting Queries", - "query": "let lookback = 14d;\nlet baselineWindow = 14d;\nTailscale_Audit_CL\n| where TimeGenerated > ago(lookback)\n| extend ActorLogin = tostring(Actor.loginName)\n| where isnotempty(ActorLogin)\n| summarize FirstSeen = min(TimeGenerated), Actions = make_set(Action), Targets = make_set(tostring(Target.type)), TotalEvents = count() by ActorLogin\n| join kind=leftanti (\n Tailscale_Audit_CL\n | where TimeGenerated between (ago(lookback + baselineWindow) .. ago(lookback))\n | extend ActorLogin = tostring(Actor.loginName)\n | distinct ActorLogin\n) on ActorLogin\n| order by FirstSeen asc\n", + "query": "Tailscale_Keys_CL\n| summarize arg_max(TimeGenerated, *) by KeyId\n| where isnull(Revoked) or Revoked == datetime(null)\n| where isnull(Expires) or Expires == datetime(null)\n| project KeyId, Description, UserId, Created, Capabilities\n| order by Created asc\n", "version": 2, "tags": [ { "name": "description", - "value": "Surfaces actors making their first configuration change in the lookback window. New legitimate admins look identical to compromised credentials - review whether each surfaced actor is expected to have admin rights." + "value": "Identifies tailnet auth keys that have no expiry timestamp set. Non-expiring keys grant unattended enrollment in perpetuity and should be rotated or replaced with ephemeral, time-bounded keys." }, { "name": "tactics", - "value": "InitialAccess,Persistence" + "value": "Persistence,CredentialAccess" }, { "name": "techniques", - "value": "T1078" + "value": "T1098,T1136" } ] } @@ -7044,13 +7944,13 @@ { "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", "apiVersion": "2022-01-01-preview", - "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject1')._huntingQuerycontentId1),'/'))))]", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject6')._huntingQuerycontentId6),'/'))))]", "properties": { - "description": "Tailscale (CCF) Hunting Query 1", - "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject1')._huntingQuerycontentId1)]", - "contentId": "[variables('huntingQueryObject1')._huntingQuerycontentId1]", + "description": "Tailscale (CCF) Hunting Query 6", + "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject6')._huntingQuerycontentId6)]", + "contentId": "[variables('huntingQueryObject6')._huntingQuerycontentId6]", "kind": "HuntingQuery", - "version": "[variables('huntingQueryObject1').huntingQueryVersion1]", + "version": "[variables('huntingQueryObject6').huntingQueryVersion6]", "source": { "kind": "Solution", "name": "Tailscale (CCF)", @@ -7061,7 +7961,7 @@ "email": "[variables('_email')]" }, "support": { - "name": "Community", + "name": "Tailscale (CCF)", "tier": "Community", "email": "ccfconnectors.county118@passmail.com", "link": "https://github.com/Azure/Azure-Sentinel/issues" @@ -7075,53 +7975,53 @@ "packageName": "[variables('_solutionName')]", "packageId": "[variables('_solutionId')]", "contentSchemaVersion": "3.0.0", - "contentId": "[variables('huntingQueryObject1')._huntingQuerycontentId1]", + "contentId": "[variables('huntingQueryObject6')._huntingQuerycontentId6]", "contentKind": "HuntingQuery", - "displayName": "Tailscale: First-seen actor making configuration changes", - "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject1')._huntingQuerycontentId1,'-', '1.0.0')))]", - "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject1')._huntingQuerycontentId1,'-', '1.0.0')))]", + "displayName": "Tailscale: Auth keys with no expiry", + "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject6')._huntingQuerycontentId6,'-', '1.0.0')))]", + "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject6')._huntingQuerycontentId6,'-', '1.0.0')))]", "version": "1.0.0" } }, { "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", "apiVersion": "2023-04-01-preview", - "name": "[variables('huntingQueryObject2').huntingQueryTemplateSpecName2]", + "name": "[variables('huntingQueryObject7').huntingQueryTemplateSpecName7]", "location": "[parameters('workspace-location')]", "dependsOn": [ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleACLPolicyChurn_HuntingQueries Hunting Query with template version 3.0.5", + "description": "TailscaleOrphanedUsers_HuntingQueries Hunting Query with template version 3.0.1", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "[variables('huntingQueryObject2').huntingQueryVersion2]", + "contentVersion": "[variables('huntingQueryObject7').huntingQueryVersion7]", "parameters": {}, "variables": {}, "resources": [ { "type": "Microsoft.OperationalInsights/savedSearches", "apiVersion": "2025-07-01", - "name": "Tailscale_(CCF)_Hunting_Query_2", + "name": "Tailscale_(CCF)_Hunting_Query_7", "location": "[parameters('workspace-location')]", "properties": { "eTag": "*", - "displayName": "Tailscale: ACL policy churn", + "displayName": "Tailscale: Users with zero devices", "category": "Hunting Queries", - "query": "let bucket = 30m;\nlet churnThreshold = 3;\nTailscale_Audit_CL\n| where Action == \"UPDATE\"\n| where tostring(Target.type) == \"TAILNET\"\n| where tostring(Target.property) == \"ACL\"\n| extend ActorLogin = tostring(Actor.loginName)\n| summarize EditCount = count(), Editors = make_set(ActorLogin), FirstEdit = min(TimeGenerated), LastEdit = max(TimeGenerated)\n by bin(TimeGenerated, bucket)\n| where EditCount >= churnThreshold\n| order by EditCount desc\n", + "query": "Tailscale_Users_CL\n| summarize arg_max(TimeGenerated, *) by UserId\n| where DeviceCount == 0\n| where Status != \"suspended\"\n| project LoginName, DisplayName, Role, Status, DeviceCount, Created, LastSeen\n| order by LastSeen asc\n", "version": 2, "tags": [ { "name": "description", - "value": "Identifies short windows where the tailnet ACL policy file was rewritten multiple times. Iterative policy edits during an incident can indicate rule-hunting by an attacker or a misconfiguration spiral; legitimate ACL refactors usually land as a single change." + "value": "Identifies tailnet users who have no devices currently registered. Orphaned identities are candidates for off-boarding - they retain whatever role/permissions they were granted and can still create auth keys or invite others. Review against your HR / identity-provider join-leaver records." }, { "name": "tactics", - "value": "DefenseEvasion,PrivilegeEscalation" + "value": "InitialAccess" }, { "name": "techniques", - "value": "T1098,T1556" + "value": "T1078" } ] } @@ -7129,13 +8029,13 @@ { "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", "apiVersion": "2022-01-01-preview", - "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject2')._huntingQuerycontentId2),'/'))))]", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject7')._huntingQuerycontentId7),'/'))))]", "properties": { - "description": "Tailscale (CCF) Hunting Query 2", - "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject2')._huntingQuerycontentId2)]", - "contentId": "[variables('huntingQueryObject2')._huntingQuerycontentId2]", + "description": "Tailscale (CCF) Hunting Query 7", + "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject7')._huntingQuerycontentId7)]", + "contentId": "[variables('huntingQueryObject7')._huntingQuerycontentId7]", "kind": "HuntingQuery", - "version": "[variables('huntingQueryObject2').huntingQueryVersion2]", + "version": "[variables('huntingQueryObject7').huntingQueryVersion7]", "source": { "kind": "Solution", "name": "Tailscale (CCF)", @@ -7146,7 +8046,7 @@ "email": "[variables('_email')]" }, "support": { - "name": "Community", + "name": "Tailscale (CCF)", "tier": "Community", "email": "ccfconnectors.county118@passmail.com", "link": "https://github.com/Azure/Azure-Sentinel/issues" @@ -7160,53 +8060,53 @@ "packageName": "[variables('_solutionName')]", "packageId": "[variables('_solutionId')]", "contentSchemaVersion": "3.0.0", - "contentId": "[variables('huntingQueryObject2')._huntingQuerycontentId2]", + "contentId": "[variables('huntingQueryObject7')._huntingQuerycontentId7]", "contentKind": "HuntingQuery", - "displayName": "Tailscale: ACL policy churn", - "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject2')._huntingQuerycontentId2,'-', '1.0.0')))]", - "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject2')._huntingQuerycontentId2,'-', '1.0.0')))]", + "displayName": "Tailscale: Users with zero devices", + "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject7')._huntingQuerycontentId7,'-', '1.0.0')))]", + "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject7')._huntingQuerycontentId7,'-', '1.0.0')))]", "version": "1.0.0" } }, { "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", "apiVersion": "2023-04-01-preview", - "name": "[variables('huntingQueryObject3').huntingQueryTemplateSpecName3]", + "name": "[variables('huntingQueryObject8').huntingQueryTemplateSpecName8]", "location": "[parameters('workspace-location')]", "dependsOn": [ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleOffHoursConfigChanges_HuntingQueries Hunting Query with template version 3.0.5", + "description": "TailscaleSplitDnsPerDomainChanges_HuntingQueries Hunting Query with template version 3.0.1", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "[variables('huntingQueryObject3').huntingQueryVersion3]", + "contentVersion": "[variables('huntingQueryObject8').huntingQueryVersion8]", "parameters": {}, "variables": {}, "resources": [ { "type": "Microsoft.OperationalInsights/savedSearches", "apiVersion": "2025-07-01", - "name": "Tailscale_(CCF)_Hunting_Query_3", + "name": "Tailscale_(CCF)_Hunting_Query_8", "location": "[parameters('workspace-location')]", "properties": { "eTag": "*", - "displayName": "Tailscale: Off-hours configuration changes", + "displayName": "Tailscale: Split-DNS per-domain change history", "category": "Hunting Queries", - "query": "let businessStartUtc = 8;\nlet businessEndUtc = 18;\nTailscale_Audit_CL\n| extend HourOfDay = datetime_part(\"hour\", TimeGenerated), DayOfWeek = dayofweek(TimeGenerated)\n| where DayOfWeek in (0d, 6d) // Sunday and Saturday\n or HourOfDay < businessStartUtc\n or HourOfDay >= businessEndUtc\n| extend ActorLogin = tostring(Actor.loginName)\n| extend TargetType = tostring(Target.type)\n| project TimeGenerated, ActorLogin, Action, TargetType, Target, Origin\n| order by TimeGenerated desc\n", + "query": "Tailscale_Audit_CL\n| where Action == \"UPDATE\"\n| where tostring(Target.type) == \"TAILNET\"\n| where tostring(Target.property) == \"DNS_SPLIT_DNS\"\n| extend ActorLogin = tostring(Actor.loginName)\n| extend OldKeys = bag_keys(Old), NewKeys = bag_keys(New)\n| extend AllDomains = set_union(OldKeys, NewKeys)\n| mv-expand Domain = AllDomains to typeof(string)\n| extend OldResolvers = Old[Domain], NewResolvers = New[Domain]\n| extend Change = case(\n isnull(OldResolvers) and isnotnull(NewResolvers), \"added\",\n isnotnull(OldResolvers) and isnull(NewResolvers), \"removed\",\n tostring(OldResolvers) != tostring(NewResolvers), \"resolvers-changed\",\n \"unchanged\")\n| where Change != \"unchanged\"\n| project TimeGenerated, ActorLogin, Domain, Change, OldResolvers, NewResolvers\n| order by TimeGenerated desc\n", "version": 2, "tags": [ { "name": "description", - "value": "Lists configuration audit events that occurred outside typical business hours (defined as Monday-Friday 08:00-18:00 UTC). Adjust the time window to match your operating region. Useful for spotting impromptu maintenance, account compromise, or insider activity." + "value": "Reconstructs the per-domain Split-DNS change history from the audit log. For each modification event, expands the Old and New documents and surfaces which domains were added, removed, or had their resolver IPs changed. Useful for forensic review after a suspected DNS hijack and for confirming that a planned DNS migration deployed as intended." }, { "name": "tactics", - "value": "InitialAccess,Persistence" + "value": "DefenseEvasion,CommandAndControl" }, { "name": "techniques", - "value": "T1078" + "value": "T1556,T1568" } ] } @@ -7214,13 +8114,13 @@ { "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", "apiVersion": "2022-01-01-preview", - "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject3')._huntingQuerycontentId3),'/'))))]", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject8')._huntingQuerycontentId8),'/'))))]", "properties": { - "description": "Tailscale (CCF) Hunting Query 3", - "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject3')._huntingQuerycontentId3)]", - "contentId": "[variables('huntingQueryObject3')._huntingQuerycontentId3]", + "description": "Tailscale (CCF) Hunting Query 8", + "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject8')._huntingQuerycontentId8)]", + "contentId": "[variables('huntingQueryObject8')._huntingQuerycontentId8]", "kind": "HuntingQuery", - "version": "[variables('huntingQueryObject3').huntingQueryVersion3]", + "version": "[variables('huntingQueryObject8').huntingQueryVersion8]", "source": { "kind": "Solution", "name": "Tailscale (CCF)", @@ -7231,7 +8131,7 @@ "email": "[variables('_email')]" }, "support": { - "name": "Community", + "name": "Tailscale (CCF)", "tier": "Community", "email": "ccfconnectors.county118@passmail.com", "link": "https://github.com/Azure/Azure-Sentinel/issues" @@ -7245,53 +8145,53 @@ "packageName": "[variables('_solutionName')]", "packageId": "[variables('_solutionId')]", "contentSchemaVersion": "3.0.0", - "contentId": "[variables('huntingQueryObject3')._huntingQuerycontentId3]", + "contentId": "[variables('huntingQueryObject8')._huntingQuerycontentId8]", "contentKind": "HuntingQuery", - "displayName": "Tailscale: Off-hours configuration changes", - "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject3')._huntingQuerycontentId3,'-', '1.0.0')))]", - "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject3')._huntingQuerycontentId3,'-', '1.0.0')))]", + "displayName": "Tailscale: Split-DNS per-domain change history", + "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject8')._huntingQuerycontentId8,'-', '1.0.0')))]", + "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject8')._huntingQuerycontentId8,'-', '1.0.0')))]", "version": "1.0.0" } }, { "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", "apiVersion": "2023-04-01-preview", - "name": "[variables('huntingQueryObject4').huntingQueryTemplateSpecName4]", + "name": "[variables('huntingQueryObject9').huntingQueryTemplateSpecName9]", "location": "[parameters('workspace-location')]", "dependsOn": [ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleAuthKeySprawl_HuntingQueries Hunting Query with template version 3.0.5", + "description": "TailscaleDevicesWithSshEnabled_HuntingQueries Hunting Query with template version 3.0.1", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "[variables('huntingQueryObject4').huntingQueryVersion4]", + "contentVersion": "[variables('huntingQueryObject9').huntingQueryVersion9]", "parameters": {}, "variables": {}, "resources": [ { "type": "Microsoft.OperationalInsights/savedSearches", "apiVersion": "2025-07-01", - "name": "Tailscale_(CCF)_Hunting_Query_4", + "name": "Tailscale_(CCF)_Hunting_Query_9", "location": "[parameters('workspace-location')]", "properties": { "eTag": "*", - "displayName": "Tailscale: Auth key sprawl", + "displayName": "Tailscale: Devices with Tailscale SSH enabled", "category": "Hunting Queries", - "query": "let bucket = 1h;\nlet sprawlThreshold = 5;\nTailscale_Audit_CL\n| where Action == \"CREATE\"\n| where tostring(Target.type) == \"AUTH_KEY\"\n| extend ActorLogin = tostring(Actor.loginName)\n| summarize KeysCreated = count(), KeyIDs = make_set(tostring(Target.id)), Reusable = make_set(tostring(New.reusable)), Ephemeral = make_set(tostring(New.ephemeral))\n by bin(TimeGenerated, bucket), ActorLogin\n| where KeysCreated >= sprawlThreshold\n| order by KeysCreated desc\n", + "query": "Tailscale_Devices_CL\n| summarize arg_max(TimeGenerated, *) by DeviceId\n| where SshEnabled == true\n| project DeviceName, Hostname, User, Os, Distro, ClientVersion, LastSeen, Tags\n| order by DeviceName asc\n", "version": 2, "tags": [ { "name": "description", - "value": "Actors creating multiple auth keys in a short window. A single admin creating many keys for unattended enrollment is normal during a rollout; the same pattern can also indicate token-spraying for persistence. Cross-reference with the Tailscale: Auth key created alert to filter context." + "value": "Identifies devices that currently have Tailscale SSH enabled. Tailscale SSH delivers SSH access over the tailnet using Tailscale identity (no SSH keys needed) and is governed by the SSH ACL block in the policy file. Cross-reference this list with the SSH ACL to confirm only the intended devices are reachable as SSH targets." }, { "name": "tactics", - "value": "Persistence,CredentialAccess" + "value": "LateralMovement,Persistence" }, { "name": "techniques", - "value": "T1098,T1136" + "value": "T1021" } ] } @@ -7299,13 +8199,13 @@ { "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", "apiVersion": "2022-01-01-preview", - "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject4')._huntingQuerycontentId4),'/'))))]", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject9')._huntingQuerycontentId9),'/'))))]", "properties": { - "description": "Tailscale (CCF) Hunting Query 4", - "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject4')._huntingQuerycontentId4)]", - "contentId": "[variables('huntingQueryObject4')._huntingQuerycontentId4]", + "description": "Tailscale (CCF) Hunting Query 9", + "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject9')._huntingQuerycontentId9)]", + "contentId": "[variables('huntingQueryObject9')._huntingQuerycontentId9]", "kind": "HuntingQuery", - "version": "[variables('huntingQueryObject4').huntingQueryVersion4]", + "version": "[variables('huntingQueryObject9').huntingQueryVersion9]", "source": { "kind": "Solution", "name": "Tailscale (CCF)", @@ -7316,7 +8216,7 @@ "email": "[variables('_email')]" }, "support": { - "name": "Community", + "name": "Tailscale (CCF)", "tier": "Community", "email": "ccfconnectors.county118@passmail.com", "link": "https://github.com/Azure/Azure-Sentinel/issues" @@ -7330,49 +8230,49 @@ "packageName": "[variables('_solutionName')]", "packageId": "[variables('_solutionId')]", "contentSchemaVersion": "3.0.0", - "contentId": "[variables('huntingQueryObject4')._huntingQuerycontentId4]", + "contentId": "[variables('huntingQueryObject9')._huntingQuerycontentId9]", "contentKind": "HuntingQuery", - "displayName": "Tailscale: Auth key sprawl", - "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject4')._huntingQuerycontentId4,'-', '1.0.0')))]", - "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject4')._huntingQuerycontentId4,'-', '1.0.0')))]", + "displayName": "Tailscale: Devices with Tailscale SSH enabled", + "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject9')._huntingQuerycontentId9,'-', '1.0.0')))]", + "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject9')._huntingQuerycontentId9,'-', '1.0.0')))]", "version": "1.0.0" } }, { "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", "apiVersion": "2023-04-01-preview", - "name": "[variables('huntingQueryObject5').huntingQueryTemplateSpecName5]", + "name": "[variables('huntingQueryObject10').huntingQueryTemplateSpecName10]", "location": "[parameters('workspace-location')]", "dependsOn": [ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleDormantDevices_HuntingQueries Hunting Query with template version 3.0.5", + "description": "TailscaleExternalDeviceInventory_HuntingQueries Hunting Query with template version 3.0.1", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "[variables('huntingQueryObject5').huntingQueryVersion5]", + "contentVersion": "[variables('huntingQueryObject10').huntingQueryVersion10]", "parameters": {}, "variables": {}, "resources": [ { "type": "Microsoft.OperationalInsights/savedSearches", "apiVersion": "2025-07-01", - "name": "Tailscale_(CCF)_Hunting_Query_5", + "name": "Tailscale_(CCF)_Hunting_Query_10", "location": "[parameters('workspace-location')]", "properties": { "eTag": "*", - "displayName": "Tailscale: Devices not seen in 30+ days", + "displayName": "Tailscale: External (shared-in) device inventory", "category": "Hunting Queries", - "query": "Tailscale_Devices_CL\n| summarize arg_max(TimeGenerated, *) by DeviceId\n| where LastSeen < ago(30d)\n| extend DaysSinceSeen = datetime_diff('day', now(), LastSeen)\n| project DeviceName, Hostname, User, Os, ClientVersion, LastSeen, DaysSinceSeen, Tags, AdvertisedRoutes\n| order by DaysSinceSeen desc\n", + "query": "Tailscale_Devices_CL\n| summarize arg_max(TimeGenerated, *) by DeviceId\n| where IsExternal == true\n| project DeviceName, Hostname, User, Os, ClientVersion, Created, LastSeen, Tags\n| order by Created desc\n", "version": 2, "tags": [ { "name": "description", - "value": "Lists tailnet devices that have not connected to the control plane for at least 30 days. Dormant devices accumulate risk - they may still have valid keys, advertised routes, or tags but no operational oversight. Candidate for removal or key rotation." + "value": "Identifies external (shared-in) devices currently active in the tailnet. These devices belong to another tailnet and have been admitted via a Tailscale sharing arrangement. Confirm each entry maps to a documented collaboration and that the corresponding ACL restricts the device to the intended resources." }, { "name": "tactics", - "value": "Discovery" + "value": "InitialAccess" }, { "name": "techniques", @@ -7384,13 +8284,13 @@ { "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", "apiVersion": "2022-01-01-preview", - "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject5')._huntingQuerycontentId5),'/'))))]", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject10')._huntingQuerycontentId10),'/'))))]", "properties": { - "description": "Tailscale (CCF) Hunting Query 5", - "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject5')._huntingQuerycontentId5)]", - "contentId": "[variables('huntingQueryObject5')._huntingQuerycontentId5]", + "description": "Tailscale (CCF) Hunting Query 10", + "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject10')._huntingQuerycontentId10)]", + "contentId": "[variables('huntingQueryObject10')._huntingQuerycontentId10]", "kind": "HuntingQuery", - "version": "[variables('huntingQueryObject5').huntingQueryVersion5]", + "version": "[variables('huntingQueryObject10').huntingQueryVersion10]", "source": { "kind": "Solution", "name": "Tailscale (CCF)", @@ -7401,7 +8301,7 @@ "email": "[variables('_email')]" }, "support": { - "name": "Community", + "name": "Tailscale (CCF)", "tier": "Community", "email": "ccfconnectors.county118@passmail.com", "link": "https://github.com/Azure/Azure-Sentinel/issues" @@ -7415,53 +8315,53 @@ "packageName": "[variables('_solutionName')]", "packageId": "[variables('_solutionId')]", "contentSchemaVersion": "3.0.0", - "contentId": "[variables('huntingQueryObject5')._huntingQuerycontentId5]", + "contentId": "[variables('huntingQueryObject10')._huntingQuerycontentId10]", "contentKind": "HuntingQuery", - "displayName": "Tailscale: Devices not seen in 30+ days", - "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject5')._huntingQuerycontentId5,'-', '1.0.0')))]", - "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject5')._huntingQuerycontentId5,'-', '1.0.0')))]", + "displayName": "Tailscale: External (shared-in) device inventory", + "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject10')._huntingQuerycontentId10,'-', '1.0.0')))]", + "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject10')._huntingQuerycontentId10,'-', '1.0.0')))]", "version": "1.0.0" } }, { "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", "apiVersion": "2023-04-01-preview", - "name": "[variables('huntingQueryObject6').huntingQueryTemplateSpecName6]", + "name": "[variables('huntingQueryObject11').huntingQueryTemplateSpecName11]", "location": "[parameters('workspace-location')]", "dependsOn": [ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleAuthKeysNoExpiry_HuntingQueries Hunting Query with template version 3.0.5", + "description": "TailscaleOutdatedClients_HuntingQueries Hunting Query with template version 3.0.1", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "[variables('huntingQueryObject6').huntingQueryVersion6]", + "contentVersion": "[variables('huntingQueryObject11').huntingQueryVersion11]", "parameters": {}, "variables": {}, "resources": [ { "type": "Microsoft.OperationalInsights/savedSearches", "apiVersion": "2025-07-01", - "name": "Tailscale_(CCF)_Hunting_Query_6", + "name": "Tailscale_(CCF)_Hunting_Query_11", "location": "[parameters('workspace-location')]", "properties": { "eTag": "*", - "displayName": "Tailscale: Auth keys with no expiry", + "displayName": "Tailscale: Devices with outdated client version", "category": "Hunting Queries", - "query": "Tailscale_Keys_CL\n| summarize arg_max(TimeGenerated, *) by KeyId\n| where isnull(Revoked) or Revoked == datetime(null)\n| where isnull(Expires) or Expires == datetime(null)\n| project KeyId, Description, UserId, Created, Capabilities\n| order by Created asc\n", + "query": "Tailscale_Devices_CL\n| summarize arg_max(TimeGenerated, *) by DeviceId\n| where UpdateAvailable == true\n| project DeviceName, Hostname, User, Os, Distro, ClientVersion, LastSeen, Tags\n| order by ClientVersion asc, LastSeen desc\n", "version": 2, "tags": [ { "name": "description", - "value": "Identifies tailnet auth keys that have no expiry timestamp set. Non-expiring keys grant unattended enrollment in perpetuity and should be rotated or replaced with ephemeral, time-bounded keys." + "value": "Identifies tailnet devices that report UpdateAvailable=true on the latest snapshot. Tailscale releases security and feature updates regularly; a device showing UpdateAvailable lacks the most recent improvements. Use this list to plan a fleet update, especially before enabling new ACL features that require minimum client versions." }, { "name": "tactics", - "value": "Persistence,CredentialAccess" + "value": "DefenseEvasion" }, { "name": "techniques", - "value": "T1098,T1136" + "value": "T1562" } ] } @@ -7469,13 +8369,13 @@ { "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", "apiVersion": "2022-01-01-preview", - "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject6')._huntingQuerycontentId6),'/'))))]", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject11')._huntingQuerycontentId11),'/'))))]", "properties": { - "description": "Tailscale (CCF) Hunting Query 6", - "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject6')._huntingQuerycontentId6)]", - "contentId": "[variables('huntingQueryObject6')._huntingQuerycontentId6]", + "description": "Tailscale (CCF) Hunting Query 11", + "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject11')._huntingQuerycontentId11)]", + "contentId": "[variables('huntingQueryObject11')._huntingQuerycontentId11]", "kind": "HuntingQuery", - "version": "[variables('huntingQueryObject6').huntingQueryVersion6]", + "version": "[variables('huntingQueryObject11').huntingQueryVersion11]", "source": { "kind": "Solution", "name": "Tailscale (CCF)", @@ -7486,7 +8386,7 @@ "email": "[variables('_email')]" }, "support": { - "name": "Community", + "name": "Tailscale (CCF)", "tier": "Community", "email": "ccfconnectors.county118@passmail.com", "link": "https://github.com/Azure/Azure-Sentinel/issues" @@ -7500,53 +8400,53 @@ "packageName": "[variables('_solutionName')]", "packageId": "[variables('_solutionId')]", "contentSchemaVersion": "3.0.0", - "contentId": "[variables('huntingQueryObject6')._huntingQuerycontentId6]", + "contentId": "[variables('huntingQueryObject11')._huntingQuerycontentId11]", "contentKind": "HuntingQuery", - "displayName": "Tailscale: Auth keys with no expiry", - "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject6')._huntingQuerycontentId6,'-', '1.0.0')))]", - "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject6')._huntingQuerycontentId6,'-', '1.0.0')))]", + "displayName": "Tailscale: Devices with outdated client version", + "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject11')._huntingQuerycontentId11,'-', '1.0.0')))]", + "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject11')._huntingQuerycontentId11,'-', '1.0.0')))]", "version": "1.0.0" } }, { "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", "apiVersion": "2023-04-01-preview", - "name": "[variables('huntingQueryObject7').huntingQueryTemplateSpecName7]", + "name": "[variables('huntingQueryObject12').huntingQueryTemplateSpecName12]", "location": "[parameters('workspace-location')]", "dependsOn": [ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleOrphanedUsers_HuntingQueries Hunting Query with template version 3.0.5", + "description": "TailscaleSubnetRouteExposure_HuntingQueries Hunting Query with template version 3.0.1", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "[variables('huntingQueryObject7').huntingQueryVersion7]", + "contentVersion": "[variables('huntingQueryObject12').huntingQueryVersion12]", "parameters": {}, "variables": {}, "resources": [ { "type": "Microsoft.OperationalInsights/savedSearches", "apiVersion": "2025-07-01", - "name": "Tailscale_(CCF)_Hunting_Query_7", + "name": "Tailscale_(CCF)_Hunting_Query_12", "location": "[parameters('workspace-location')]", "properties": { "eTag": "*", - "displayName": "Tailscale: Users with zero devices", + "displayName": "Tailscale: Subnet router CIDR exposure inventory", "category": "Hunting Queries", - "query": "Tailscale_Users_CL\n| summarize arg_max(TimeGenerated, *) by UserId\n| where DeviceCount == 0\n| where Status != \"suspended\"\n| project LoginName, DisplayName, Role, Status, DeviceCount, Created, LastSeen\n| order by LastSeen asc\n", + "query": "let exitRoutes = dynamic([\"0.0.0.0/0\", \"::/0\"]);\nTailscale_Devices_CL\n| summarize arg_max(TimeGenerated, *) by DeviceId\n| where array_length(AdvertisedRoutes) > 0 or array_length(EnabledRoutes) > 0\n| extend SubnetAdvertised = set_difference(AdvertisedRoutes, exitRoutes)\n| extend SubnetEnabled = set_difference(EnabledRoutes, exitRoutes)\n| where array_length(SubnetAdvertised) > 0 or array_length(SubnetEnabled) > 0\n| project DeviceName, Hostname, User, Os, SubnetAdvertised, SubnetEnabled, Tags, LastSeen\n| order by DeviceName asc\n", "version": 2, "tags": [ { "name": "description", - "value": "Lists tailnet users who have no devices currently registered. Orphaned identities are candidates for off-boarding - they retain whatever role/permissions they were granted and can still create auth keys or invite others. Review against your HR / identity-provider join-leaver records." + "value": "Lists every device currently advertising or enabling subnet routes (i.e., bridging non-tailnet networks into the tailnet). Excludes pure exit-node advertisements (0.0.0.0/0 and ::/0) so only true subnet exposure is surfaced. Use this for change-control - confirm every CIDR listed is intended to be reachable from the tailnet, and that the device's ACL restricts who can route through it." }, { "name": "tactics", - "value": "InitialAccess" + "value": "LateralMovement" }, { "name": "techniques", - "value": "T1078" + "value": "T1021,T1018" } ] } @@ -7554,13 +8454,13 @@ { "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", "apiVersion": "2022-01-01-preview", - "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject7')._huntingQuerycontentId7),'/'))))]", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject12')._huntingQuerycontentId12),'/'))))]", "properties": { - "description": "Tailscale (CCF) Hunting Query 7", - "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject7')._huntingQuerycontentId7)]", - "contentId": "[variables('huntingQueryObject7')._huntingQuerycontentId7]", + "description": "Tailscale (CCF) Hunting Query 12", + "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject12')._huntingQuerycontentId12)]", + "contentId": "[variables('huntingQueryObject12')._huntingQuerycontentId12]", "kind": "HuntingQuery", - "version": "[variables('huntingQueryObject7').huntingQueryVersion7]", + "version": "[variables('huntingQueryObject12').huntingQueryVersion12]", "source": { "kind": "Solution", "name": "Tailscale (CCF)", @@ -7571,7 +8471,7 @@ "email": "[variables('_email')]" }, "support": { - "name": "Community", + "name": "Tailscale (CCF)", "tier": "Community", "email": "ccfconnectors.county118@passmail.com", "link": "https://github.com/Azure/Azure-Sentinel/issues" @@ -7585,53 +8485,53 @@ "packageName": "[variables('_solutionName')]", "packageId": "[variables('_solutionId')]", "contentSchemaVersion": "3.0.0", - "contentId": "[variables('huntingQueryObject7')._huntingQuerycontentId7]", + "contentId": "[variables('huntingQueryObject12')._huntingQuerycontentId12]", "contentKind": "HuntingQuery", - "displayName": "Tailscale: Users with zero devices", - "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject7')._huntingQuerycontentId7,'-', '1.0.0')))]", - "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject7')._huntingQuerycontentId7,'-', '1.0.0')))]", + "displayName": "Tailscale: Subnet router CIDR exposure inventory", + "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject12')._huntingQuerycontentId12,'-', '1.0.0')))]", + "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject12')._huntingQuerycontentId12,'-', '1.0.0')))]", "version": "1.0.0" } }, { "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", "apiVersion": "2023-04-01-preview", - "name": "[variables('huntingQueryObject8').huntingQueryTemplateSpecName8]", + "name": "[variables('huntingQueryObject13').huntingQueryTemplateSpecName13]", "location": "[parameters('workspace-location')]", "dependsOn": [ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleSplitDnsPerDomainChanges_HuntingQueries Hunting Query with template version 3.0.5", + "description": "TailscalePremiumNewNodePairs_HuntingQueries Hunting Query with template version 3.0.1", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "[variables('huntingQueryObject8').huntingQueryVersion8]", + "contentVersion": "[variables('huntingQueryObject13').huntingQueryVersion13]", "parameters": {}, "variables": {}, "resources": [ { "type": "Microsoft.OperationalInsights/savedSearches", "apiVersion": "2025-07-01", - "name": "Tailscale_(CCF)_Hunting_Query_8", + "name": "Tailscale_(CCF)_Hunting_Query_13", "location": "[parameters('workspace-location')]", "properties": { "eTag": "*", - "displayName": "Tailscale: Split-DNS per-domain change history", + "displayName": "Tailscale Premium: New src->dst node pairs (lateral movement candidates)", "category": "Hunting Queries", - "query": "Tailscale_Audit_CL\n| where Action == \"UPDATE\"\n| where tostring(Target.type) == \"TAILNET\"\n| where tostring(Target.property) == \"DNS_SPLIT_DNS\"\n| extend ActorLogin = tostring(Actor.loginName)\n| extend OldKeys = bag_keys(Old), NewKeys = bag_keys(New)\n| extend AllDomains = set_union(OldKeys, NewKeys)\n| mv-expand Domain = AllDomains to typeof(string)\n| extend OldResolvers = Old[Domain], NewResolvers = New[Domain]\n| extend Change = case(\n isnull(OldResolvers) and isnotnull(NewResolvers), \"added\",\n isnotnull(OldResolvers) and isnull(NewResolvers), \"removed\",\n tostring(OldResolvers) != tostring(NewResolvers), \"resolvers-changed\",\n \"unchanged\")\n| where Change != \"unchanged\"\n| project TimeGenerated, ActorLogin, Domain, Change, OldResolvers, NewResolvers\n| order by TimeGenerated desc\n", + "query": "let recent = 1d;\nlet baseline = 7d;\nlet recentPairs =\n Tailscale_Network_CL\n | where TimeGenerated > ago(recent)\n | mv-expand t = VirtualTraffic\n | extend Src = tostring(t.src), Dst = tostring(t.dst), Proto = toint(t.proto)\n | summarize FirstSeen = min(TimeGenerated), TxBytes = sum(tolong(t.txBytes)), RxBytes = sum(tolong(t.rxBytes)), FlowCount = count() by Src, Dst, Proto;\nlet baselinePairs =\n Tailscale_Network_CL\n | where TimeGenerated between (ago(baseline + recent) .. ago(recent))\n | mv-expand t = VirtualTraffic\n | extend Src = tostring(t.src), Dst = tostring(t.dst), Proto = toint(t.proto)\n | distinct Src, Dst, Proto;\nrecentPairs\n| join kind=leftanti baselinePairs on Src, Dst, Proto\n| order by FirstSeen asc\n", "version": 2, "tags": [ { "name": "description", - "value": "Reconstructs the per-domain Split-DNS change history from the audit log. For each modification event, expands the Old and New documents and surfaces which domains were added, removed, or had their resolver IPs changed. Useful for forensic review after a suspected DNS hijack and for confirming that a planned DNS migration deployed as intended." + "value": "Identifies tailnet src->dst pairs observed in the last 24h that were NOT observed in the prior 7-day baseline. Useful for spotting lateral movement to nodes that don't usually talk to each other. Requires Tailscale Premium or Enterprise (network flow logs)." }, { "name": "tactics", - "value": "DefenseEvasion,CommandAndControl" + "value": "LateralMovement,Discovery" }, { "name": "techniques", - "value": "T1556,T1568" + "value": "T1021,T1018" } ] } @@ -7639,13 +8539,13 @@ { "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", "apiVersion": "2022-01-01-preview", - "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject8')._huntingQuerycontentId8),'/'))))]", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject13')._huntingQuerycontentId13),'/'))))]", "properties": { - "description": "Tailscale (CCF) Hunting Query 8", - "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject8')._huntingQuerycontentId8)]", - "contentId": "[variables('huntingQueryObject8')._huntingQuerycontentId8]", + "description": "Tailscale (CCF) Hunting Query 13", + "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject13')._huntingQuerycontentId13)]", + "contentId": "[variables('huntingQueryObject13')._huntingQuerycontentId13]", "kind": "HuntingQuery", - "version": "[variables('huntingQueryObject8').huntingQueryVersion8]", + "version": "[variables('huntingQueryObject13').huntingQueryVersion13]", "source": { "kind": "Solution", "name": "Tailscale (CCF)", @@ -7656,7 +8556,7 @@ "email": "[variables('_email')]" }, "support": { - "name": "Community", + "name": "Tailscale (CCF)", "tier": "Community", "email": "ccfconnectors.county118@passmail.com", "link": "https://github.com/Azure/Azure-Sentinel/issues" @@ -7670,53 +8570,53 @@ "packageName": "[variables('_solutionName')]", "packageId": "[variables('_solutionId')]", "contentSchemaVersion": "3.0.0", - "contentId": "[variables('huntingQueryObject8')._huntingQuerycontentId8]", + "contentId": "[variables('huntingQueryObject13')._huntingQuerycontentId13]", "contentKind": "HuntingQuery", - "displayName": "Tailscale: Split-DNS per-domain change history", - "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject8')._huntingQuerycontentId8,'-', '1.0.0')))]", - "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject8')._huntingQuerycontentId8,'-', '1.0.0')))]", + "displayName": "Tailscale Premium: New src->dst node pairs (lateral movement candidates)", + "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject13')._huntingQuerycontentId13,'-', '1.0.0')))]", + "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject13')._huntingQuerycontentId13,'-', '1.0.0')))]", "version": "1.0.0" } }, { "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", "apiVersion": "2023-04-01-preview", - "name": "[variables('huntingQueryObject9').huntingQueryTemplateSpecName9]", + "name": "[variables('huntingQueryObject14').huntingQueryTemplateSpecName14]", "location": "[parameters('workspace-location')]", "dependsOn": [ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleDevicesWithSshEnabled_HuntingQueries Hunting Query with template version 3.0.5", + "description": "TailscalePremiumTopTalkers_HuntingQueries Hunting Query with template version 3.0.1", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "[variables('huntingQueryObject9').huntingQueryVersion9]", + "contentVersion": "[variables('huntingQueryObject14').huntingQueryVersion14]", "parameters": {}, "variables": {}, "resources": [ { "type": "Microsoft.OperationalInsights/savedSearches", "apiVersion": "2025-07-01", - "name": "Tailscale_(CCF)_Hunting_Query_9", + "name": "Tailscale_(CCF)_Hunting_Query_14", "location": "[parameters('workspace-location')]", "properties": { "eTag": "*", - "displayName": "Tailscale: Devices with Tailscale SSH enabled", + "displayName": "Tailscale Premium: Top talkers by bytes (virtual traffic)", "category": "Hunting Queries", - "query": "Tailscale_Devices_CL\n| summarize arg_max(TimeGenerated, *) by DeviceId\n| where SshEnabled == true\n| project DeviceName, Hostname, User, Os, Distro, ClientVersion, LastSeen, Tags\n| order by DeviceName asc\n", + "query": "Tailscale_Network_CL\n| where TimeGenerated > ago(1d)\n| mv-expand t = VirtualTraffic\n| extend Src = tostring(t.src), Dst = tostring(t.dst), Proto = toint(t.proto), TxBytes = tolong(t.txBytes), RxBytes = tolong(t.rxBytes)\n| summarize TotalBytes = sum(TxBytes + RxBytes), TotalPackets = sum(tolong(t.txPkts) + tolong(t.rxPkts)), FlowCount = count() by Src, Dst, Proto\n| extend TotalMB = round(TotalBytes / 1024.0 / 1024.0, 2)\n| top 50 by TotalBytes\n", "version": 2, "tags": [ { "name": "description", - "value": "Inventory of devices that currently have Tailscale SSH enabled. Tailscale SSH delivers SSH access over the tailnet using Tailscale identity (no SSH keys needed) and is governed by the SSH ACL block in the policy file. Cross-reference this list with the SSH ACL to confirm only the intended devices are reachable as SSH targets." + "value": "Identifies tailnet src->dst pairs ranked by total bytes transferred over the last 24h. Useful for capacity planning, identifying data-heavy flows, and spotting unexpected volume that could indicate data staging. Requires Tailscale Premium or Enterprise." }, { "name": "tactics", - "value": "LateralMovement,Persistence" + "value": "Exfiltration,Collection" }, { "name": "techniques", - "value": "T1021" + "value": "T1041,T1567" } ] } @@ -7724,13 +8624,13 @@ { "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", "apiVersion": "2022-01-01-preview", - "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject9')._huntingQuerycontentId9),'/'))))]", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject14')._huntingQuerycontentId14),'/'))))]", "properties": { - "description": "Tailscale (CCF) Hunting Query 9", - "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject9')._huntingQuerycontentId9)]", - "contentId": "[variables('huntingQueryObject9')._huntingQuerycontentId9]", + "description": "Tailscale (CCF) Hunting Query 14", + "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject14')._huntingQuerycontentId14)]", + "contentId": "[variables('huntingQueryObject14')._huntingQuerycontentId14]", "kind": "HuntingQuery", - "version": "[variables('huntingQueryObject9').huntingQueryVersion9]", + "version": "[variables('huntingQueryObject14').huntingQueryVersion14]", "source": { "kind": "Solution", "name": "Tailscale (CCF)", @@ -7741,7 +8641,7 @@ "email": "[variables('_email')]" }, "support": { - "name": "Community", + "name": "Tailscale (CCF)", "tier": "Community", "email": "ccfconnectors.county118@passmail.com", "link": "https://github.com/Azure/Azure-Sentinel/issues" @@ -7755,53 +8655,53 @@ "packageName": "[variables('_solutionName')]", "packageId": "[variables('_solutionId')]", "contentSchemaVersion": "3.0.0", - "contentId": "[variables('huntingQueryObject9')._huntingQuerycontentId9]", + "contentId": "[variables('huntingQueryObject14')._huntingQuerycontentId14]", "contentKind": "HuntingQuery", - "displayName": "Tailscale: Devices with Tailscale SSH enabled", - "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject9')._huntingQuerycontentId9,'-', '1.0.0')))]", - "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject9')._huntingQuerycontentId9,'-', '1.0.0')))]", + "displayName": "Tailscale Premium: Top talkers by bytes (virtual traffic)", + "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject14')._huntingQuerycontentId14,'-', '1.0.0')))]", + "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject14')._huntingQuerycontentId14,'-', '1.0.0')))]", "version": "1.0.0" } }, { "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", "apiVersion": "2023-04-01-preview", - "name": "[variables('huntingQueryObject10').huntingQueryTemplateSpecName10]", + "name": "[variables('huntingQueryObject15').huntingQueryTemplateSpecName15]", "location": "[parameters('workspace-location')]", "dependsOn": [ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleExternalDeviceInventory_HuntingQueries Hunting Query with template version 3.0.5", + "description": "TailscalePremiumExitNodeUsage_HuntingQueries Hunting Query with template version 3.0.1", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "[variables('huntingQueryObject10').huntingQueryVersion10]", + "contentVersion": "[variables('huntingQueryObject15').huntingQueryVersion15]", "parameters": {}, "variables": {}, "resources": [ { "type": "Microsoft.OperationalInsights/savedSearches", "apiVersion": "2025-07-01", - "name": "Tailscale_(CCF)_Hunting_Query_10", + "name": "Tailscale_(CCF)_Hunting_Query_15", "location": "[parameters('workspace-location')]", "properties": { "eTag": "*", - "displayName": "Tailscale: External (shared-in) device inventory", + "displayName": "Tailscale Premium: Exit-node usage patterns", "category": "Hunting Queries", - "query": "Tailscale_Devices_CL\n| summarize arg_max(TimeGenerated, *) by DeviceId\n| where IsExternal == true\n| project DeviceName, Hostname, User, Os, ClientVersion, Created, LastSeen, Tags\n| order by Created desc\n", + "query": "Tailscale_Network_CL\n| where TimeGenerated > ago(1d)\n| where array_length(ExitTraffic) > 0\n| mv-expand t = ExitTraffic\n| extend Src = tostring(t.src), ExitDst = tostring(t.dst), Proto = toint(t.proto), TxBytes = tolong(t.txBytes), RxBytes = tolong(t.rxBytes)\n| summarize TotalBytes = sum(TxBytes + RxBytes), FlowCount = count(), ExitDestinations = make_set(ExitDst, 25)\n by NodeId, SrcNodeName = tostring(SrcNode.name), Src\n| extend TotalMB = round(TotalBytes / 1024.0 / 1024.0, 2)\n| order by TotalBytes desc\n| take 100\n", "version": 2, "tags": [ { "name": "description", - "value": "Inventory of external (shared-in) devices currently active in the tailnet. These devices belong to another tailnet and have been admitted via a Tailscale sharing arrangement. Confirm each entry maps to a documented collaboration and that the corresponding ACL restricts the device to the intended resources." + "value": "Identifies traffic leaving the tailnet via exit nodes. Exit node use is typically intentional (regional egress, privacy routing) but unexpected exit-node traffic from a node should be investigated as a potential pivot point or unsanctioned egress. Requires Tailscale Premium or Enterprise." }, { "name": "tactics", - "value": "InitialAccess" + "value": "CommandAndControl,Exfiltration" }, { "name": "techniques", - "value": "T1078" + "value": "T1090,T1041" } ] } @@ -7809,13 +8709,13 @@ { "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", "apiVersion": "2022-01-01-preview", - "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject10')._huntingQuerycontentId10),'/'))))]", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject15')._huntingQuerycontentId15),'/'))))]", "properties": { - "description": "Tailscale (CCF) Hunting Query 10", - "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject10')._huntingQuerycontentId10)]", - "contentId": "[variables('huntingQueryObject10')._huntingQuerycontentId10]", + "description": "Tailscale (CCF) Hunting Query 15", + "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject15')._huntingQuerycontentId15)]", + "contentId": "[variables('huntingQueryObject15')._huntingQuerycontentId15]", "kind": "HuntingQuery", - "version": "[variables('huntingQueryObject10').huntingQueryVersion10]", + "version": "[variables('huntingQueryObject15').huntingQueryVersion15]", "source": { "kind": "Solution", "name": "Tailscale (CCF)", @@ -7826,7 +8726,7 @@ "email": "[variables('_email')]" }, "support": { - "name": "Community", + "name": "Tailscale (CCF)", "tier": "Community", "email": "ccfconnectors.county118@passmail.com", "link": "https://github.com/Azure/Azure-Sentinel/issues" @@ -7840,53 +8740,53 @@ "packageName": "[variables('_solutionName')]", "packageId": "[variables('_solutionId')]", "contentSchemaVersion": "3.0.0", - "contentId": "[variables('huntingQueryObject10')._huntingQuerycontentId10]", + "contentId": "[variables('huntingQueryObject15')._huntingQuerycontentId15]", "contentKind": "HuntingQuery", - "displayName": "Tailscale: External (shared-in) device inventory", - "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject10')._huntingQuerycontentId10,'-', '1.0.0')))]", - "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject10')._huntingQuerycontentId10,'-', '1.0.0')))]", + "displayName": "Tailscale Premium: Exit-node usage patterns", + "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject15')._huntingQuerycontentId15,'-', '1.0.0')))]", + "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject15')._huntingQuerycontentId15,'-', '1.0.0')))]", "version": "1.0.0" } }, { "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", "apiVersion": "2023-04-01-preview", - "name": "[variables('huntingQueryObject11').huntingQueryTemplateSpecName11]", + "name": "[variables('huntingQueryObject16').huntingQueryTemplateSpecName16]", "location": "[parameters('workspace-location')]", "dependsOn": [ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleOutdatedClients_HuntingQueries Hunting Query with template version 3.0.5", + "description": "TailscalePremiumBeaconingCandidates_HuntingQueries Hunting Query with template version 3.0.1", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "[variables('huntingQueryObject11').huntingQueryVersion11]", + "contentVersion": "[variables('huntingQueryObject16').huntingQueryVersion16]", "parameters": {}, "variables": {}, "resources": [ { "type": "Microsoft.OperationalInsights/savedSearches", "apiVersion": "2025-07-01", - "name": "Tailscale_(CCF)_Hunting_Query_11", + "name": "Tailscale_(CCF)_Hunting_Query_16", "location": "[parameters('workspace-location')]", "properties": { "eTag": "*", - "displayName": "Tailscale: Devices with outdated client version", + "displayName": "Tailscale Premium: Beaconing candidates (regular periodic flows)", "category": "Hunting Queries", - "query": "Tailscale_Devices_CL\n| summarize arg_max(TimeGenerated, *) by DeviceId\n| where UpdateAvailable == true\n| project DeviceName, Hostname, User, Os, Distro, ClientVersion, LastSeen, Tags\n| order by ClientVersion asc, LastSeen desc\n", + "query": "let lookback = 2d;\nlet minFlows = 10;\nlet beaconPercentThreshold = 80.0;\nTailscale_Network_CL\n| where TimeGenerated > ago(lookback)\n| mv-expand t = VirtualTraffic\n| extend Src = tostring(t.src), Dst = tostring(t.dst), Proto = toint(t.proto)\n| project TimeGenerated, Src, Dst, Proto\n| sort by Src asc, Dst asc, Proto asc, TimeGenerated asc\n| serialize\n| extend NextTime = next(TimeGenerated), NextSrc = next(Src), NextDst = next(Dst), NextProto = next(Proto)\n| where Src == NextSrc and Dst == NextDst and Proto == NextProto\n| extend DeltaSec = datetime_diff('second', NextTime, TimeGenerated)\n| where DeltaSec > 5\n| summarize DeltaCount = count() by Src, Dst, Proto, DeltaSec\n| summarize (MostFrequentDeltaCount, MostFrequentDeltaSec) = arg_max(DeltaCount, DeltaSec), TotalFlows = sum(DeltaCount) by Src, Dst, Proto\n| where TotalFlows >= minFlows\n| extend BeaconPercent = round(MostFrequentDeltaCount * 100.0 / TotalFlows, 1)\n| where BeaconPercent >= beaconPercentThreshold\n| order by BeaconPercent desc\n", "version": 2, "tags": [ { "name": "description", - "value": "Lists tailnet devices that report UpdateAvailable=true on the latest snapshot. Tailscale releases security and feature updates regularly; a device showing UpdateAvailable lacks the most recent improvements. Use this list to plan a fleet update, especially before enabling new ACL features that require minimum client versions." + "value": "Identifies flows that recur at a highly regular interval, which is the signature of C2 beaconing or scheduled exfiltration jobs. Looks for src->dst pairs where 80%+ of inter-flow gaps cluster around the same delta. Requires Tailscale Premium or Enterprise." }, { "name": "tactics", - "value": "DefenseEvasion" + "value": "CommandAndControl,Exfiltration" }, { "name": "techniques", - "value": "T1562" + "value": "T1071,T1095,T1029" } ] } @@ -7894,13 +8794,13 @@ { "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", "apiVersion": "2022-01-01-preview", - "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject11')._huntingQuerycontentId11),'/'))))]", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject16')._huntingQuerycontentId16),'/'))))]", "properties": { - "description": "Tailscale (CCF) Hunting Query 11", - "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject11')._huntingQuerycontentId11)]", - "contentId": "[variables('huntingQueryObject11')._huntingQuerycontentId11]", + "description": "Tailscale (CCF) Hunting Query 16", + "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject16')._huntingQuerycontentId16)]", + "contentId": "[variables('huntingQueryObject16')._huntingQuerycontentId16]", "kind": "HuntingQuery", - "version": "[variables('huntingQueryObject11').huntingQueryVersion11]", + "version": "[variables('huntingQueryObject16').huntingQueryVersion16]", "source": { "kind": "Solution", "name": "Tailscale (CCF)", @@ -7911,7 +8811,7 @@ "email": "[variables('_email')]" }, "support": { - "name": "Community", + "name": "Tailscale (CCF)", "tier": "Community", "email": "ccfconnectors.county118@passmail.com", "link": "https://github.com/Azure/Azure-Sentinel/issues" @@ -7925,53 +8825,53 @@ "packageName": "[variables('_solutionName')]", "packageId": "[variables('_solutionId')]", "contentSchemaVersion": "3.0.0", - "contentId": "[variables('huntingQueryObject11')._huntingQuerycontentId11]", + "contentId": "[variables('huntingQueryObject16')._huntingQuerycontentId16]", "contentKind": "HuntingQuery", - "displayName": "Tailscale: Devices with outdated client version", - "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject11')._huntingQuerycontentId11,'-', '1.0.0')))]", - "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject11')._huntingQuerycontentId11,'-', '1.0.0')))]", + "displayName": "Tailscale Premium: Beaconing candidates (regular periodic flows)", + "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject16')._huntingQuerycontentId16,'-', '1.0.0')))]", + "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject16')._huntingQuerycontentId16,'-', '1.0.0')))]", "version": "1.0.0" } }, { "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", "apiVersion": "2023-04-01-preview", - "name": "[variables('huntingQueryObject12').huntingQueryTemplateSpecName12]", + "name": "[variables('huntingQueryObject17').huntingQueryTemplateSpecName17]", "location": "[parameters('workspace-location')]", "dependsOn": [ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleSubnetRouteExposure_HuntingQueries Hunting Query with template version 3.0.5", + "description": "TailscalePremiumPostureInventory_HuntingQueries Hunting Query with template version 3.0.1", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "[variables('huntingQueryObject12').huntingQueryVersion12]", + "contentVersion": "[variables('huntingQueryObject17').huntingQueryVersion17]", "parameters": {}, "variables": {}, "resources": [ { "type": "Microsoft.OperationalInsights/savedSearches", "apiVersion": "2025-07-01", - "name": "Tailscale_(CCF)_Hunting_Query_12", + "name": "Tailscale_(CCF)_Hunting_Query_17", "location": "[parameters('workspace-location')]", "properties": { "eTag": "*", - "displayName": "Tailscale: Subnet router CIDR exposure inventory", + "displayName": "Tailscale Premium: Current posture integration inventory", "category": "Hunting Queries", - "query": "let exitRoutes = dynamic([\"0.0.0.0/0\", \"::/0\"]);\nTailscale_Devices_CL\n| summarize arg_max(TimeGenerated, *) by DeviceId\n| where array_length(AdvertisedRoutes) > 0 or array_length(EnabledRoutes) > 0\n| extend SubnetAdvertised = set_difference(AdvertisedRoutes, exitRoutes)\n| extend SubnetEnabled = set_difference(EnabledRoutes, exitRoutes)\n| where array_length(SubnetAdvertised) > 0 or array_length(SubnetEnabled) > 0\n| project DeviceName, Hostname, User, Os, SubnetAdvertised, SubnetEnabled, Tags, LastSeen\n| order by DeviceName asc\n", + "query": "Tailscale_PostureIntegrations_CL\n| summarize arg_max(TimeGenerated, *) by IntegrationId\n| project IntegrationId, Provider, ClientId, TenantId_Provider, Status, ConfigOverwrites\n| order by Provider asc\n", "version": 2, "tags": [ { "name": "description", - "value": "Lists every device currently advertising or enabling subnet routes (i.e., bridging non-tailnet networks into the tailnet). Excludes pure exit-node advertisements (0.0.0.0/0 and ::/0) so only true subnet exposure is surfaced. Use this for change-control - confirm every CIDR listed is intended to be reachable from the tailnet, and that the device's ACL restricts who can route through it." + "value": "Identifies the current set of device-posture integrations configured on the tailnet (latest snapshot per integration). Useful for compliance attestation - confirms what MDM/EDR systems are connected and what their status is. Compare against the expected baseline to detect drift. Requires Tailscale Premium or Enterprise." }, { "name": "tactics", - "value": "LateralMovement" + "value": "DefenseEvasion" }, { "name": "techniques", - "value": "T1021,T1018" + "value": "T1562" } ] } @@ -7979,13 +8879,13 @@ { "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", "apiVersion": "2022-01-01-preview", - "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject12')._huntingQuerycontentId12),'/'))))]", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject17')._huntingQuerycontentId17),'/'))))]", "properties": { - "description": "Tailscale (CCF) Hunting Query 12", - "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject12')._huntingQuerycontentId12)]", - "contentId": "[variables('huntingQueryObject12')._huntingQuerycontentId12]", + "description": "Tailscale (CCF) Hunting Query 17", + "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject17')._huntingQuerycontentId17)]", + "contentId": "[variables('huntingQueryObject17')._huntingQuerycontentId17]", "kind": "HuntingQuery", - "version": "[variables('huntingQueryObject12').huntingQueryVersion12]", + "version": "[variables('huntingQueryObject17').huntingQueryVersion17]", "source": { "kind": "Solution", "name": "Tailscale (CCF)", @@ -7996,7 +8896,7 @@ "email": "[variables('_email')]" }, "support": { - "name": "Community", + "name": "Tailscale (CCF)", "tier": "Community", "email": "ccfconnectors.county118@passmail.com", "link": "https://github.com/Azure/Azure-Sentinel/issues" @@ -8010,53 +8910,53 @@ "packageName": "[variables('_solutionName')]", "packageId": "[variables('_solutionId')]", "contentSchemaVersion": "3.0.0", - "contentId": "[variables('huntingQueryObject12')._huntingQuerycontentId12]", + "contentId": "[variables('huntingQueryObject17')._huntingQuerycontentId17]", "contentKind": "HuntingQuery", - "displayName": "Tailscale: Subnet router CIDR exposure inventory", - "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject12')._huntingQuerycontentId12,'-', '1.0.0')))]", - "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject12')._huntingQuerycontentId12,'-', '1.0.0')))]", + "displayName": "Tailscale Premium: Current posture integration inventory", + "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject17')._huntingQuerycontentId17,'-', '1.0.0')))]", + "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject17')._huntingQuerycontentId17,'-', '1.0.0')))]", "version": "1.0.0" } }, { "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", "apiVersion": "2023-04-01-preview", - "name": "[variables('huntingQueryObject13').huntingQueryTemplateSpecName13]", + "name": "[variables('huntingQueryObject18').huntingQueryTemplateSpecName18]", "location": "[parameters('workspace-location')]", "dependsOn": [ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscalePremiumNewNodePairs_HuntingQueries Hunting Query with template version 3.0.5", + "description": "TailscalePremiumDerpRelayPersistence_HuntingQueries Hunting Query with template version 3.0.1", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "[variables('huntingQueryObject13').huntingQueryVersion13]", + "contentVersion": "[variables('huntingQueryObject18').huntingQueryVersion18]", "parameters": {}, "variables": {}, "resources": [ { "type": "Microsoft.OperationalInsights/savedSearches", "apiVersion": "2025-07-01", - "name": "Tailscale_(CCF)_Hunting_Query_13", + "name": "Tailscale_(CCF)_Hunting_Query_18", "location": "[parameters('workspace-location')]", "properties": { "eTag": "*", - "displayName": "Tailscale Premium: New src->dst node pairs (lateral movement candidates)", + "displayName": "Tailscale Premium: Devices with persistent DERP relay usage", "category": "Hunting Queries", - "query": "let recent = 1d;\nlet baseline = 7d;\nlet recentPairs =\n Tailscale_Network_CL\n | where TimeGenerated > ago(recent)\n | mv-expand t = VirtualTraffic\n | extend Src = tostring(t.src), Dst = tostring(t.dst), Proto = toint(t.proto)\n | summarize FirstSeen = min(TimeGenerated), TxBytes = sum(tolong(t.txBytes)), RxBytes = sum(tolong(t.rxBytes)), FlowCount = count() by Src, Dst, Proto;\nlet baselinePairs =\n Tailscale_Network_CL\n | where TimeGenerated between (ago(baseline + recent) .. ago(recent))\n | mv-expand t = VirtualTraffic\n | extend Src = tostring(t.src), Dst = tostring(t.dst), Proto = toint(t.proto)\n | distinct Src, Dst, Proto;\nrecentPairs\n| join kind=leftanti baselinePairs on Src, Dst, Proto\n| order by FirstSeen asc\n", + "query": "Tailscale_Network_CL\n| where TimeGenerated > ago(24h)\n| summarize\n TotalFlows = count(),\n RelayedFlows = countif(IsRelayed),\n DistinctDsts = dcount(DstNodeName),\n FirstFlow = min(TimeGenerated),\n LastFlow = max(TimeGenerated)\n by SrcNodeName, SrcUser, SrcOs, SrcTags=tostring(SrcTags)\n| where TotalFlows >= 50\n| extend RelayedPct = round(100.0 * RelayedFlows / TotalFlows, 1)\n| where RelayedPct >= 30.0\n| project SrcNodeName, SrcUser, SrcOs, SrcTags, TotalFlows, RelayedFlows, RelayedPct, DistinctDsts, FirstFlow, LastFlow\n| order by RelayedPct desc, TotalFlows desc\n", "version": 2, "tags": [ { "name": "description", - "value": "Lists tailnet src->dst pairs observed in the last 24h that were NOT observed in the prior 7-day baseline. Useful for spotting lateral movement to nodes that don't usually talk to each other. Requires Tailscale Premium or Enterprise (network flow logs)." + "value": "Identifies devices that have consistently fallen back to DERP relay (IsRelayed = true) over the past 24 hours. Sustained relay usage points to NAT/firewall misconfiguration on the device's network, a tunnel-blocking middlebox, or in rare cases deliberate evasion attempting to obscure direct peer-to-peer paths. Useful for proactive network-hygiene investigation and capacity planning. Requires Tailscale Premium or Enterprise." }, { "name": "tactics", - "value": "LateralMovement,Discovery" + "value": "CommandAndControl" }, { "name": "techniques", - "value": "T1021,T1018" + "value": "T1572" } ] } @@ -8064,13 +8964,13 @@ { "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", "apiVersion": "2022-01-01-preview", - "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject13')._huntingQuerycontentId13),'/'))))]", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject18')._huntingQuerycontentId18),'/'))))]", "properties": { - "description": "Tailscale (CCF) Hunting Query 13", - "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject13')._huntingQuerycontentId13)]", - "contentId": "[variables('huntingQueryObject13')._huntingQuerycontentId13]", + "description": "Tailscale (CCF) Hunting Query 18", + "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject18')._huntingQuerycontentId18)]", + "contentId": "[variables('huntingQueryObject18')._huntingQuerycontentId18]", "kind": "HuntingQuery", - "version": "[variables('huntingQueryObject13').huntingQueryVersion13]", + "version": "[variables('huntingQueryObject18').huntingQueryVersion18]", "source": { "kind": "Solution", "name": "Tailscale (CCF)", @@ -8081,7 +8981,7 @@ "email": "[variables('_email')]" }, "support": { - "name": "Community", + "name": "Tailscale (CCF)", "tier": "Community", "email": "ccfconnectors.county118@passmail.com", "link": "https://github.com/Azure/Azure-Sentinel/issues" @@ -8095,53 +8995,53 @@ "packageName": "[variables('_solutionName')]", "packageId": "[variables('_solutionId')]", "contentSchemaVersion": "3.0.0", - "contentId": "[variables('huntingQueryObject13')._huntingQuerycontentId13]", + "contentId": "[variables('huntingQueryObject18')._huntingQuerycontentId18]", "contentKind": "HuntingQuery", - "displayName": "Tailscale Premium: New src->dst node pairs (lateral movement candidates)", - "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject13')._huntingQuerycontentId13,'-', '1.0.0')))]", - "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject13')._huntingQuerycontentId13,'-', '1.0.0')))]", + "displayName": "Tailscale Premium: Devices with persistent DERP relay usage", + "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject18')._huntingQuerycontentId18,'-', '1.0.0')))]", + "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject18')._huntingQuerycontentId18,'-', '1.0.0')))]", "version": "1.0.0" } }, { "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", "apiVersion": "2023-04-01-preview", - "name": "[variables('huntingQueryObject14').huntingQueryTemplateSpecName14]", + "name": "[variables('huntingQueryObject19').huntingQueryTemplateSpecName19]", "location": "[parameters('workspace-location')]", "dependsOn": [ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscalePremiumTopTalkers_HuntingQueries Hunting Query with template version 3.0.5", + "description": "TailscalePremiumTaggedServiceFanIn_HuntingQueries Hunting Query with template version 3.0.1", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "[variables('huntingQueryObject14').huntingQueryVersion14]", + "contentVersion": "[variables('huntingQueryObject19').huntingQueryVersion19]", "parameters": {}, "variables": {}, "resources": [ { "type": "Microsoft.OperationalInsights/savedSearches", "apiVersion": "2025-07-01", - "name": "Tailscale_(CCF)_Hunting_Query_14", + "name": "Tailscale_(CCF)_Hunting_Query_19", "location": "[parameters('workspace-location')]", "properties": { "eTag": "*", - "displayName": "Tailscale Premium: Top talkers by bytes (virtual traffic)", + "displayName": "Tailscale Premium: Tagged services with broad inbound exposure", "category": "Hunting Queries", - "query": "Tailscale_Network_CL\n| where TimeGenerated > ago(1d)\n| mv-expand t = VirtualTraffic\n| extend Src = tostring(t.src), Dst = tostring(t.dst), Proto = toint(t.proto), TxBytes = tolong(t.txBytes), RxBytes = tolong(t.rxBytes)\n| summarize TotalBytes = sum(TxBytes + RxBytes), TotalPackets = sum(tolong(t.txPkts) + tolong(t.rxPkts)), FlowCount = count() by Src, Dst, Proto\n| extend TotalMB = round(TotalBytes / 1024.0 / 1024.0, 2)\n| top 50 by TotalBytes\n", + "query": "Tailscale_Network_CL\n| where TimeGenerated > ago(7d)\n| where isnotempty(DstTags)\n| where HasVirtualTraffic or HasSubnetTraffic\n| summarize\n DistinctSrcUsers = dcount(SrcUser),\n DistinctSrcDevices = dcount(SrcNodeName),\n DistinctSrcOs = dcount(SrcOs),\n Flows = count(),\n FirstFlow = min(TimeGenerated),\n LastFlow = max(TimeGenerated)\n by DstNodeName, DstTags=tostring(DstTags)\n| extend ShortDstName = tostring(split(DstNodeName, \".\")[0])\n| project ShortDstName, DstTags, DistinctSrcDevices, DistinctSrcUsers, DistinctSrcOs, Flows, FirstFlow, LastFlow\n| order by DistinctSrcDevices desc, Flows desc\n", "version": 2, "tags": [ { "name": "description", - "value": "Ranks tailnet src->dst pairs by total bytes transferred over the last 24h. Useful for capacity planning, identifying data-heavy flows, and spotting unexpected volume that could indicate data staging. Requires Tailscale Premium or Enterprise." + "value": "Identifies tagged services (devices with non-empty DstTags) ranked by inbound diversity over 7 days. Baseline: a tag:plex serving the household typically sees connections from 1-3 user devices; a tag:db or tag:internal that receives connections from 10+ distinct users or 3+ OS families may indicate ACL drift, credential sharing, or unauthorised access. Sort by DistinctSrcDevices to surface services with the broadest blast-radius. Requires Tailscale Premium or Enterprise." }, { "name": "tactics", - "value": "Exfiltration,Collection" + "value": "LateralMovement,InitialAccess" }, { "name": "techniques", - "value": "T1041,T1567" + "value": "T1021,T1133" } ] } @@ -8149,13 +9049,13 @@ { "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", "apiVersion": "2022-01-01-preview", - "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject14')._huntingQuerycontentId14),'/'))))]", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject19')._huntingQuerycontentId19),'/'))))]", "properties": { - "description": "Tailscale (CCF) Hunting Query 14", - "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject14')._huntingQuerycontentId14)]", - "contentId": "[variables('huntingQueryObject14')._huntingQuerycontentId14]", + "description": "Tailscale (CCF) Hunting Query 19", + "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject19')._huntingQuerycontentId19)]", + "contentId": "[variables('huntingQueryObject19')._huntingQuerycontentId19]", "kind": "HuntingQuery", - "version": "[variables('huntingQueryObject14').huntingQueryVersion14]", + "version": "[variables('huntingQueryObject19').huntingQueryVersion19]", "source": { "kind": "Solution", "name": "Tailscale (CCF)", @@ -8166,7 +9066,7 @@ "email": "[variables('_email')]" }, "support": { - "name": "Community", + "name": "Tailscale (CCF)", "tier": "Community", "email": "ccfconnectors.county118@passmail.com", "link": "https://github.com/Azure/Azure-Sentinel/issues" @@ -8180,53 +9080,53 @@ "packageName": "[variables('_solutionName')]", "packageId": "[variables('_solutionId')]", "contentSchemaVersion": "3.0.0", - "contentId": "[variables('huntingQueryObject14')._huntingQuerycontentId14]", + "contentId": "[variables('huntingQueryObject19')._huntingQuerycontentId19]", "contentKind": "HuntingQuery", - "displayName": "Tailscale Premium: Top talkers by bytes (virtual traffic)", - "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject14')._huntingQuerycontentId14,'-', '1.0.0')))]", - "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject14')._huntingQuerycontentId14,'-', '1.0.0')))]", + "displayName": "Tailscale Premium: Tagged services with broad inbound exposure", + "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject19')._huntingQuerycontentId19,'-', '1.0.0')))]", + "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject19')._huntingQuerycontentId19,'-', '1.0.0')))]", "version": "1.0.0" } }, { "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", "apiVersion": "2023-04-01-preview", - "name": "[variables('huntingQueryObject15').huntingQueryTemplateSpecName15]", + "name": "[variables('huntingQueryObject20').huntingQueryTemplateSpecName20]", "location": "[parameters('workspace-location')]", "dependsOn": [ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscalePremiumExitNodeUsage_HuntingQueries Hunting Query with template version 3.0.5", + "description": "TailscalePremiumCrossTagFlowMatrix_HuntingQueries Hunting Query with template version 3.0.1", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "[variables('huntingQueryObject15').huntingQueryVersion15]", + "contentVersion": "[variables('huntingQueryObject20').huntingQueryVersion20]", "parameters": {}, "variables": {}, "resources": [ { "type": "Microsoft.OperationalInsights/savedSearches", "apiVersion": "2025-07-01", - "name": "Tailscale_(CCF)_Hunting_Query_15", + "name": "Tailscale_(CCF)_Hunting_Query_20", "location": "[parameters('workspace-location')]", "properties": { "eTag": "*", - "displayName": "Tailscale Premium: Exit-node usage patterns", + "displayName": "Tailscale Premium: Cross-tag flow matrix", "category": "Hunting Queries", - "query": "Tailscale_Network_CL\n| where TimeGenerated > ago(1d)\n| where array_length(ExitTraffic) > 0\n| mv-expand t = ExitTraffic\n| extend Src = tostring(t.src), ExitDst = tostring(t.dst), Proto = toint(t.proto), TxBytes = tolong(t.txBytes), RxBytes = tolong(t.rxBytes)\n| summarize TotalBytes = sum(TxBytes + RxBytes), FlowCount = count(), ExitDestinations = make_set(ExitDst, 25)\n by NodeId, SrcNodeName = tostring(SrcNode.name), Src\n| extend TotalMB = round(TotalBytes / 1024.0 / 1024.0, 2)\n| order by TotalBytes desc\n| take 100\n", + "query": "Tailscale_Network_CL\n| where TimeGenerated > ago(7d)\n| extend SrcCategory = case(\n isnotempty(SrcTags), tostring(SrcTags),\n isnotempty(SrcUser), \"\",\n \"\")\n| extend DstCategory = case(\n isnotempty(DstTags), tostring(DstTags),\n isnotempty(DstUser), \"\",\n \"\")\n| summarize\n Flows = count(),\n DistinctSrcDevices = dcount(SrcNodeName),\n DistinctDstDevices = dcount(DstNodeName),\n FirstSeen = min(TimeGenerated),\n LastSeen = max(TimeGenerated)\n by SrcCategory, DstCategory\n| extend SameTagLoop = SrcCategory == DstCategory and SrcCategory != \"\" and SrcCategory != \"\"\n| project SrcCategory, DstCategory, Flows, DistinctSrcDevices, DistinctDstDevices, SameTagLoop, FirstSeen, LastSeen\n| order by Flows desc\n", "version": 2, "tags": [ { "name": "description", - "value": "Summarises traffic leaving the tailnet via exit nodes. Exit node use is typically intentional (regional egress, privacy routing) but unexpected exit-node traffic from a node should be investigated as a potential pivot point or unsanctioned egress. Requires Tailscale Premium or Enterprise." + "value": "Identifies network flows pivoted by source-tag x destination-tag over the past 7 days, treating untagged user devices as ``. Highlights which tagged services interact - useful for ACL validation, detecting unexpected tag-to-tag traffic, and spotting tag-to-same-tag loops that can indicate worm-style propagation or service-mesh anomalies. Order by Flows to find the heaviest tag pairs; sort by DistinctSrcDevices to find broadly-used services. Requires Tailscale Premium or Enterprise." }, { "name": "tactics", - "value": "CommandAndControl,Exfiltration" + "value": "LateralMovement,Discovery" }, { "name": "techniques", - "value": "T1090,T1041" + "value": "T1021,T1046" } ] } @@ -8234,13 +9134,13 @@ { "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", "apiVersion": "2022-01-01-preview", - "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject15')._huntingQuerycontentId15),'/'))))]", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject20')._huntingQuerycontentId20),'/'))))]", "properties": { - "description": "Tailscale (CCF) Hunting Query 15", - "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject15')._huntingQuerycontentId15)]", - "contentId": "[variables('huntingQueryObject15')._huntingQuerycontentId15]", + "description": "Tailscale (CCF) Hunting Query 20", + "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject20')._huntingQuerycontentId20)]", + "contentId": "[variables('huntingQueryObject20')._huntingQuerycontentId20]", "kind": "HuntingQuery", - "version": "[variables('huntingQueryObject15').huntingQueryVersion15]", + "version": "[variables('huntingQueryObject20').huntingQueryVersion20]", "source": { "kind": "Solution", "name": "Tailscale (CCF)", @@ -8251,7 +9151,7 @@ "email": "[variables('_email')]" }, "support": { - "name": "Community", + "name": "Tailscale (CCF)", "tier": "Community", "email": "ccfconnectors.county118@passmail.com", "link": "https://github.com/Azure/Azure-Sentinel/issues" @@ -8265,53 +9165,53 @@ "packageName": "[variables('_solutionName')]", "packageId": "[variables('_solutionId')]", "contentSchemaVersion": "3.0.0", - "contentId": "[variables('huntingQueryObject15')._huntingQuerycontentId15]", + "contentId": "[variables('huntingQueryObject20')._huntingQuerycontentId20]", "contentKind": "HuntingQuery", - "displayName": "Tailscale Premium: Exit-node usage patterns", - "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject15')._huntingQuerycontentId15,'-', '1.0.0')))]", - "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject15')._huntingQuerycontentId15,'-', '1.0.0')))]", + "displayName": "Tailscale Premium: Cross-tag flow matrix", + "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject20')._huntingQuerycontentId20,'-', '1.0.0')))]", + "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject20')._huntingQuerycontentId20,'-', '1.0.0')))]", "version": "1.0.0" } }, { "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", "apiVersion": "2023-04-01-preview", - "name": "[variables('huntingQueryObject16').huntingQueryTemplateSpecName16]", + "name": "[variables('huntingQueryObject21').huntingQueryTemplateSpecName21]", "location": "[parameters('workspace-location')]", "dependsOn": [ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscalePremiumBeaconingCandidates_HuntingQueries Hunting Query with template version 3.0.5", + "description": "TailscalePremiumOffHoursFlows_HuntingQueries Hunting Query with template version 3.0.1", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "[variables('huntingQueryObject16').huntingQueryVersion16]", + "contentVersion": "[variables('huntingQueryObject21').huntingQueryVersion21]", "parameters": {}, "variables": {}, "resources": [ { "type": "Microsoft.OperationalInsights/savedSearches", "apiVersion": "2025-07-01", - "name": "Tailscale_(CCF)_Hunting_Query_16", + "name": "Tailscale_(CCF)_Hunting_Query_21", "location": "[parameters('workspace-location')]", "properties": { "eTag": "*", - "displayName": "Tailscale Premium: Beaconing candidates (regular periodic flows)", + "displayName": "Tailscale Premium: Network flows outside business hours", "category": "Hunting Queries", - "query": "let lookback = 2d;\nlet minFlows = 10;\nlet beaconPercentThreshold = 80.0;\nTailscale_Network_CL\n| where TimeGenerated > ago(lookback)\n| mv-expand t = VirtualTraffic\n| extend Src = tostring(t.src), Dst = tostring(t.dst), Proto = toint(t.proto)\n| project TimeGenerated, Src, Dst, Proto\n| sort by Src asc, Dst asc, Proto asc, TimeGenerated asc\n| serialize\n| extend NextTime = next(TimeGenerated), NextSrc = next(Src), NextDst = next(Dst), NextProto = next(Proto)\n| where Src == NextSrc and Dst == NextDst and Proto == NextProto\n| extend DeltaSec = datetime_diff('second', NextTime, TimeGenerated)\n| where DeltaSec > 5\n| summarize DeltaCount = count() by Src, Dst, Proto, DeltaSec\n| summarize (MostFrequentDeltaCount, MostFrequentDeltaSec) = arg_max(DeltaCount, DeltaSec), TotalFlows = sum(DeltaCount) by Src, Dst, Proto\n| where TotalFlows >= minFlows\n| extend BeaconPercent = round(MostFrequentDeltaCount * 100.0 / TotalFlows, 1)\n| where BeaconPercent >= beaconPercentThreshold\n| order by BeaconPercent desc\n", + "query": "Tailscale_Network_CL\n| where TimeGenerated > ago(7d)\n| where HasVirtualTraffic or HasSubnetTraffic or HasExitTraffic\n| extend HourUtc = hourofday(TimeGenerated), Dow = dayofweek(TimeGenerated)\n| where HourUtc < 7 or HourUtc >= 19 or Dow in (0d, 6d)\n| extend TaggedSource = isnotempty(SrcTags)\n| summarize\n Flows = count(),\n FirstFlow = min(TimeGenerated),\n LastFlow = max(TimeGenerated),\n DistinctHours = dcount(bin(TimeGenerated, 1h))\n by SrcNodeName, SrcUser, SrcTags=tostring(SrcTags), DstNodeName, DstTags=tostring(DstTags), TaggedSource\n| where Flows >= 5\n| project SrcNodeName, SrcUser, SrcTags, TaggedSource, DstNodeName, DstTags, Flows, DistinctHours, FirstFlow, LastFlow\n| order by Flows desc\n", "version": 2, "tags": [ { "name": "description", - "value": "Identifies flows that recur at a highly regular interval, which is the signature of C2 beaconing or scheduled exfiltration jobs. Looks for src->dst pairs where 80%+ of inter-flow gaps cluster around the same delta. Requires Tailscale Premium or Enterprise." + "value": "Identifies network flows occurring outside 07:00-19:00 UTC on weekdays, plus all of weekend, over the past 7 days. Filters to virtual/subnet/exit traffic (drops DERP-only keepalive noise). Useful for spotting unattended automation gone wrong, scheduled exfiltration, or unsanctioned after-hours access by humans. The TaggedSource column makes it easy to separate cron-like service traffic (tag:backup, tag:cron) from human user activity. Requires Tailscale Premium or Enterprise." }, { "name": "tactics", - "value": "CommandAndControl,Exfiltration" + "value": "Exfiltration,CommandAndControl" }, { "name": "techniques", - "value": "T1071,T1095,T1029" + "value": "T1029,T1071" } ] } @@ -8319,13 +9219,13 @@ { "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", "apiVersion": "2022-01-01-preview", - "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject16')._huntingQuerycontentId16),'/'))))]", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject21')._huntingQuerycontentId21),'/'))))]", "properties": { - "description": "Tailscale (CCF) Hunting Query 16", - "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject16')._huntingQuerycontentId16)]", - "contentId": "[variables('huntingQueryObject16')._huntingQuerycontentId16]", + "description": "Tailscale (CCF) Hunting Query 21", + "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject21')._huntingQuerycontentId21)]", + "contentId": "[variables('huntingQueryObject21')._huntingQuerycontentId21]", "kind": "HuntingQuery", - "version": "[variables('huntingQueryObject16').huntingQueryVersion16]", + "version": "[variables('huntingQueryObject21').huntingQueryVersion21]", "source": { "kind": "Solution", "name": "Tailscale (CCF)", @@ -8336,7 +9236,7 @@ "email": "[variables('_email')]" }, "support": { - "name": "Community", + "name": "Tailscale (CCF)", "tier": "Community", "email": "ccfconnectors.county118@passmail.com", "link": "https://github.com/Azure/Azure-Sentinel/issues" @@ -8350,53 +9250,53 @@ "packageName": "[variables('_solutionName')]", "packageId": "[variables('_solutionId')]", "contentSchemaVersion": "3.0.0", - "contentId": "[variables('huntingQueryObject16')._huntingQuerycontentId16]", + "contentId": "[variables('huntingQueryObject21')._huntingQuerycontentId21]", "contentKind": "HuntingQuery", - "displayName": "Tailscale Premium: Beaconing candidates (regular periodic flows)", - "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject16')._huntingQuerycontentId16,'-', '1.0.0')))]", - "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject16')._huntingQuerycontentId16,'-', '1.0.0')))]", + "displayName": "Tailscale Premium: Network flows outside business hours", + "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject21')._huntingQuerycontentId21,'-', '1.0.0')))]", + "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject21')._huntingQuerycontentId21,'-', '1.0.0')))]", "version": "1.0.0" } }, { "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", "apiVersion": "2023-04-01-preview", - "name": "[variables('huntingQueryObject17').huntingQueryTemplateSpecName17]", + "name": "[variables('huntingQueryObject22').huntingQueryTemplateSpecName22]", "location": "[parameters('workspace-location')]", "dependsOn": [ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscalePremiumPostureInventory_HuntingQueries Hunting Query with template version 3.0.5", + "description": "TailscalePremiumUserMultiDevice_HuntingQueries Hunting Query with template version 3.0.1", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "[variables('huntingQueryObject17').huntingQueryVersion17]", + "contentVersion": "[variables('huntingQueryObject22').huntingQueryVersion22]", "parameters": {}, "variables": {}, "resources": [ { "type": "Microsoft.OperationalInsights/savedSearches", "apiVersion": "2025-07-01", - "name": "Tailscale_(CCF)_Hunting_Query_17", + "name": "Tailscale_(CCF)_Hunting_Query_22", "location": "[parameters('workspace-location')]", "properties": { "eTag": "*", - "displayName": "Tailscale Premium: Current posture integration inventory", + "displayName": "Tailscale Premium: Users generating traffic from multiple devices", "category": "Hunting Queries", - "query": "Tailscale_PostureIntegrations_CL\n| summarize arg_max(TimeGenerated, *) by IntegrationId\n| project IntegrationId, Provider, ClientId, TenantId_Provider, Status, ConfigOverwrites\n| order by Provider asc\n", + "query": "let recent = Tailscale_Network_CL\n | where TimeGenerated > ago(24h)\n | where isnotempty(SrcUser)\n | summarize\n Devices = make_set(SrcNodeName, 20),\n DeviceCount = dcount(SrcNodeName),\n OsTypes = make_set(SrcOs, 10),\n FirstFlow = min(TimeGenerated),\n LastFlow = max(TimeGenerated),\n Flows = count()\n by SrcUser\n | where DeviceCount >= 2;\nlet devicesCreatedToday = Tailscale_Devices_CL\n | where TimeGenerated > ago(24h)\n | summarize arg_max(TimeGenerated, *) by DeviceId\n | where Created > ago(24h)\n | distinct DeviceName;\nrecent\n| extend NewDevicesToday = set_intersect(Devices, toscalar(devicesCreatedToday | summarize make_set(DeviceName)))\n| extend HasNewDevice = array_length(NewDevicesToday) > 0\n| project SrcUser, DeviceCount, Devices, OsTypes, HasNewDevice, NewDevicesToday, Flows, FirstFlow, LastFlow\n| order by HasNewDevice desc, DeviceCount desc\n", "version": 2, "tags": [ { "name": "description", - "value": "Lists the current set of device-posture integrations configured on the tailnet (latest snapshot per integration). Useful for compliance attestation - confirms what MDM/EDR systems are connected and what their status is. Compare against the expected baseline to detect drift. Requires Tailscale Premium or Enterprise." + "value": "Identifies users (SrcUser) generating tailnet flows from more than one distinct device in the past 24 hours. Normal for a user with phone + laptop. Useful for spotting account compromise (sudden new device for a user), unauthorised device enrollment, or device sharing across users. Cross-reference NewDevicesToday against Tailscale_Devices_CL.Created to confirm whether each device is genuinely new vs long-known. Requires Tailscale Premium or Enterprise." }, { "name": "tactics", - "value": "DefenseEvasion" + "value": "InitialAccess,Persistence" }, { "name": "techniques", - "value": "T1562" + "value": "T1078" } ] } @@ -8404,13 +9304,13 @@ { "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", "apiVersion": "2022-01-01-preview", - "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject17')._huntingQuerycontentId17),'/'))))]", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject22')._huntingQuerycontentId22),'/'))))]", "properties": { - "description": "Tailscale (CCF) Hunting Query 17", - "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject17')._huntingQuerycontentId17)]", - "contentId": "[variables('huntingQueryObject17')._huntingQuerycontentId17]", + "description": "Tailscale (CCF) Hunting Query 22", + "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject22')._huntingQuerycontentId22)]", + "contentId": "[variables('huntingQueryObject22')._huntingQuerycontentId22]", "kind": "HuntingQuery", - "version": "[variables('huntingQueryObject17').huntingQueryVersion17]", + "version": "[variables('huntingQueryObject22').huntingQueryVersion22]", "source": { "kind": "Solution", "name": "Tailscale (CCF)", @@ -8421,7 +9321,7 @@ "email": "[variables('_email')]" }, "support": { - "name": "Community", + "name": "Tailscale (CCF)", "tier": "Community", "email": "ccfconnectors.county118@passmail.com", "link": "https://github.com/Azure/Azure-Sentinel/issues" @@ -8435,11 +9335,11 @@ "packageName": "[variables('_solutionName')]", "packageId": "[variables('_solutionId')]", "contentSchemaVersion": "3.0.0", - "contentId": "[variables('huntingQueryObject17')._huntingQuerycontentId17]", + "contentId": "[variables('huntingQueryObject22')._huntingQuerycontentId22]", "contentKind": "HuntingQuery", - "displayName": "Tailscale Premium: Current posture integration inventory", - "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject17')._huntingQuerycontentId17,'-', '1.0.0')))]", - "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject17')._huntingQuerycontentId17,'-', '1.0.0')))]", + "displayName": "Tailscale Premium: Users generating traffic from multiple devices", + "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject22')._huntingQuerycontentId22,'-', '1.0.0')))]", + "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject22')._huntingQuerycontentId22,'-', '1.0.0')))]", "version": "1.0.0" } }, @@ -8452,7 +9352,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscaleStandardOperations Workbook with template version 3.0.5", + "description": "TailscaleStandardOperations Workbook with template version 3.0.1", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('workbookVersion1')]", @@ -8470,7 +9370,7 @@ }, "properties": { "displayName": "[parameters('workbook1-name')]", - "serializedData": "{\"version\":\"Notebook/1.0\",\"items\":[{\"type\":9,\"content\":{\"version\":\"KqlParameterItem/1.0\",\"parameters\":[{\"id\":\"7b05a598-5120-43f4-bf5d-576c2a7ff28d\",\"version\":\"KqlParameterItem/1.0\",\"name\":\"TimeRange\",\"type\":4,\"isRequired\":true,\"value\":{\"durationMs\":86400000},\"typeSettings\":{\"selectableValues\":[{\"durationMs\":3600000},{\"durationMs\":14400000},{\"durationMs\":43200000},{\"durationMs\":86400000},{\"durationMs\":172800000},{\"durationMs\":604800000},{\"durationMs\":2592000000}]}}],\"style\":\"pills\",\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\"},\"name\":\"parameters\"},{\"type\":1,\"content\":{\"json\":\"
Tailscale Operations (Standard)
Single-pane visibility into your Tailscale tailnet on Personal (Free), Starter and Standard tiers: who, what, when, where, and what changed. Scope every panel with the time range below; the Investigate tab adds Actor and Device pickers for drilldown. Premium-tier panels (network flow logs, posture integrations) live in the separate Tailscale Operations (Premium) workbook.
\"},\"name\":\"header\"},{\"type\":1,\"content\":{\"json\":\"
Tailnet at a glance
\"},\"name\":\"div-tailnet-at-a-glance-04e62b\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"let DEV = Tailscale_Devices_CL | summarize arg_max(TimeGenerated, *) by DeviceId;\\nlet USR = Tailscale_Users_CL | summarize arg_max(TimeGenerated, *) by UserId;\\nlet KEY = Tailscale_Keys_CL | summarize arg_max(TimeGenerated, *) by KeyId;\\nunion\\n (DEV | summarize V=toreal(count()) | extend Metric=\\\"Devices\\\", Order=1),\\n (DEV | where Authorized == true | summarize V=toreal(count()) | extend Metric=\\\"Authorized\\\", Order=2),\\n (DEV | where UpdateAvailable == true | summarize V=toreal(count()) | extend Metric=\\\"Updates Available\\\", Order=3),\\n (DEV | where SshEnabled == true | summarize V=toreal(count()) | extend Metric=\\\"SSH-Enabled\\\", Order=4),\\n (USR | summarize V=toreal(count()) | extend Metric=\\\"Users\\\", Order=5),\\n (USR | where Role =~ \\\"admin\\\" or Role =~ \\\"owner\\\" or Role =~ \\\"network-admin\\\" | summarize V=toreal(count()) | extend Metric=\\\"Admins\\\", Order=6),\\n (KEY | where isnull(Revoked) and (isnull(Expires) or Expires > now()) | summarize V=toreal(count()) | extend Metric=\\\"Active Keys\\\", Order=7),\\n (Tailscale_Audit_CL | where TimeGenerated {TimeRange} | summarize V=toreal(count()) | extend Metric=\\\"Audit Events ({TimeRange:label})\\\", Order=8)\\n| order by Order asc | project Metric, Value=V\",\"size\":3,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"tiles\",\"tileSettings\":{\"titleContent\":{\"columnMatch\":\"Metric\",\"formatter\":1},\"leftContent\":{\"columnMatch\":\"Value\",\"formatter\":12,\"formatOptions\":{\"palette\":\"auto\"}},\"showBorder\":false}},\"name\":\"q-4e9d7cff\"},{\"type\":11,\"content\":{\"version\":\"LinkItem/1.0\",\"style\":\"tabs\",\"links\":[{\"id\":\"9794e9fd-916b-494d-8e72-af63d2f4c6c7\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Overview\",\"subTarget\":\"overview\",\"style\":\"link\"},{\"id\":\"71cf49db-33c5-4d4b-920a-2ec0c6a258dc\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Investigate\",\"subTarget\":\"investigate\",\"style\":\"link\"},{\"id\":\"5c56bb37-2053-47a0-b6fd-9539768c144d\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Hunts\",\"subTarget\":\"hunts\",\"style\":\"link\"},{\"id\":\"d2004ded-07f8-446a-a720-f0a63d1d9dda\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Identity\",\"subTarget\":\"identity\",\"style\":\"link\"},{\"id\":\"f23b3e14-1511-4a29-bf5e-bd65e55dbb40\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Devices\",\"subTarget\":\"devices\",\"style\":\"link\"},{\"id\":\"724f8352-e21b-45a6-9029-39dc92693c05\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Credentials\",\"subTarget\":\"credentials\",\"style\":\"link\"},{\"id\":\"8400ec64-e118-44e0-ae29-84afe94b8e0e\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Admin Audit\",\"subTarget\":\"audit\",\"style\":\"link\"},{\"id\":\"b7e04861-edfa-4426-b6be-f1481ca569b6\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Network & DNS\",\"subTarget\":\"network\",\"style\":\"link\"},{\"id\":\"cfbc96e4-8585-4d89-8f65-8344c8cc6eb2\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Pipeline Health\",\"subTarget\":\"pipeline\",\"style\":\"link\"}]},\"name\":\"tabs\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":1,\"content\":{\"json\":\"
Audit activity over time
\"},\"name\":\"div-audit-activity-over--546688\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| summarize EventCount = count() by bin(TimeGenerated, 1h), Action\\n| order by TimeGenerated asc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"timechart\",\"title\":\"Audit events by action\",\"noDataMessage\":\"No audit events in the selected window. Widen the time range; remember the Tailscale audit poll runs every ~30 min.\",\"noDataMessageStyle\":5},\"name\":\"q-effcd498\"},{\"type\":1,\"content\":{\"json\":\"
Who's doing what
\"},\"name\":\"div-who's-doing-what-52144e\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| extend Actor=tostring(coalesce(Actor.loginName, Actor.displayName, Actor.type))\\n| where isnotempty(Actor)\\n| summarize Events=count(), DistinctActions=dcount(Action), LastSeen=max(TimeGenerated) by Actor\\n| order by Events desc | take 15\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Top actors (by event count)\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"Events\",\"formatter\":8,\"formatOptions\":{\"palette\":\"blue\"}},{\"columnMatch\":\"LastSeen\",\"formatter\":6}]}},\"name\":\"q-34c77fa9\",\"customWidth\":\"50\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| extend TargetType=tostring(Target.type)\\n| where isnotempty(TargetType)\\n| summarize Events=count() by TargetType\\n| order by Events desc | take 15\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"piechart\",\"title\":\"Activity by target type\"},\"name\":\"q-4b3b709d\",\"customWidth\":\"50\"},{\"type\":1,\"content\":{\"json\":\"
Recent admin events
\"},\"name\":\"div-recent-admin-events-87c9e0\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| extend Actor=tostring(coalesce(Actor.loginName, Actor.displayName, Actor.type))\\n| extend TargetType=tostring(Target.type), TargetName=tostring(coalesce(Target.name, Target.id))\\n| project TimeGenerated, Action, Actor, TargetType, TargetName, Origin\\n| order by TimeGenerated desc | take 30\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Most recent 30 audit events\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"TimeGenerated\",\"formatter\":6}]}},\"name\":\"q-5e7d6306\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"overview\"},\"name\":\"group-overview\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":9,\"content\":{\"version\":\"KqlParameterItem/1.0\",\"parameters\":[{\"id\":\"b83a25c1-da18-49a2-a444-6517f13d891c\",\"version\":\"KqlParameterItem/1.0\",\"name\":\"SelectedActor\",\"label\":\"Actor\",\"type\":2,\"isRequired\":false,\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\\n| where isnotempty(ActorLogin)\\n| summarize Events=count() by ActorLogin\\n| order by Events desc | take 50\\n| project value=ActorLogin, label=strcat(ActorLogin, \\\" (\\\", Events, \\\")\\\")\",\"typeSettings\":{\"additionalResourceOptions\":[\"value::all\"],\"showDefault\":false},\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"value\":\"value::all\"},{\"id\":\"a9cf7907-f201-4725-a072-a8bd34bef74e\",\"version\":\"KqlParameterItem/1.0\",\"name\":\"SelectedDevice\",\"label\":\"Device\",\"type\":2,\"isRequired\":false,\"query\":\"Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| order by LastSeen desc | take 100\\n| project value=DeviceName, label=strcat(coalesce(DeviceName, Hostname), \\\" (\\\", User, \\\")\\\")\",\"typeSettings\":{\"additionalResourceOptions\":[\"value::all\"],\"showDefault\":false},\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"value\":\"value::all\"}],\"style\":\"pills\",\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\"},\"name\":\"investigate-pickers\"},{\"type\":1,\"content\":{\"json\":\"
Actor activity timeline
\"},\"name\":\"div-actor-activity-timel-5ec305\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\\n| where \\\"{SelectedActor}\\\" == \\\"value::all\\\" or ActorLogin == \\\"{SelectedActor}\\\"\\n| summarize Events=count() by bin(TimeGenerated, 1h), Action\\n| order by TimeGenerated asc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"timechart\",\"title\":\"Actions over time for selected actor (or all actors)\",\"noDataMessage\":\"Select an actor from the Actor dropdown above, or leave on 'All' to see total activity.\",\"noDataMessageStyle\":5},\"name\":\"q-32d40848\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\\n| where \\\"{SelectedActor}\\\" == \\\"value::all\\\" or ActorLogin == \\\"{SelectedActor}\\\"\\n| extend TargetType=tostring(Target.type), TargetName=tostring(coalesce(Target.name, Target.id))\\n| project TimeGenerated, ActorLogin, Action, TargetType, TargetName, Origin\\n| order by TimeGenerated desc | take 100\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Events from selected actor\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"TimeGenerated\",\"formatter\":6}]},\"noDataMessage\":\"No events for this actor in the selected window.\",\"noDataMessageStyle\":5},\"name\":\"q-a742f6fd\"},{\"type\":1,\"content\":{\"json\":\"
Selected device timeline
\"},\"name\":\"div-selected-device-time-00bead\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| where \\\"{SelectedDevice}\\\" == \\\"value::all\\\" or DeviceName == \\\"{SelectedDevice}\\\"\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| extend OnlineNow = ClientConnectivity.endpoints != \\\"\\\" or ConnectedToControl == true\\n| project DeviceName, Hostname, User, Os, ClientVersion, UpdateAvailable, Authorized, IsExternal, SshEnabled, LastSeen, Expires, KeyExpiryDisabled, OnlineNow, Addresses, Tags, AdvertisedRoutes\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Device summary\",\"noDataMessage\":\"Select a device from the Device dropdown above. Defaults to 'All'.\",\"noDataMessageStyle\":5},\"name\":\"q-11094dc8\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| extend TargetType=tostring(Target.type), TargetName=tostring(Target.name), TargetId=tostring(Target.id)\\n| where (\\\"{SelectedDevice}\\\" == \\\"value::all\\\" and TargetType == \\\"NODE\\\") or TargetName == \\\"{SelectedDevice}\\\"\\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\\n| project TimeGenerated, Action, ActorLogin, TargetName, TargetId, Origin\\n| order by TimeGenerated desc | take 100\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Audit events touching this device\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"TimeGenerated\",\"formatter\":6}]},\"noDataMessage\":\"No audit events recorded against the selected device in this window. Tailscale tags device events with Target.type=NODE; the audit feed only emits NODE events on create/update/delete, so quiet devices stay quiet here.\",\"noDataMessageStyle\":5},\"name\":\"q-ead106ce\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"investigate\"},\"name\":\"group-investigate\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":1,\"content\":{\"json\":\"
First-seen actors in the last 24h
\"},\"name\":\"div-first-seen-actors-in-ce38d9\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"let recent = Tailscale_Audit_CL | where TimeGenerated > ago(24h) | extend A=tostring(coalesce(Actor.loginName, Actor.displayName)) | summarize FirstSeen24h=min(TimeGenerated), Events=count() by A;\\nlet historical = Tailscale_Audit_CL | where TimeGenerated between(ago(30d) .. ago(24h)) | extend A=tostring(coalesce(Actor.loginName, Actor.displayName)) | distinct A;\\nrecent | join kind=leftanti historical on A | where isnotempty(A) | project FirstSeen24h, Actor=A, Events | order by Events desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Actors who have NEVER appeared before (30d baseline)\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"FirstSeen24h\",\"formatter\":6},{\"columnMatch\":\"Events\",\"formatter\":8,\"formatOptions\":{\"palette\":\"orange\"}}]},\"noDataMessage\":\"Every actor seen in the last 24h has appeared at least once in the prior 30d. Healthy state.\",\"noDataMessageStyle\":5},\"name\":\"q-9ba7b85e\"},{\"type\":1,\"content\":{\"json\":\"
Off-hours configuration changes
\"},\"name\":\"div-off-hours-configurat-3c3787\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| extend Hour=hourofday(TimeGenerated), DayOfWeek=dayofweek(TimeGenerated)/1d\\n| where Hour < 7 or Hour > 19 or DayOfWeek in (0, 6)\\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\\n| extend TargetType=tostring(Target.type)\\n| where Action !in (\\\"LOGIN\\\", \\\"LOGOUT\\\")\\n| project TimeGenerated, ActorLogin, Action, TargetType, Origin\\n| order by TimeGenerated desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Admin actions outside 07:00-19:00 weekdays\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"TimeGenerated\",\"formatter\":6}]},\"noDataMessage\":\"No off-hours admin changes recorded - healthy state for an organisation working business hours.\",\"noDataMessageStyle\":5},\"name\":\"q-721ee490\"},{\"type\":1,\"content\":{\"json\":\"
Devices with key expiry disabled
\"},\"name\":\"div-devices-with-key-exp-dfe9c1\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| where KeyExpiryDisabled == true\\n| project DeviceName, Hostname, User, Os, ClientVersion, LastSeen, Authorized, Tags\\n| order by LastSeen desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Devices that will never re-authenticate (high-risk drift)\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"LastSeen\",\"formatter\":6}]},\"noDataMessage\":\"No devices have key expiry disabled - good. Disabling key expiry creates devices that never re-auth, drifting from policy.\",\"noDataMessageStyle\":5},\"name\":\"q-8a8e2fb5\"},{\"type\":1,\"content\":{\"json\":\"
Auth keys with no expiry
\"},\"name\":\"div-auth-keys-with-no-ex-b11207\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Keys_CL\\n| summarize arg_max(TimeGenerated, *) by KeyId\\n| where isnull(Revoked) and (isnull(Expires) or ExpirySeconds == 0)\\n| project KeyId, Description, UserId, KeyType, Created, Capabilities\\n| order by Created desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Active keys that never expire\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"Created\",\"formatter\":6}]},\"noDataMessage\":\"No never-expiring auth keys - rotation hygiene is good.\",\"noDataMessageStyle\":5},\"name\":\"q-1372740c\"},{\"type\":1,\"content\":{\"json\":\"
Devices running outdated clients
\"},\"name\":\"div-devices-running-outd-bcd077\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| where UpdateAvailable == true\\n| project DeviceName, Hostname, User, Os, ClientVersion, LastSeen, Tags\\n| order by LastSeen desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Devices flagged update-available by Tailscale\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"LastSeen\",\"formatter\":6}]},\"noDataMessage\":\"All devices on current client - nothing to patch.\",\"noDataMessageStyle\":5},\"name\":\"q-ecebf1f7\"},{\"type\":1,\"content\":{\"json\":\"
Dormant devices (LastSeen > 30 days)
\"},\"name\":\"div-dormant-devices-(las-761156\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| where LastSeen < ago(30d)\\n| extend DaysIdle = toint((now() - LastSeen) / 1d)\\n| project DeviceName, Hostname, User, Os, ClientVersion, LastSeen, DaysIdle, Authorized, Tags\\n| order by DaysIdle desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Devices idle 30+ days - candidates for retirement\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"LastSeen\",\"formatter\":6},{\"columnMatch\":\"DaysIdle\",\"formatter\":8,\"formatOptions\":{\"palette\":\"redBright\"}}]},\"noDataMessage\":\"No devices idle 30+ days - inventory is fresh.\",\"noDataMessageStyle\":5},\"name\":\"q-fb6c1fcc\"},{\"type\":1,\"content\":{\"json\":\"
Subnet route exposure
\"},\"name\":\"div-subnet-route-exposur-289c42\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| where array_length(AdvertisedRoutes) > 0 or array_length(EnabledRoutes) > 0\\n| extend Routes = tostring(EnabledRoutes), Advertised = tostring(AdvertisedRoutes)\\n| project DeviceName, Hostname, User, Os, Advertised, Routes, LastSeen, SshEnabled, Authorized\\n| order by LastSeen desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Devices advertising or running subnet routes / exit-node duty\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"LastSeen\",\"formatter\":6}]},\"noDataMessage\":\"No devices advertising subnet routes. Pure mesh topology.\",\"noDataMessageStyle\":5},\"name\":\"q-9533b081\"},{\"type\":1,\"content\":{\"json\":\"
Devices with SSH enabled
\"},\"name\":\"div-devices-with-ssh-ena-285238\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| where SshEnabled == true\\n| project DeviceName, Hostname, User, Os, ClientVersion, LastSeen, Authorized, Tags\\n| order by LastSeen desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Devices with Tailscale SSH enabled (Tailscale-managed remote-shell access)\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"LastSeen\",\"formatter\":6}]},\"noDataMessage\":\"No devices have Tailscale SSH enabled - no SSH-via-Tailscale risk surface.\",\"noDataMessageStyle\":5},\"name\":\"q-735368fb\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"hunts\"},\"name\":\"group-hunts\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":1,\"content\":{\"json\":\"
User inventory snapshot
\"},\"name\":\"div-user-inventory-snaps-9dc96c\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"let U = Tailscale_Users_CL | summarize arg_max(TimeGenerated, *) by UserId;\\nunion\\n (U | summarize V=toreal(count()) | extend Metric=\\\"Total users\\\", Order=1),\\n (U | where Role in~ (\\\"admin\\\",\\\"owner\\\",\\\"network-admin\\\",\\\"it-admin\\\",\\\"billing-admin\\\") | summarize V=toreal(count()) | extend Metric=\\\"Admin-tier users\\\", Order=2),\\n (U | where Status =~ \\\"active\\\" | summarize V=toreal(count()) | extend Metric=\\\"Active\\\", Order=3),\\n (U | where CurrentlyConnected == true | summarize V=toreal(count()) | extend Metric=\\\"Connected now\\\", Order=4),\\n (U | where Status =~ \\\"idle\\\" or LastSeen < ago(30d) | summarize V=toreal(count()) | extend Metric=\\\"Idle / dormant\\\", Order=5),\\n (U | where UserType =~ \\\"shared\\\" | summarize V=toreal(count()) | extend Metric=\\\"Shared (external)\\\", Order=6)\\n| order by Order asc | project Metric, Value=V\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"tiles\",\"tileSettings\":{\"titleContent\":{\"columnMatch\":\"Metric\",\"formatter\":1},\"leftContent\":{\"columnMatch\":\"Value\",\"formatter\":12,\"formatOptions\":{\"palette\":\"auto\"}},\"showBorder\":false}},\"name\":\"q-5689d9e8\"},{\"type\":1,\"content\":{\"json\":\"
Distribution
\"},\"name\":\"div-distribution-de67ec\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Users_CL\\n| summarize arg_max(TimeGenerated, *) by UserId\\n| summarize Count=count() by Role\\n| order by Count desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"piechart\",\"title\":\"Users by role\"},\"name\":\"q-0c47912a\",\"customWidth\":\"33\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Users_CL\\n| summarize arg_max(TimeGenerated, *) by UserId\\n| summarize Count=count() by Status\\n| order by Count desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"piechart\",\"title\":\"Users by status\"},\"name\":\"q-e8c20a69\",\"customWidth\":\"33\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Users_CL\\n| summarize arg_max(TimeGenerated, *) by UserId\\n| summarize Count=count() by UserType\\n| order by Count desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"piechart\",\"title\":\"Users by type (member / shared)\"},\"name\":\"q-2c51776d\",\"customWidth\":\"33\"},{\"type\":1,\"content\":{\"json\":\"
Activity heatmap
\"},\"name\":\"div-activity-heatmap-ca6a21\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Users_CL\\n| summarize arg_max(TimeGenerated, *) by UserId\\n| extend DaysSinceLogin = toint((now() - LastSeen) / 1d)\\n| extend Bucket = case(\\n DaysSinceLogin < 1, \\\"Today\\\",\\n DaysSinceLogin < 7, \\\"This week\\\",\\n DaysSinceLogin < 30, \\\"This month\\\",\\n DaysSinceLogin < 90, \\\"Past quarter\\\",\\n \\\"90+ days\\\")\\n| summarize Users=count() by Bucket\\n| order by case(Bucket==\\\"Today\\\",1, Bucket==\\\"This week\\\",2, Bucket==\\\"This month\\\",3, Bucket==\\\"Past quarter\\\",4, 5) asc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"barchart\",\"title\":\"Users by recency of last login\"},\"name\":\"q-350e118d\"},{\"type\":1,\"content\":{\"json\":\"
Full user list
\"},\"name\":\"div-full-user-list-136aac\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Users_CL\\n| summarize arg_max(TimeGenerated, *) by UserId\\n| project DisplayName, LoginName, Role, Status, UserType, DeviceCount, CurrentlyConnected, Created, LastSeen\\n| order by LastSeen desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"All users (latest snapshot per user ID)\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"Created\",\"formatter\":6},{\"columnMatch\":\"LastSeen\",\"formatter\":6},{\"columnMatch\":\"Role\",\"formatter\":11},{\"columnMatch\":\"Status\",\"formatter\":11},{\"columnMatch\":\"DeviceCount\",\"formatter\":8,\"formatOptions\":{\"palette\":\"blue\"}}]}},\"name\":\"q-cee23d3c\"},{\"type\":1,\"content\":{\"json\":\"
Orphaned users (active but no devices)
\"},\"name\":\"div-orphaned-users-(acti-56d6f8\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Users_CL\\n| summarize arg_max(TimeGenerated, *) by UserId\\n| where Status =~ \\\"active\\\" and DeviceCount == 0\\n| project DisplayName, LoginName, Role, UserType, Created, LastSeen\\n| order by Created desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Active accounts with zero devices - candidates for offboarding review\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"Created\",\"formatter\":6},{\"columnMatch\":\"LastSeen\",\"formatter\":6}]},\"noDataMessage\":\"Every active account has at least one device - good hygiene.\",\"noDataMessageStyle\":5},\"name\":\"q-83fcd942\"},{\"type\":1,\"content\":{\"json\":\"
Role escalation history
\"},\"name\":\"div-role-escalation-hist-bd8df4\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| where Action == \\\"USER_ROLE_UPDATE\\\" or Action == \\\"USER_ROLES_ASSIGNED\\\" or Action contains \\\"ROLE\\\"\\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\\n| extend TargetName=tostring(coalesce(Target.name, Target.id))\\n| extend FromRole=tostring(Old.role), ToRole=tostring(New.role)\\n| project TimeGenerated, ActorLogin, Action, TargetName, FromRole, ToRole, Origin\\n| order by TimeGenerated desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Recent role changes\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"TimeGenerated\",\"formatter\":6},{\"columnMatch\":\"ToRole\",\"formatter\":11}]},\"noDataMessage\":\"No role changes in this window.\",\"noDataMessageStyle\":5},\"name\":\"q-f6c8358a\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"identity\"},\"name\":\"group-identity\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":1,\"content\":{\"json\":\"
Device fleet snapshot
\"},\"name\":\"div-device-fleet-snapsho-939675\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"let D = Tailscale_Devices_CL | summarize arg_max(TimeGenerated, *) by DeviceId;\\nunion\\n (D | summarize V=toreal(count()) | extend Metric=\\\"Total devices\\\", Order=1),\\n (D | where Authorized == true | summarize V=toreal(count()) | extend Metric=\\\"Authorized\\\", Order=2),\\n (D | where IsExternal == true | summarize V=toreal(count()) | extend Metric=\\\"External (shared)\\\", Order=3),\\n (D | where UpdateAvailable == true | summarize V=toreal(count()) | extend Metric=\\\"Updates available\\\", Order=4),\\n (D | where SshEnabled == true | summarize V=toreal(count()) | extend Metric=\\\"SSH-enabled\\\", Order=5),\\n (D | where KeyExpiryDisabled == true | summarize V=toreal(count()) | extend Metric=\\\"No key expiry\\\", Order=6),\\n (D | where array_length(AdvertisedRoutes) > 0 | summarize V=toreal(count()) | extend Metric=\\\"Subnet/exit-node\\\", Order=7),\\n (D | where LastSeen < ago(30d) | summarize V=toreal(count()) | extend Metric=\\\"Stale (30+ days)\\\", Order=8)\\n| order by Order asc | project Metric, Value=V\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"tiles\",\"tileSettings\":{\"titleContent\":{\"columnMatch\":\"Metric\",\"formatter\":1},\"leftContent\":{\"columnMatch\":\"Value\",\"formatter\":12,\"formatOptions\":{\"palette\":\"auto\"}},\"showBorder\":false}},\"name\":\"q-366964fc\"},{\"type\":1,\"content\":{\"json\":\"
Distribution
\"},\"name\":\"div-distribution-396d03\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| summarize Count=count() by Os\\n| order by Count desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"piechart\",\"title\":\"Devices by OS\"},\"name\":\"q-0c8f5988\",\"customWidth\":\"33\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| summarize Count=count() by ClientVersion\\n| order by Count desc | take 10\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"barchart\",\"title\":\"Top 10 client versions\"},\"name\":\"q-af1c45cd\",\"customWidth\":\"33\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| mv-expand Tag = Tags to typeof(string)\\n| summarize Devices=dcount(DeviceId) by Tag=iff(isempty(Tag), \\\"(untagged)\\\", Tag)\\n| order by Devices desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"piechart\",\"title\":\"Devices by tag\"},\"name\":\"q-c3a0ef5b\",\"customWidth\":\"33\"},{\"type\":1,\"content\":{\"json\":\"
Devices needing attention
\"},\"name\":\"div-devices-needing-atte-f04a47\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| where UpdateAvailable == true or KeyExpiryDisabled == true or LastSeen < ago(30d) or Authorized == false\\n| extend Issues = strcat_array(pack_array(\\n iff(UpdateAvailable == true, \\\"needs-update\\\", \\\"\\\"),\\n iff(KeyExpiryDisabled == true, \\\"key-never-expires\\\", \\\"\\\"),\\n iff(LastSeen < ago(30d), \\\"stale\\\", \\\"\\\"),\\n iff(Authorized == false, \\\"unauthorized\\\", \\\"\\\")), \\\",\\\")\\n| extend Issues = trim(\\\",\\\", trim_start(\\\",\\\", trim_end(\\\",\\\", replace_string(Issues, \\\",,\\\", \\\",\\\"))))\\n| project DeviceName, Hostname, User, Os, ClientVersion, LastSeen, Issues\\n| order by LastSeen desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Devices flagged with one or more issues\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"LastSeen\",\"formatter\":6},{\"columnMatch\":\"Issues\",\"formatter\":11}]},\"noDataMessage\":\"No devices need attention - all updated, fresh, authorized, and key-rotating.\",\"noDataMessageStyle\":5},\"name\":\"q-dc5db84c\"},{\"type\":1,\"content\":{\"json\":\"
Full device inventory
\"},\"name\":\"div-full-device-inventor-b70642\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| project DeviceName, Hostname, User, Os, ClientVersion, UpdateAvailable, Authorized, IsExternal, SshEnabled, LastSeen, KeyExpiryDisabled, Tags, AdvertisedRoutes\\n| order by LastSeen desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"All devices (latest snapshot per device ID)\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"LastSeen\",\"formatter\":6},{\"columnMatch\":\"Os\",\"formatter\":11},{\"columnMatch\":\"ClientVersion\",\"formatter\":11}]}},\"name\":\"q-7f7e7a9a\"},{\"type\":1,\"content\":{\"json\":\"
Subnet routers / exit nodes
\"},\"name\":\"div-subnet-routers-/-exi-b082d6\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| where array_length(AdvertisedRoutes) > 0\\n| extend AdvertisedSummary = tostring(AdvertisedRoutes), EnabledSummary = tostring(EnabledRoutes)\\n| project DeviceName, Hostname, User, Os, AdvertisedSummary, EnabledSummary, LastSeen, Authorized\\n| order by LastSeen desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Devices advertising subnet routes or exit-node capability\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"LastSeen\",\"formatter\":6}]},\"noDataMessage\":\"No subnet routers in this tailnet - pure mesh topology.\",\"noDataMessageStyle\":5},\"name\":\"q-5004df25\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"devices\"},\"name\":\"group-devices\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":1,\"content\":{\"json\":\"
Credentials snapshot
\"},\"name\":\"div-credentials-snapshot-fd464a\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"let K = Tailscale_Keys_CL | summarize arg_max(TimeGenerated, *) by KeyId;\\nunion\\n (K | summarize V=toreal(count()) | extend Metric=\\\"Total keys\\\", Order=1),\\n (K | where isnull(Revoked) and (isnull(Expires) or Expires > now()) | summarize V=toreal(count()) | extend Metric=\\\"Active\\\", Order=2),\\n (K | where isnotnull(Revoked) | summarize V=toreal(count()) | extend Metric=\\\"Revoked\\\", Order=3),\\n (K | where Expires < now() and isnull(Revoked) | summarize V=toreal(count()) | extend Metric=\\\"Expired\\\", Order=4),\\n (K | where isnull(Revoked) and Expires between(now() .. ago(-7d)) | summarize V=toreal(count()) | extend Metric=\\\"Expiring in 7d\\\", Order=5),\\n (K | where isnull(Revoked) and (isnull(Expires) or ExpirySeconds==0) | summarize V=toreal(count()) | extend Metric=\\\"Never expire\\\", Order=6),\\n (K | where KeyType =~ \\\"auth\\\" | summarize V=toreal(count()) | extend Metric=\\\"Auth keys\\\", Order=7),\\n (K | where KeyType =~ \\\"api\\\" or KeyType contains \\\"oauth\\\" | summarize V=toreal(count()) | extend Metric=\\\"API / OAuth\\\", Order=8)\\n| order by Order asc | project Metric, Value=V\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"tiles\",\"tileSettings\":{\"titleContent\":{\"columnMatch\":\"Metric\",\"formatter\":1},\"leftContent\":{\"columnMatch\":\"Value\",\"formatter\":12,\"formatOptions\":{\"palette\":\"auto\"}},\"showBorder\":false}},\"name\":\"q-79398bc0\"},{\"type\":1,\"content\":{\"json\":\"
Distribution
\"},\"name\":\"div-distribution-15f665\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Keys_CL\\n| summarize arg_max(TimeGenerated, *) by KeyId\\n| summarize Count=count() by KeyType\\n| order by Count desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"piechart\",\"title\":\"Keys by type\"},\"name\":\"q-23e6618b\",\"customWidth\":\"50\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Keys_CL\\n| summarize arg_max(TimeGenerated, *) by KeyId\\n| where isnull(Revoked)\\n| extend Bucket = case(\\n isnull(Expires) or ExpirySeconds == 0, \\\"Never\\\",\\n Expires < now(), \\\"Already expired\\\",\\n Expires < ago(-1d), \\\"<24h\\\",\\n Expires < ago(-7d), \\\"1-7d\\\",\\n Expires < ago(-30d), \\\"8-30d\\\",\\n Expires < ago(-90d), \\\"31-90d\\\",\\n \\\"90+d\\\")\\n| summarize Keys=count() by Bucket\\n| order by case(Bucket==\\\"Already expired\\\",1, Bucket==\\\"<24h\\\",2, Bucket==\\\"1-7d\\\",3, Bucket==\\\"8-30d\\\",4, Bucket==\\\"31-90d\\\",5, Bucket==\\\"90+d\\\",6, Bucket==\\\"Never\\\",7, 8) asc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"barchart\",\"title\":\"Active key expiry distribution\"},\"name\":\"q-7484d1a0\",\"customWidth\":\"50\"},{\"type\":1,\"content\":{\"json\":\"
Active credential register
\"},\"name\":\"div-active-credential-re-c27539\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Keys_CL\\n| summarize arg_max(TimeGenerated, *) by KeyId\\n| where isnull(Revoked)\\n| extend ExpiryStatus = case(\\n isnull(Expires) or ExpirySeconds == 0, \\\"Never expires\\\",\\n Expires < now(), \\\"Expired\\\",\\n Expires < ago(-7d), \\\"Expires in 7d\\\",\\n Expires < ago(-30d), \\\"Expires in 30d\\\",\\n \\\"OK\\\")\\n| project KeyId, KeyType, Description, UserId, Created, Expires, ExpiryStatus, Capabilities\\n| order by Created desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"All active credentials with computed expiry status\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"Created\",\"formatter\":6},{\"columnMatch\":\"Expires\",\"formatter\":6},{\"columnMatch\":\"ExpiryStatus\",\"formatter\":11},{\"columnMatch\":\"KeyType\",\"formatter\":11}]}},\"name\":\"q-4b27a750\"},{\"type\":1,\"content\":{\"json\":\"
Credential CRUD events
\"},\"name\":\"div-credential-crud-even-e455b0\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| where Action contains \\\"API_KEY\\\" or Action contains \\\"AUTH_KEY\\\" or Action contains \\\"OAUTH\\\" or Action contains \\\"KEY_CREATE\\\" or Action contains \\\"KEY_REVOKE\\\"\\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\\n| extend TargetId=tostring(Target.id), TargetType=tostring(Target.type)\\n| project TimeGenerated, Action, ActorLogin, TargetType, TargetId, Origin\\n| order by TimeGenerated desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Recent credential create / revoke / rotate events\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"TimeGenerated\",\"formatter\":6}]},\"noDataMessage\":\"No credential CRUD activity in this window.\",\"noDataMessageStyle\":5},\"name\":\"q-777693bd\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"credentials\"},\"name\":\"group-credentials\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":1,\"content\":{\"json\":\"
Audit volume
\"},\"name\":\"div-audit-volume-de29a2\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| summarize Events=count() by bin(TimeGenerated, 1h)\\n| order by TimeGenerated asc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"timechart\",\"title\":\"Audit events per hour\",\"noDataMessage\":\"No audit events in this window.\",\"noDataMessageStyle\":5},\"name\":\"q-f5eee265\"},{\"type\":1,\"content\":{\"json\":\"
Action heatmap by hour of day
\"},\"name\":\"div-action-heatmap-by-ho-c8bd5e\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| extend Hour=hourofday(TimeGenerated)\\n| summarize Events=count() by Hour, Action\\n| order by Hour asc, Events desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"categoricalbar\",\"title\":\"When are admin actions happening?\"},\"name\":\"q-c6f45d95\"},{\"type\":1,\"content\":{\"json\":\"
Actor / Action heatmap
\"},\"name\":\"div-actor-/-action-heatm-d820ba\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\\n| where isnotempty(ActorLogin)\\n| summarize Events=count() by ActorLogin, Action\\n| order by Events desc | take 100\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Who is firing which action\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"Events\",\"formatter\":4,\"formatOptions\":{\"palette\":\"blue\"}}]},\"noDataMessage\":\"No audit events in this window.\",\"noDataMessageStyle\":5},\"name\":\"q-06c13b3b\"},{\"type\":1,\"content\":{\"json\":\"
Recent activity
\"},\"name\":\"div-recent-activity-63b210\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\\n| extend TargetType=tostring(Target.type), TargetName=tostring(coalesce(Target.name, Target.id))\\n| project TimeGenerated, Action, ActorLogin, TargetType, TargetName, Origin, EventGroupID\\n| order by TimeGenerated desc | take 100\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Last 100 audit events\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"TimeGenerated\",\"formatter\":6},{\"columnMatch\":\"Action\",\"formatter\":11}]},\"noDataMessage\":\"No audit events in this window.\",\"noDataMessageStyle\":5},\"name\":\"q-07a25a40\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"audit\"},\"name\":\"group-audit\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":1,\"content\":{\"json\":\"
DNS configuration (current state)
\"},\"name\":\"div-dns-configuration-(c-5f2e1e\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Dns_CL\\n| summarize arg_max(TimeGenerated, *) by ConfigType\\n| project ConfigType, Nameservers, MagicDNS, SearchPaths, LastSnapshot=TimeGenerated\\n| order by ConfigType asc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"MagicDNS, nameservers, search paths\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"LastSnapshot\",\"formatter\":6},{\"columnMatch\":\"ConfigType\",\"formatter\":11}]},\"noDataMessage\":\"No DNS snapshots in the workspace yet. DNS polls runs at ~30 min cadence.\",\"noDataMessageStyle\":5},\"name\":\"q-d6a6a358\"},{\"type\":1,\"content\":{\"json\":\"
Tailnet settings (current)
\"},\"name\":\"div-tailnet-settings-(cu-e03b16\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Settings_CL\\n| summarize arg_max(TimeGenerated, *) by TenantId\\n| project DevicesApprovalOn, DevicesAutoUpdatesOn, DevicesKeyDurationDays, UsersApprovalOn, NetworkFlowLoggingOn, RegionalRoutingOn, PostureIdentityCollectionOn, UsersRoleAllowedToJoinExternalTailnets, LastSnapshot=TimeGenerated\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Tailnet policy gates\",\"noDataMessage\":\"No settings snapshot yet.\",\"noDataMessageStyle\":5},\"name\":\"q-0622cfc3\"},{\"type\":1,\"content\":{\"json\":\"
DNS change history
\"},\"name\":\"div-dns-change-history-9dd376\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| extend TargetProperty=tostring(Target.property)\\n| where Action contains \\\"DNS\\\" or TargetProperty has_any (\\\"DNS_NAMESERVERS\\\", \\\"DNS_SPLIT_DNS\\\", \\\"MAGICDNS\\\", \\\"DNS_SEARCH_PATHS\\\")\\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\\n| project TimeGenerated, ActorLogin, Action, TargetProperty, Origin\\n| order by TimeGenerated desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Recent DNS-related admin changes\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"TimeGenerated\",\"formatter\":6}]},\"noDataMessage\":\"No DNS changes in this window.\",\"noDataMessageStyle\":5},\"name\":\"q-a132ff6a\"},{\"type\":1,\"content\":{\"json\":\"
ACL policy changes
\"},\"name\":\"div-acl-policy-changes-ff0e68\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| where Action == \\\"ACL_UPDATE\\\" or Action contains \\\"ACL\\\"\\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\\n| project TimeGenerated, ActorLogin, Action, Origin, EventGroupID\\n| order by TimeGenerated desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Recent ACL / policy file modifications\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"TimeGenerated\",\"formatter\":6}]},\"noDataMessage\":\"No ACL changes in this window.\",\"noDataMessageStyle\":5},\"name\":\"q-94818c53\"},{\"type\":1,\"content\":{\"json\":\"
Subnet routes & exit nodes
\"},\"name\":\"div-subnet-routes-and-ex-eef47f\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| where array_length(EnabledRoutes) > 0\\n| project DeviceName, User, Os, EnabledRoutes=tostring(EnabledRoutes), AdvertisedRoutes=tostring(AdvertisedRoutes), LastSeen\\n| order by LastSeen desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Routes currently being served from devices\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"LastSeen\",\"formatter\":6}]},\"noDataMessage\":\"No subnet routers active in this tailnet.\",\"noDataMessageStyle\":5},\"name\":\"q-61a792cd\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"network\"},\"name\":\"group-network\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":1,\"content\":{\"json\":\"
Ingest rate per table
\"},\"name\":\"div-ingest-rate-per-tabl-24e5f6\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"union withsource=Table Tailscale_Audit_CL, Tailscale_Devices_CL, Tailscale_Users_CL, Tailscale_Keys_CL, Tailscale_Dns_CL, Tailscale_Settings_CL\\n| where TimeGenerated > ago(24h)\\n| summarize Rows=count() by Table, bin(TimeGenerated, 1h)\\n| order by TimeGenerated asc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"timechart\",\"title\":\"Rows ingested per Tailscale table per hour (last 24h)\",\"noDataMessage\":\"No Tailscale data ingested in the last 24h - check the connector card under Sentinel Data Connectors.\",\"noDataMessageStyle\":5},\"name\":\"q-c6fd0143\"},{\"type\":1,\"content\":{\"json\":\"
Last poll time per table
\"},\"name\":\"div-last-poll-time-per-t-49b051\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"union withsource=Table Tailscale_Audit_CL, Tailscale_Devices_CL, Tailscale_Users_CL, Tailscale_Keys_CL, Tailscale_Dns_CL, Tailscale_Settings_CL\\n| summarize LastRow=max(TimeGenerated), TotalRows=count() by Table\\n| extend MinutesAgo=toint((now() - LastRow) / 1m)\\n| extend Status=case(MinutesAgo < 60, \\\"Fresh\\\", MinutesAgo < 360, \\\"Recent\\\", MinutesAgo < 1440, \\\"Stale\\\", \\\"Very Stale\\\")\\n| project Table, LastRow, MinutesAgo, TotalRows, Status\\n| order by MinutesAgo asc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Per-table freshness\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"LastRow\",\"formatter\":6},{\"columnMatch\":\"MinutesAgo\",\"formatter\":8,\"formatOptions\":{\"palette\":\"redBright\"}},{\"columnMatch\":\"TotalRows\",\"formatter\":8,\"formatOptions\":{\"palette\":\"blue\"}},{\"columnMatch\":\"Status\",\"formatter\":11}]}},\"name\":\"q-a02e37a8\"},{\"type\":1,\"content\":{\"json\":\"
Log Analytics operational events
\"},\"name\":\"div-log-analytics-operat-630749\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"_LogOperation\\n| where TimeGenerated > ago(24h)\\n| where _ResourceId contains \\\"tailscale\\\" or Detail contains \\\"Tailscale_\\\"\\n| project TimeGenerated, Operation, Level, Detail\\n| order by TimeGenerated desc | take 100\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Log Analytics operational events touching Tailscale tables\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"TimeGenerated\",\"formatter\":6},{\"columnMatch\":\"Level\",\"formatter\":11}]},\"noDataMessage\":\"No operational issues recorded in the last 24h.\",\"noDataMessageStyle\":5},\"name\":\"q-b492f150\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"pipeline\"},\"name\":\"group-pipeline\"},{\"type\":1,\"content\":{\"json\":\"
Tailscale Operations (Standard) (CCF) - Microsoft Sentinel content from the Tailscale (CCF) solution, Standard-tier surface. Tables polled from the Tailscale REST API: audit, devices, users, keys, dns, settings. Filter every panel via the time range above; the Investigate tab adds Actor and Device pickers for drilldown. For network flow logs and posture integrations, install the companion Tailscale Operations (Premium) workbook on a Premium / Enterprise tailnet.
\"},\"name\":\"footer\"}],\"fallbackResourceIds\":[\"Azure Monitor\"],\"fromTemplateId\":\"sentinel-Tailscale-CCF\",\"$schema\":\"https://github.com/Microsoft/Application-Insights-Workbooks/blob/master/schema/workbook.json\"}\n", + "serializedData": "{\"version\":\"Notebook/1.0\",\"items\":[{\"type\":9,\"content\":{\"version\":\"KqlParameterItem/1.0\",\"parameters\":[{\"id\":\"7b05a598-5120-43f4-bf5d-576c2a7ff28d\",\"version\":\"KqlParameterItem/1.0\",\"name\":\"TimeRange\",\"type\":4,\"isRequired\":true,\"value\":{\"durationMs\":86400000},\"typeSettings\":{\"selectableValues\":[{\"durationMs\":3600000},{\"durationMs\":14400000},{\"durationMs\":43200000},{\"durationMs\":86400000},{\"durationMs\":172800000},{\"durationMs\":604800000},{\"durationMs\":2592000000}]}}],\"style\":\"pills\",\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\"},\"name\":\"parameters\"},{\"type\":1,\"content\":{\"json\":\"
Tailscale Operations (Standard)
Single-pane visibility into your Tailscale tailnet on Personal (Free), Starter and Standard tiers: who, what, when, where, and what changed. Scope every panel with the time range below; the Investigate tab adds Actor and Device pickers for drilldown. Premium-tier panels (network flow logs, posture integrations) live in the separate Tailscale Operations (Premium) workbook.
\"},\"name\":\"header\"},{\"type\":11,\"content\":{\"version\":\"LinkItem/1.0\",\"style\":\"tabs\",\"links\":[{\"id\":\"9794e9fd-916b-494d-8e72-af63d2f4c6c7\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Overview\",\"subTarget\":\"overview\",\"style\":\"link\"},{\"id\":\"71cf49db-33c5-4d4b-920a-2ec0c6a258dc\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Investigate\",\"subTarget\":\"investigate\",\"style\":\"link\"},{\"id\":\"5c56bb37-2053-47a0-b6fd-9539768c144d\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Hunts\",\"subTarget\":\"hunts\",\"style\":\"link\"},{\"id\":\"d2004ded-07f8-446a-a720-f0a63d1d9dda\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Identity\",\"subTarget\":\"identity\",\"style\":\"link\"},{\"id\":\"f23b3e14-1511-4a29-bf5e-bd65e55dbb40\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Devices\",\"subTarget\":\"devices\",\"style\":\"link\"},{\"id\":\"724f8352-e21b-45a6-9029-39dc92693c05\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Credentials\",\"subTarget\":\"credentials\",\"style\":\"link\"},{\"id\":\"8400ec64-e118-44e0-ae29-84afe94b8e0e\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Admin Audit\",\"subTarget\":\"audit\",\"style\":\"link\"},{\"id\":\"b7e04861-edfa-4426-b6be-f1481ca569b6\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Network & DNS\",\"subTarget\":\"network\",\"style\":\"link\"},{\"id\":\"cfbc96e4-8585-4d89-8f65-8344c8cc6eb2\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Pipeline Health\",\"subTarget\":\"pipeline\",\"style\":\"link\"}]},\"name\":\"tabs\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":1,\"content\":{\"json\":\"
Tailnet at a glance
\"},\"name\":\"div-tailnet-at-a-glance-04e62b\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"let DEV = Tailscale_Devices_CL | summarize arg_max(TimeGenerated, *) by DeviceId;\\nlet USR = Tailscale_Users_CL | summarize arg_max(TimeGenerated, *) by UserId;\\nlet KEY = Tailscale_Keys_CL | summarize arg_max(TimeGenerated, *) by KeyId;\\nunion\\n (DEV | summarize V=toreal(count()) | extend Metric=\\\"Devices\\\", Order=1),\\n (DEV | where Authorized == true | summarize V=toreal(count()) | extend Metric=\\\"Authorized\\\", Order=2),\\n (DEV | where UpdateAvailable == true | summarize V=toreal(count()) | extend Metric=\\\"Updates Available\\\", Order=3),\\n (DEV | where SshEnabled == true | summarize V=toreal(count()) | extend Metric=\\\"SSH-Enabled\\\", Order=4),\\n (USR | summarize V=toreal(count()) | extend Metric=\\\"Users\\\", Order=5),\\n (USR | where Role =~ \\\"admin\\\" or Role =~ \\\"owner\\\" or Role =~ \\\"network-admin\\\" | summarize V=toreal(count()) | extend Metric=\\\"Admins\\\", Order=6),\\n (KEY | where isnull(Revoked) and (isnull(Expires) or Expires > now()) | summarize V=toreal(count()) | extend Metric=\\\"Active Keys\\\", Order=7),\\n (Tailscale_Audit_CL | where TimeGenerated {TimeRange} | summarize V=toreal(count()) | extend Metric=\\\"Audit Events ({TimeRange:label})\\\", Order=8)\\n| order by Order asc | project Metric, Value=V\",\"size\":3,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"tiles\",\"tileSettings\":{\"titleContent\":{\"columnMatch\":\"Metric\",\"formatter\":1},\"leftContent\":{\"columnMatch\":\"Value\",\"formatter\":12,\"formatOptions\":{\"palette\":\"auto\"}},\"showBorder\":false}},\"name\":\"q-4e9d7cff\"},{\"type\":1,\"content\":{\"json\":\"
Audit activity over time
\"},\"name\":\"div-audit-activity-over--546688\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| summarize EventCount = count() by bin(TimeGenerated, 1h), Action\\n| order by TimeGenerated asc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"timechart\",\"title\":\"Audit events by action\",\"noDataMessage\":\"No audit events in the selected window. Widen the time range; remember the Tailscale audit poll runs every ~30 min.\",\"noDataMessageStyle\":5},\"name\":\"q-effcd498\"},{\"type\":1,\"content\":{\"json\":\"
Who's doing what
\"},\"name\":\"div-who's-doing-what-52144e\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| extend Actor=tostring(coalesce(Actor.loginName, Actor.displayName, Actor.type))\\n| where isnotempty(Actor)\\n| summarize Events=count(), DistinctActions=dcount(Action), LastSeen=max(TimeGenerated) by Actor\\n| order by Events desc | take 15\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Top actors (by event count)\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"Events\",\"formatter\":8,\"formatOptions\":{\"palette\":\"blue\"}},{\"columnMatch\":\"LastSeen\",\"formatter\":6}]}},\"name\":\"q-34c77fa9\",\"customWidth\":\"50\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| extend TargetType=tostring(Target.type)\\n| where isnotempty(TargetType)\\n| summarize Events=count() by TargetType\\n| order by Events desc | take 15\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"piechart\",\"title\":\"Activity by target type\"},\"name\":\"q-4b3b709d\",\"customWidth\":\"50\"},{\"type\":1,\"content\":{\"json\":\"
Recent admin events
\"},\"name\":\"div-recent-admin-events-87c9e0\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| extend Actor=tostring(coalesce(Actor.loginName, Actor.displayName, Actor.type))\\n| extend TargetType=tostring(Target.type), TargetName=tostring(coalesce(Target.name, Target.id))\\n| project TimeGenerated, Action, Actor, TargetType, TargetName, Origin\\n| order by TimeGenerated desc | take 30\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Most recent 30 audit events\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"TimeGenerated\",\"formatter\":6}]}},\"name\":\"q-5e7d6306\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"overview\"},\"name\":\"group-overview\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":9,\"content\":{\"version\":\"KqlParameterItem/1.0\",\"parameters\":[{\"id\":\"b83a25c1-da18-49a2-a444-6517f13d891c\",\"version\":\"KqlParameterItem/1.0\",\"name\":\"SelectedActor\",\"label\":\"Actor\",\"type\":2,\"isRequired\":false,\"query\":\"let opts = Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\\n| where isnotempty(ActorLogin)\\n| summarize Events=count() by ActorLogin\\n| project value=ActorLogin, label=strcat(ActorLogin, \\\" (\\\", tostring(Events), \\\" events)\\\");\\n(print value=\\\"__ALL__\\\", label=\\\"(All actors)\\\")\\n| union opts\",\"typeSettings\":{\"showDefault\":false},\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"value\":\"__ALL__\"}],\"style\":\"pills\",\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\"},\"name\":\"investigate-picker-actor\"},{\"type\":1,\"content\":{\"json\":\"
Actor activity timeline
\"},\"name\":\"div-actor-activity-timel-5ec305\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\\n| where \\\"{SelectedActor}\\\" == \\\"__ALL__\\\" or ActorLogin == \\\"{SelectedActor}\\\"\\n| summarize Events=count() by bin(TimeGenerated, 1h), Action\\n| order by TimeGenerated asc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"timechart\",\"title\":\"Actions over time -- actor: {SelectedActor:label}\",\"noDataMessage\":\"Select an actor from the Actor dropdown above, or leave on 'All' to see total activity.\",\"noDataMessageStyle\":5},\"name\":\"q-32d40848\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\\n| where \\\"{SelectedActor}\\\" == \\\"__ALL__\\\" or ActorLogin == \\\"{SelectedActor}\\\"\\n| extend TargetType=tostring(Target.type), TargetName=tostring(coalesce(Target.name, Target.id))\\n| project TimeGenerated, ActorLogin, Action, TargetType, TargetName, Origin\\n| order by TimeGenerated desc | take 100\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Recent events for actor: {SelectedActor:label}\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"TimeGenerated\",\"formatter\":6}]},\"noDataMessage\":\"No events for this actor in the selected window.\",\"noDataMessageStyle\":5},\"name\":\"q-a742f6fd\"},{\"type\":9,\"content\":{\"version\":\"KqlParameterItem/1.0\",\"parameters\":[{\"id\":\"a9cf7907-f201-4725-a072-a8bd34bef74e\",\"version\":\"KqlParameterItem/1.0\",\"name\":\"SelectedDevice\",\"label\":\"Device\",\"type\":2,\"isRequired\":false,\"query\":\"let opts = Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| order by LastSeen desc | take 100\\n| project value=DeviceName, label=strcat(coalesce(DeviceName, Hostname), \\\" (\\\", User, \\\")\\\");\\n(print value=\\\"__ALL__\\\", label=\\\"(All devices)\\\")\\n| union opts\",\"typeSettings\":{\"showDefault\":false},\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"value\":\"__ALL__\"}],\"style\":\"pills\",\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\"},\"name\":\"investigate-picker-device\"},{\"type\":1,\"content\":{\"json\":\"
Selected device timeline
\"},\"name\":\"div-selected-device-time-00bead\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| where \\\"{SelectedDevice}\\\" == \\\"__ALL__\\\" or DeviceName == \\\"{SelectedDevice}\\\"\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| extend OnlineNow = ClientConnectivity.endpoints != \\\"\\\" or ConnectedToControl == true\\n| project DeviceName, Hostname, User, Os, ClientVersion, UpdateAvailable, Authorized, IsExternal, SshEnabled, LastSeen, Expires, KeyExpiryDisabled, OnlineNow, Addresses, Tags, AdvertisedRoutes\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Summary for device: {SelectedDevice:label}\",\"noDataMessage\":\"Select a device from the Device dropdown above. Defaults to 'All'.\",\"noDataMessageStyle\":5},\"name\":\"q-11094dc8\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| extend TargetType=tostring(Target.type), TargetName=tostring(Target.name), TargetId=tostring(Target.id)\\n| where (\\\"{SelectedDevice}\\\" == \\\"__ALL__\\\" and TargetType == \\\"NODE\\\") or TargetName == \\\"{SelectedDevice}\\\"\\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\\n| project TimeGenerated, Action, ActorLogin, TargetName, TargetId, Origin\\n| order by TimeGenerated desc | take 100\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Audit events touching device: {SelectedDevice:label}\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"TimeGenerated\",\"formatter\":6}]},\"noDataMessage\":\"No audit events recorded against the selected device in this window. Tailscale tags device events with Target.type=NODE; the audit feed only emits NODE events on create/update/delete, so quiet devices stay quiet here.\",\"noDataMessageStyle\":5},\"name\":\"q-ead106ce\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"investigate\"},\"name\":\"group-investigate\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":1,\"content\":{\"json\":\"
First-seen actors in the last 24h
\"},\"name\":\"div-first-seen-actors-in-ce38d9\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"let recent = Tailscale_Audit_CL | where TimeGenerated > ago(24h) | extend A=tostring(coalesce(Actor.loginName, Actor.displayName)) | summarize FirstSeen24h=min(TimeGenerated), Events=count() by A;\\nlet historical = Tailscale_Audit_CL | where TimeGenerated between(ago(30d) .. ago(24h)) | extend A=tostring(coalesce(Actor.loginName, Actor.displayName)) | distinct A;\\nrecent | join kind=leftanti historical on A | where isnotempty(A) | project FirstSeen24h, Actor=A, Events | order by Events desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Actors who have NEVER appeared before (30d baseline)\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"FirstSeen24h\",\"formatter\":6},{\"columnMatch\":\"Events\",\"formatter\":8,\"formatOptions\":{\"palette\":\"orange\"}}]},\"noDataMessage\":\"Every actor seen in the last 24h has appeared at least once in the prior 30d. Healthy state.\",\"noDataMessageStyle\":1},\"name\":\"q-9ba7b85e\"},{\"type\":1,\"content\":{\"json\":\"
Off-hours configuration changes
\"},\"name\":\"div-off-hours-configurat-3c3787\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| extend Hour=hourofday(TimeGenerated), DayOfWeek=dayofweek(TimeGenerated)/1d\\n| where Hour < 7 or Hour > 19 or DayOfWeek in (0, 6)\\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\\n| extend TargetType=tostring(Target.type)\\n| where Action !in (\\\"LOGIN\\\", \\\"LOGOUT\\\")\\n| project TimeGenerated, ActorLogin, Action, TargetType, Origin\\n| order by TimeGenerated desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Admin actions outside 07:00-19:00 weekdays\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"TimeGenerated\",\"formatter\":6}]},\"noDataMessage\":\"No off-hours admin changes recorded - healthy state for an organisation working business hours.\",\"noDataMessageStyle\":1},\"name\":\"q-721ee490\"},{\"type\":1,\"content\":{\"json\":\"
Devices with key expiry disabled
\"},\"name\":\"div-devices-with-key-exp-dfe9c1\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| where KeyExpiryDisabled == true\\n| project DeviceName, Hostname, User, Os, ClientVersion, LastSeen, Authorized, Tags\\n| order by LastSeen desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Devices that will never re-authenticate (high-risk drift)\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"LastSeen\",\"formatter\":6}]},\"noDataMessage\":\"No devices have key expiry disabled - good. Disabling key expiry creates devices that never re-auth, drifting from policy.\",\"noDataMessageStyle\":1},\"name\":\"q-8a8e2fb5\"},{\"type\":1,\"content\":{\"json\":\"
Auth keys with no expiry
\"},\"name\":\"div-auth-keys-with-no-ex-b11207\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Keys_CL\\n| summarize arg_max(TimeGenerated, *) by KeyId\\n| where isnull(Revoked) and (isnull(Expires) or ExpirySeconds == 0)\\n| project KeyId, Description, UserId, KeyType, Created, Capabilities\\n| order by Created desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Active keys that never expire\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"Created\",\"formatter\":6}]},\"noDataMessage\":\"No never-expiring auth keys - rotation hygiene is good.\",\"noDataMessageStyle\":1},\"name\":\"q-1372740c\"},{\"type\":1,\"content\":{\"json\":\"
Devices running outdated clients
\"},\"name\":\"div-devices-running-outd-bcd077\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| where UpdateAvailable == true\\n| project DeviceName, Hostname, User, Os, ClientVersion, LastSeen, Tags\\n| order by LastSeen desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Devices flagged update-available by Tailscale\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"LastSeen\",\"formatter\":6}]},\"noDataMessage\":\"All devices on current client - nothing to patch.\",\"noDataMessageStyle\":1},\"name\":\"q-ecebf1f7\"},{\"type\":1,\"content\":{\"json\":\"
Dormant devices (LastSeen > 30 days)
\"},\"name\":\"div-dormant-devices-(las-761156\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| where LastSeen < ago(30d)\\n| extend DaysIdle = toint((now() - LastSeen) / 1d)\\n| project DeviceName, Hostname, User, Os, ClientVersion, LastSeen, DaysIdle, Authorized, Tags\\n| order by DaysIdle desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Devices idle 30+ days - candidates for retirement\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"LastSeen\",\"formatter\":6},{\"columnMatch\":\"DaysIdle\",\"formatter\":8,\"formatOptions\":{\"palette\":\"redBright\"}}]},\"noDataMessage\":\"No devices idle 30+ days - inventory is fresh.\",\"noDataMessageStyle\":1},\"name\":\"q-fb6c1fcc\"},{\"type\":1,\"content\":{\"json\":\"
Subnet route exposure
\"},\"name\":\"div-subnet-route-exposur-289c42\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| where array_length(AdvertisedRoutes) > 0 or array_length(EnabledRoutes) > 0\\n| extend Routes = tostring(EnabledRoutes), Advertised = tostring(AdvertisedRoutes)\\n| project DeviceName, Hostname, User, Os, Advertised, Routes, LastSeen, SshEnabled, Authorized\\n| order by LastSeen desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Devices advertising or running subnet routes / exit-node duty\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"LastSeen\",\"formatter\":6}]},\"noDataMessage\":\"No devices advertising subnet routes. Pure mesh topology.\",\"noDataMessageStyle\":1},\"name\":\"q-9533b081\"},{\"type\":1,\"content\":{\"json\":\"
Devices with SSH enabled
\"},\"name\":\"div-devices-with-ssh-ena-285238\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| where SshEnabled == true\\n| project DeviceName, Hostname, User, Os, ClientVersion, LastSeen, Authorized, Tags\\n| order by LastSeen desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Devices with Tailscale SSH enabled (Tailscale-managed remote-shell access)\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"LastSeen\",\"formatter\":6}]},\"noDataMessage\":\"No devices have Tailscale SSH enabled - no SSH-via-Tailscale risk surface.\",\"noDataMessageStyle\":1},\"name\":\"q-735368fb\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"hunts\"},\"name\":\"group-hunts\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":1,\"content\":{\"json\":\"
User inventory snapshot
\"},\"name\":\"div-user-inventory-snaps-9dc96c\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"let U = Tailscale_Users_CL | summarize arg_max(TimeGenerated, *) by UserId;\\nunion\\n (U | summarize V=toreal(count()) | extend Metric=\\\"Total users\\\", Order=1),\\n (U | where Role in~ (\\\"admin\\\",\\\"owner\\\",\\\"network-admin\\\",\\\"it-admin\\\",\\\"billing-admin\\\") | summarize V=toreal(count()) | extend Metric=\\\"Admin-tier users\\\", Order=2),\\n (U | where Status =~ \\\"active\\\" | summarize V=toreal(count()) | extend Metric=\\\"Active\\\", Order=3),\\n (U | where CurrentlyConnected == true | summarize V=toreal(count()) | extend Metric=\\\"Connected now\\\", Order=4),\\n (U | where Status =~ \\\"idle\\\" or LastSeen < ago(30d) | summarize V=toreal(count()) | extend Metric=\\\"Idle / dormant\\\", Order=5),\\n (U | where UserType =~ \\\"shared\\\" | summarize V=toreal(count()) | extend Metric=\\\"Shared (external)\\\", Order=6)\\n| order by Order asc | project Metric, Value=V\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"tiles\",\"tileSettings\":{\"titleContent\":{\"columnMatch\":\"Metric\",\"formatter\":1},\"leftContent\":{\"columnMatch\":\"Value\",\"formatter\":12,\"formatOptions\":{\"palette\":\"auto\"}},\"showBorder\":false}},\"name\":\"q-5689d9e8\"},{\"type\":1,\"content\":{\"json\":\"
Distribution
\"},\"name\":\"div-distribution-de67ec\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Users_CL\\n| summarize arg_max(TimeGenerated, *) by UserId\\n| summarize Count=count() by Role\\n| order by Count desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"piechart\",\"title\":\"Users by role\"},\"name\":\"q-0c47912a\",\"customWidth\":\"33\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Users_CL\\n| summarize arg_max(TimeGenerated, *) by UserId\\n| summarize Count=count() by Status\\n| order by Count desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"piechart\",\"title\":\"Users by status\"},\"name\":\"q-e8c20a69\",\"customWidth\":\"33\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Users_CL\\n| summarize arg_max(TimeGenerated, *) by UserId\\n| summarize Count=count() by UserType\\n| order by Count desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"piechart\",\"title\":\"Users by type (member / shared)\"},\"name\":\"q-2c51776d\",\"customWidth\":\"33\"},{\"type\":1,\"content\":{\"json\":\"
Activity heatmap
\"},\"name\":\"div-activity-heatmap-ca6a21\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Users_CL\\n| summarize arg_max(TimeGenerated, *) by UserId\\n| extend DaysSinceLogin = toint((now() - LastSeen) / 1d)\\n| extend Bucket = case(\\n DaysSinceLogin < 1, \\\"Today\\\",\\n DaysSinceLogin < 7, \\\"This week\\\",\\n DaysSinceLogin < 30, \\\"This month\\\",\\n DaysSinceLogin < 90, \\\"Past quarter\\\",\\n \\\"90+ days\\\")\\n| summarize Users=count() by Bucket\\n| order by case(Bucket==\\\"Today\\\",1, Bucket==\\\"This week\\\",2, Bucket==\\\"This month\\\",3, Bucket==\\\"Past quarter\\\",4, 5) asc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"barchart\",\"title\":\"Users by recency of last login\"},\"name\":\"q-350e118d\"},{\"type\":1,\"content\":{\"json\":\"
Full user list
\"},\"name\":\"div-full-user-list-136aac\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Users_CL\\n| summarize arg_max(TimeGenerated, *) by UserId\\n| project DisplayName, LoginName, Role, Status, UserType, DeviceCount, CurrentlyConnected, Created, LastSeen\\n| order by LastSeen desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"All users (latest snapshot per user ID)\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"Created\",\"formatter\":6},{\"columnMatch\":\"LastSeen\",\"formatter\":6},{\"columnMatch\":\"Role\",\"formatter\":1},{\"columnMatch\":\"Status\",\"formatter\":1},{\"columnMatch\":\"DeviceCount\",\"formatter\":8,\"formatOptions\":{\"palette\":\"blue\"}}]}},\"name\":\"q-cee23d3c\"},{\"type\":1,\"content\":{\"json\":\"
Orphaned users (active but no devices)
\"},\"name\":\"div-orphaned-users-(acti-56d6f8\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Users_CL\\n| summarize arg_max(TimeGenerated, *) by UserId\\n| where Status =~ \\\"active\\\" and DeviceCount == 0\\n| project DisplayName, LoginName, Role, UserType, Created, LastSeen\\n| order by Created desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Active accounts with zero devices - candidates for offboarding review\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"Created\",\"formatter\":6},{\"columnMatch\":\"LastSeen\",\"formatter\":6}]},\"noDataMessage\":\"Every active account has at least one device - good hygiene.\",\"noDataMessageStyle\":1},\"name\":\"q-83fcd942\"},{\"type\":1,\"content\":{\"json\":\"
Role escalation history
\"},\"name\":\"div-role-escalation-hist-bd8df4\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| where Action == \\\"USER_ROLE_UPDATE\\\" or Action == \\\"USER_ROLES_ASSIGNED\\\" or Action contains \\\"ROLE\\\"\\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\\n| extend TargetName=tostring(coalesce(Target.name, Target.id))\\n| extend FromRole=tostring(Old.role), ToRole=tostring(New.role)\\n| project TimeGenerated, ActorLogin, Action, TargetName, FromRole, ToRole, Origin\\n| order by TimeGenerated desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Recent role changes\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"TimeGenerated\",\"formatter\":6},{\"columnMatch\":\"ToRole\",\"formatter\":1}]},\"noDataMessage\":\"No role changes in this window.\",\"noDataMessageStyle\":1},\"name\":\"q-f6c8358a\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"identity\"},\"name\":\"group-identity\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":1,\"content\":{\"json\":\"
Device fleet snapshot
\"},\"name\":\"div-device-fleet-snapsho-939675\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"let D = Tailscale_Devices_CL | summarize arg_max(TimeGenerated, *) by DeviceId;\\nunion\\n (D | summarize V=toreal(count()) | extend Metric=\\\"Total devices\\\", Order=1),\\n (D | where Authorized == true | summarize V=toreal(count()) | extend Metric=\\\"Authorized\\\", Order=2),\\n (D | where IsExternal == true | summarize V=toreal(count()) | extend Metric=\\\"External (shared)\\\", Order=3),\\n (D | where UpdateAvailable == true | summarize V=toreal(count()) | extend Metric=\\\"Updates available\\\", Order=4),\\n (D | where SshEnabled == true | summarize V=toreal(count()) | extend Metric=\\\"SSH-enabled\\\", Order=5),\\n (D | where KeyExpiryDisabled == true | summarize V=toreal(count()) | extend Metric=\\\"No key expiry\\\", Order=6),\\n (D | where array_length(AdvertisedRoutes) > 0 | summarize V=toreal(count()) | extend Metric=\\\"Subnet/exit-node\\\", Order=7),\\n (D | where LastSeen < ago(30d) | summarize V=toreal(count()) | extend Metric=\\\"Stale (30+ days)\\\", Order=8)\\n| order by Order asc | project Metric, Value=V\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"tiles\",\"tileSettings\":{\"titleContent\":{\"columnMatch\":\"Metric\",\"formatter\":1},\"leftContent\":{\"columnMatch\":\"Value\",\"formatter\":12,\"formatOptions\":{\"palette\":\"auto\"}},\"showBorder\":false}},\"name\":\"q-366964fc\"},{\"type\":1,\"content\":{\"json\":\"
Distribution
\"},\"name\":\"div-distribution-396d03\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| summarize Count=count() by Os\\n| order by Count desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"piechart\",\"title\":\"Devices by OS\"},\"name\":\"q-0c8f5988\",\"customWidth\":\"33\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| summarize Count=count() by ClientVersion\\n| order by Count desc | take 10\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"barchart\",\"title\":\"Top 10 client versions\"},\"name\":\"q-af1c45cd\",\"customWidth\":\"33\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| mv-expand Tag = Tags to typeof(string)\\n| summarize Devices=dcount(DeviceId) by Tag=iff(isempty(Tag), \\\"(untagged)\\\", Tag)\\n| order by Devices desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"piechart\",\"title\":\"Devices by tag\"},\"name\":\"q-c3a0ef5b\",\"customWidth\":\"33\"},{\"type\":1,\"content\":{\"json\":\"
Devices needing attention
\"},\"name\":\"div-devices-needing-atte-f04a47\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| where UpdateAvailable == true or KeyExpiryDisabled == true or LastSeen < ago(30d) or Authorized == false\\n| extend Issues = strcat_array(pack_array(\\n iff(UpdateAvailable == true, \\\"needs-update\\\", \\\"\\\"),\\n iff(KeyExpiryDisabled == true, \\\"key-never-expires\\\", \\\"\\\"),\\n iff(LastSeen < ago(30d), \\\"stale\\\", \\\"\\\"),\\n iff(Authorized == false, \\\"unauthorized\\\", \\\"\\\")), \\\",\\\")\\n| extend Issues = trim(\\\",\\\", trim_start(\\\",\\\", trim_end(\\\",\\\", replace_string(Issues, \\\",,\\\", \\\",\\\"))))\\n| project DeviceName, Hostname, User, Os, ClientVersion, LastSeen, Issues\\n| order by LastSeen desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Devices flagged with one or more issues\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"LastSeen\",\"formatter\":6},{\"columnMatch\":\"Issues\",\"formatter\":1}]},\"noDataMessage\":\"No devices need attention - all updated, fresh, authorized, and key-rotating.\",\"noDataMessageStyle\":1},\"name\":\"q-dc5db84c\"},{\"type\":1,\"content\":{\"json\":\"
Full device inventory
\"},\"name\":\"div-full-device-inventor-b70642\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| project DeviceName, Hostname, User, Os, ClientVersion, UpdateAvailable, Authorized, IsExternal, SshEnabled, LastSeen, KeyExpiryDisabled, Tags, AdvertisedRoutes\\n| order by LastSeen desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"All devices (latest snapshot per device ID)\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"LastSeen\",\"formatter\":6},{\"columnMatch\":\"Os\",\"formatter\":1},{\"columnMatch\":\"ClientVersion\",\"formatter\":1}]}},\"name\":\"q-7f7e7a9a\"},{\"type\":1,\"content\":{\"json\":\"
Subnet routers / exit nodes
\"},\"name\":\"div-subnet-routers-/-exi-b082d6\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| where array_length(AdvertisedRoutes) > 0\\n| extend AdvertisedSummary = tostring(AdvertisedRoutes), EnabledSummary = tostring(EnabledRoutes)\\n| project DeviceName, Hostname, User, Os, AdvertisedSummary, EnabledSummary, LastSeen, Authorized\\n| order by LastSeen desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Devices advertising subnet routes or exit-node capability\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"LastSeen\",\"formatter\":6}]},\"noDataMessage\":\"No subnet routers in this tailnet - pure mesh topology.\",\"noDataMessageStyle\":1},\"name\":\"q-5004df25\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"devices\"},\"name\":\"group-devices\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":1,\"content\":{\"json\":\"
Credentials snapshot
\"},\"name\":\"div-credentials-snapshot-fd464a\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"let K = Tailscale_Keys_CL | summarize arg_max(TimeGenerated, *) by KeyId;\\nunion\\n (K | summarize V=toreal(count()) | extend Metric=\\\"Total keys\\\", Order=1),\\n (K | where isnull(Revoked) and (isnull(Expires) or Expires > now()) | summarize V=toreal(count()) | extend Metric=\\\"Active\\\", Order=2),\\n (K | where isnotnull(Revoked) | summarize V=toreal(count()) | extend Metric=\\\"Revoked\\\", Order=3),\\n (K | where Expires < now() and isnull(Revoked) | summarize V=toreal(count()) | extend Metric=\\\"Expired\\\", Order=4),\\n (K | where isnull(Revoked) and Expires between(now() .. ago(-7d)) | summarize V=toreal(count()) | extend Metric=\\\"Expiring in 7d\\\", Order=5),\\n (K | where isnull(Revoked) and (isnull(Expires) or ExpirySeconds==0) | summarize V=toreal(count()) | extend Metric=\\\"Never expire\\\", Order=6),\\n (K | where KeyType =~ \\\"auth\\\" | summarize V=toreal(count()) | extend Metric=\\\"Auth keys\\\", Order=7),\\n (K | where KeyType =~ \\\"api\\\" or KeyType contains \\\"oauth\\\" | summarize V=toreal(count()) | extend Metric=\\\"API / OAuth\\\", Order=8)\\n| order by Order asc | project Metric, Value=V\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"tiles\",\"tileSettings\":{\"titleContent\":{\"columnMatch\":\"Metric\",\"formatter\":1},\"leftContent\":{\"columnMatch\":\"Value\",\"formatter\":12,\"formatOptions\":{\"palette\":\"auto\"}},\"showBorder\":false}},\"name\":\"q-79398bc0\"},{\"type\":1,\"content\":{\"json\":\"
Distribution
\"},\"name\":\"div-distribution-15f665\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Keys_CL\\n| summarize arg_max(TimeGenerated, *) by KeyId\\n| summarize Count=count() by KeyType\\n| order by Count desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"piechart\",\"title\":\"Keys by type\"},\"name\":\"q-23e6618b\",\"customWidth\":\"50\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Keys_CL\\n| summarize arg_max(TimeGenerated, *) by KeyId\\n| where isnull(Revoked)\\n| extend Bucket = case(\\n isnull(Expires) or ExpirySeconds == 0, \\\"Never\\\",\\n Expires < now(), \\\"Already expired\\\",\\n Expires < ago(-1d), \\\"<24h\\\",\\n Expires < ago(-7d), \\\"1-7d\\\",\\n Expires < ago(-30d), \\\"8-30d\\\",\\n Expires < ago(-90d), \\\"31-90d\\\",\\n \\\"90+d\\\")\\n| summarize Keys=count() by Bucket\\n| order by case(Bucket==\\\"Already expired\\\",1, Bucket==\\\"<24h\\\",2, Bucket==\\\"1-7d\\\",3, Bucket==\\\"8-30d\\\",4, Bucket==\\\"31-90d\\\",5, Bucket==\\\"90+d\\\",6, Bucket==\\\"Never\\\",7, 8) asc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"barchart\",\"title\":\"Active key expiry distribution\"},\"name\":\"q-7484d1a0\",\"customWidth\":\"50\"},{\"type\":1,\"content\":{\"json\":\"
Active credential register
\"},\"name\":\"div-active-credential-re-c27539\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Keys_CL\\n| summarize arg_max(TimeGenerated, *) by KeyId\\n| where isnull(Revoked)\\n| extend ExpiryStatus = case(\\n isnull(Expires) or ExpirySeconds == 0, \\\"Never expires\\\",\\n Expires < now(), \\\"Expired\\\",\\n Expires < ago(-7d), \\\"Expires in 7d\\\",\\n Expires < ago(-30d), \\\"Expires in 30d\\\",\\n \\\"OK\\\")\\n| project KeyId, KeyType, Description, UserId, Created, Expires, ExpiryStatus, Capabilities\\n| order by Created desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"All active credentials with computed expiry status\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"Created\",\"formatter\":6},{\"columnMatch\":\"Expires\",\"formatter\":6},{\"columnMatch\":\"ExpiryStatus\",\"formatter\":1},{\"columnMatch\":\"KeyType\",\"formatter\":1}]}},\"name\":\"q-4b27a750\"},{\"type\":1,\"content\":{\"json\":\"
Credential CRUD events
\"},\"name\":\"div-credential-crud-even-e455b0\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| where Action contains \\\"API_KEY\\\" or Action contains \\\"AUTH_KEY\\\" or Action contains \\\"OAUTH\\\" or Action contains \\\"KEY_CREATE\\\" or Action contains \\\"KEY_REVOKE\\\"\\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\\n| extend TargetId=tostring(Target.id), TargetType=tostring(Target.type)\\n| project TimeGenerated, Action, ActorLogin, TargetType, TargetId, Origin\\n| order by TimeGenerated desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Recent credential create / revoke / rotate events\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"TimeGenerated\",\"formatter\":6}]},\"noDataMessage\":\"No credential CRUD activity in this window.\",\"noDataMessageStyle\":1},\"name\":\"q-777693bd\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"credentials\"},\"name\":\"group-credentials\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":1,\"content\":{\"json\":\"
Audit volume
\"},\"name\":\"div-audit-volume-de29a2\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| summarize Events=count() by bin(TimeGenerated, 1h)\\n| order by TimeGenerated asc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"timechart\",\"title\":\"Audit events per hour\",\"noDataMessage\":\"No audit events in this window.\",\"noDataMessageStyle\":5},\"name\":\"q-f5eee265\"},{\"type\":1,\"content\":{\"json\":\"
Action heatmap by hour of day
\"},\"name\":\"div-action-heatmap-by-ho-c8bd5e\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| extend Hour=hourofday(TimeGenerated)\\n| summarize Events=count() by Hour, Action\\n| order by Hour asc, Events desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"categoricalbar\",\"title\":\"When are admin actions happening?\"},\"name\":\"q-c6f45d95\"},{\"type\":1,\"content\":{\"json\":\"
Actor / Action heatmap
\"},\"name\":\"div-actor-/-action-heatm-d820ba\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\\n| where isnotempty(ActorLogin)\\n| summarize Events=count() by ActorLogin, Action\\n| order by Events desc | take 100\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Who is firing which action\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"Events\",\"formatter\":4,\"formatOptions\":{\"palette\":\"blue\"}}]},\"noDataMessage\":\"No audit events in this window.\",\"noDataMessageStyle\":5},\"name\":\"q-06c13b3b\"},{\"type\":1,\"content\":{\"json\":\"
Recent activity
\"},\"name\":\"div-recent-activity-63b210\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\\n| extend TargetType=tostring(Target.type), TargetName=tostring(coalesce(Target.name, Target.id))\\n| project TimeGenerated, Action, ActorLogin, TargetType, TargetName, Origin, EventGroupID\\n| order by TimeGenerated desc | take 100\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Last 100 audit events\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"TimeGenerated\",\"formatter\":6},{\"columnMatch\":\"Action\",\"formatter\":1}]},\"noDataMessage\":\"No audit events in this window.\",\"noDataMessageStyle\":5},\"name\":\"q-07a25a40\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"audit\"},\"name\":\"group-audit\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":1,\"content\":{\"json\":\"
DNS configuration (current state)
\"},\"name\":\"div-dns-configuration-(c-5f2e1e\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Dns_CL\\n| summarize arg_max(TimeGenerated, *) by ConfigType\\n| project ConfigType, Nameservers, MagicDNS, SearchPaths, LastSnapshot=TimeGenerated\\n| order by ConfigType asc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"MagicDNS, nameservers, search paths\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"LastSnapshot\",\"formatter\":6},{\"columnMatch\":\"ConfigType\",\"formatter\":1}]},\"noDataMessage\":\"No DNS snapshots in the workspace yet. DNS polls runs at ~30 min cadence.\",\"noDataMessageStyle\":5},\"name\":\"q-d6a6a358\"},{\"type\":1,\"content\":{\"json\":\"
Tailnet settings (current)
\"},\"name\":\"div-tailnet-settings-(cu-e03b16\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Settings_CL\\n| summarize arg_max(TimeGenerated, *) by TenantId\\n| project DevicesApprovalOn, DevicesAutoUpdatesOn, DevicesKeyDurationDays, UsersApprovalOn, NetworkFlowLoggingOn, RegionalRoutingOn, PostureIdentityCollectionOn, UsersRoleAllowedToJoinExternalTailnets, LastSnapshot=TimeGenerated\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Tailnet policy gates\",\"noDataMessage\":\"No settings snapshot yet.\",\"noDataMessageStyle\":5},\"name\":\"q-0622cfc3\"},{\"type\":1,\"content\":{\"json\":\"
DNS change history
\"},\"name\":\"div-dns-change-history-9dd376\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| extend TargetProperty=tostring(Target.property)\\n| where Action contains \\\"DNS\\\" or TargetProperty has_any (\\\"DNS_NAMESERVERS\\\", \\\"DNS_SPLIT_DNS\\\", \\\"MAGICDNS\\\", \\\"DNS_SEARCH_PATHS\\\")\\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\\n| project TimeGenerated, ActorLogin, Action, TargetProperty, Origin\\n| order by TimeGenerated desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Recent DNS-related admin changes\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"TimeGenerated\",\"formatter\":6}]},\"noDataMessage\":\"No DNS changes in this window.\",\"noDataMessageStyle\":1},\"name\":\"q-a132ff6a\"},{\"type\":1,\"content\":{\"json\":\"
ACL policy changes
\"},\"name\":\"div-acl-policy-changes-ff0e68\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| where Action == \\\"ACL_UPDATE\\\" or Action contains \\\"ACL\\\"\\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\\n| project TimeGenerated, ActorLogin, Action, Origin, EventGroupID\\n| order by TimeGenerated desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Recent ACL / policy file modifications\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"TimeGenerated\",\"formatter\":6}]},\"noDataMessage\":\"No ACL changes in this window.\",\"noDataMessageStyle\":1},\"name\":\"q-94818c53\"},{\"type\":1,\"content\":{\"json\":\"
Subnet routes & exit nodes
\"},\"name\":\"div-subnet-routes-and-ex-eef47f\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| where array_length(EnabledRoutes) > 0\\n| project DeviceName, User, Os, EnabledRoutes=tostring(EnabledRoutes), AdvertisedRoutes=tostring(AdvertisedRoutes), LastSeen\\n| order by LastSeen desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Routes currently being served from devices\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"LastSeen\",\"formatter\":6}]},\"noDataMessage\":\"No subnet routers active in this tailnet.\",\"noDataMessageStyle\":1},\"name\":\"q-61a792cd\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"network\"},\"name\":\"group-network\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":1,\"content\":{\"json\":\"
Ingest rate per table
\"},\"name\":\"div-ingest-rate-per-tabl-24e5f6\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"union withsource=Table Tailscale_Audit_CL, Tailscale_Devices_CL, Tailscale_Users_CL, Tailscale_Keys_CL, Tailscale_Dns_CL, Tailscale_Settings_CL\\n| where TimeGenerated > ago(24h)\\n| summarize Rows=count() by Table, bin(TimeGenerated, 1h)\\n| order by TimeGenerated asc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"timechart\",\"title\":\"Rows ingested per Tailscale table per hour (last 24h)\",\"noDataMessage\":\"No Tailscale data ingested in the last 24h - check the connector card under Sentinel Data Connectors.\",\"noDataMessageStyle\":5},\"name\":\"q-c6fd0143\"},{\"type\":1,\"content\":{\"json\":\"
Last poll time per table
\"},\"name\":\"div-last-poll-time-per-t-49b051\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"union withsource=Table Tailscale_Audit_CL, Tailscale_Devices_CL, Tailscale_Users_CL, Tailscale_Keys_CL, Tailscale_Dns_CL, Tailscale_Settings_CL\\n| summarize LastRow=max(TimeGenerated), TotalRows=count() by Table\\n| extend MinutesAgo=toint((now() - LastRow) / 1m)\\n| extend Status=case(MinutesAgo < 60, \\\"Fresh\\\", MinutesAgo < 360, \\\"Recent\\\", MinutesAgo < 1440, \\\"Stale\\\", \\\"Very Stale\\\")\\n| project Table, LastRow, MinutesAgo, TotalRows, Status\\n| order by MinutesAgo asc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Per-table freshness\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"LastRow\",\"formatter\":6},{\"columnMatch\":\"MinutesAgo\",\"formatter\":8,\"formatOptions\":{\"palette\":\"redBright\"}},{\"columnMatch\":\"TotalRows\",\"formatter\":8,\"formatOptions\":{\"palette\":\"blue\"}},{\"columnMatch\":\"Status\",\"formatter\":1}]}},\"name\":\"q-a02e37a8\"},{\"type\":1,\"content\":{\"json\":\"
Log Analytics operational events
\"},\"name\":\"div-log-analytics-operat-630749\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"_LogOperation\\n| where TimeGenerated > ago(24h)\\n| where _ResourceId contains \\\"tailscale\\\" or Detail contains \\\"Tailscale_\\\"\\n| project TimeGenerated, Operation, Level, Detail\\n| order by TimeGenerated desc | take 100\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Log Analytics operational events touching Tailscale tables\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"TimeGenerated\",\"formatter\":6},{\"columnMatch\":\"Level\",\"formatter\":1}]},\"noDataMessage\":\"No operational issues recorded in the last 24h.\",\"noDataMessageStyle\":1},\"name\":\"q-b492f150\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"pipeline\"},\"name\":\"group-pipeline\"},{\"type\":1,\"content\":{\"json\":\"
Tailscale Operations (Standard) (CCF) - Microsoft Sentinel content from the Tailscale (CCF) solution, Standard-tier surface. Tables polled from the Tailscale REST API: audit, devices, users, keys, dns, settings. Filter every panel via the time range above; the Investigate tab adds Actor and Device pickers for drilldown. For network flow logs and posture integrations, install the companion Tailscale Operations (Premium) workbook on a Premium / Enterprise tailnet.
\"},\"name\":\"footer\"}],\"fallbackResourceIds\":[\"Azure Monitor\"],\"fromTemplateId\":\"sentinel-Tailscale-CCF\",\"$schema\":\"https://github.com/Microsoft/Application-Insights-Workbooks/blob/master/schema/workbook.json\"}\n", "version": "1.0", "sourceId": "[variables('workspaceResourceId')]", "category": "sentinel" @@ -8496,7 +9396,7 @@ "email": "[variables('_email')]" }, "support": { - "name": "Community", + "name": "Tailscale (CCF)", "tier": "Community", "email": "ccfconnectors.county118@passmail.com", "link": "https://github.com/Azure/Azure-Sentinel/issues" @@ -8564,7 +9464,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "TailscalePremiumOperations Workbook with template version 3.0.5", + "description": "TailscalePremiumOperations Workbook with template version 3.0.1", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('workbookVersion2')]", @@ -8582,7 +9482,7 @@ }, "properties": { "displayName": "[parameters('workbook2-name')]", - "serializedData": "{\"version\":\"Notebook/1.0\",\"items\":[{\"type\":9,\"content\":{\"version\":\"KqlParameterItem/1.0\",\"parameters\":[{\"id\":\"5c59168c-014b-4a83-8a8f-73cc60997e6b\",\"version\":\"KqlParameterItem/1.0\",\"name\":\"TimeRange\",\"type\":4,\"isRequired\":true,\"value\":{\"durationMs\":86400000},\"typeSettings\":{\"selectableValues\":[{\"durationMs\":3600000},{\"durationMs\":14400000},{\"durationMs\":43200000},{\"durationMs\":86400000},{\"durationMs\":172800000},{\"durationMs\":604800000},{\"durationMs\":2592000000}]}}],\"style\":\"pills\",\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\"},\"name\":\"parameters\"},{\"type\":1,\"content\":{\"json\":\"
Tailscale Premium (CCF)
Comprehensive visibility for Premium and Enterprise tier tailnets. All Standard telemetry plus network flow logs and posture-integration inventory. Scope every panel with the time range below.
\"},\"name\":\"header\"},{\"type\":11,\"content\":{\"version\":\"LinkItem/1.0\",\"style\":\"tabs\",\"links\":[{\"id\":\"c1c44fc1-1b89-45a5-92cb-8e2374d9a855\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Overview\",\"subTarget\":\"overview\",\"style\":\"link\"},{\"id\":\"d1147d96-d052-47f7-b048-2b471f6f091c\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Devices\",\"subTarget\":\"devices\",\"style\":\"link\"},{\"id\":\"52c56980-5cfe-456c-a499-9cfd8317ad69\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Users & Identity\",\"subTarget\":\"users\",\"style\":\"link\"},{\"id\":\"9eb1d383-09e7-4aed-af3c-0dbe8c73dac6\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Credentials\",\"subTarget\":\"credentials\",\"style\":\"link\"},{\"id\":\"7cd3279a-4b91-430a-a68d-fb1fa2d74d9b\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"DNS & Tailnet\",\"subTarget\":\"dns\",\"style\":\"link\"},{\"id\":\"e5179782-bc17-42b3-9f8c-c2e0fbfd1f30\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Webhooks\",\"subTarget\":\"webhooks\",\"style\":\"link\"},{\"id\":\"6cce0298-c5ec-4092-bfc8-b84a6764fe62\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Network Flows\",\"subTarget\":\"network\",\"style\":\"link\"},{\"id\":\"8d8bbfed-b08e-401b-9f62-8eaa597261d3\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Posture\",\"subTarget\":\"posture\",\"style\":\"link\"},{\"id\":\"81d1910b-f903-4811-b762-cabeb7740c88\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Audit Activity\",\"subTarget\":\"audit\",\"style\":\"link\"},{\"id\":\"f685a8e3-878e-4cd6-b6d3-5340d57eb32b\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Security Signals\",\"subTarget\":\"signals\",\"style\":\"link\"}]},\"name\":\"tabs\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":1,\"content\":{\"json\":\"
At a glance
\"},\"name\":\"div-at-a-glance-75296e\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"union (Tailscale_Devices_CL | where TimeGenerated {TimeRange} | summarize arg_max(TimeGenerated, *) by DeviceId | summarize V=toreal(count()) | extend Metric=\\\"Devices\\\", Order=1),(Tailscale_Users_CL | where TimeGenerated {TimeRange} | summarize arg_max(TimeGenerated, *) by UserId | summarize V=toreal(count()) | extend Metric=\\\"Users\\\", Order=2),(Tailscale_Keys_CL | where TimeGenerated {TimeRange} | summarize arg_max(TimeGenerated, *) by KeyId | summarize V=toreal(countif(isnull(Revoked))) | extend Metric=\\\"Active keys\\\", Order=3),(Tailscale_Audit_CL | where TimeGenerated {TimeRange} | summarize V=toreal(count()) | extend Metric=\\\"Audit events\\\", Order=4),(Tailscale_Audit_CL | where TimeGenerated {TimeRange} | where tostring(Target.property) == \\\"ACL\\\" or tostring(Target.property) == \\\"DNS_SPLIT_DNS\\\" or tostring(Target.property) == \\\"DNS_NAMESERVERS\\\" or tostring(Target.property) == \\\"MAGICDNS_PREFERENCES\\\" | summarize V=toreal(count()) | extend Metric=\\\"DNS / ACL changes\\\", Order=5)\\n| order by Order asc | project Metric, Value=V\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"tiles\",\"tileSettings\":{\"titleContent\":{\"columnMatch\":\"Metric\",\"formatter\":1},\"leftContent\":{\"columnMatch\":\"Value\",\"formatter\":12,\"formatOptions\":{\"palette\":\"auto\"}},\"showBorder\":false}},\"name\":\"q-b71f9f4a\"},{\"type\":1,\"content\":{\"json\":\"
Activity over time
\"},\"name\":\"div-activity-over-time-bcc7c5\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| summarize EventCount = count() by bin(TimeGenerated, 1h), Action\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"title\":\"Audit events by action\",\"visualization\":\"timechart\"},\"name\":\"q-8fc88ccb\"},{\"type\":1,\"content\":{\"json\":\"
Top actors
\"},\"name\":\"div-top-actors-bd287b\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| extend ActorLogin = tostring(Actor.loginName)\\n| where isnotempty(ActorLogin)\\n| summarize EventCount = count() by ActorLogin\\n| top 10 by EventCount\\n| render barchart\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"title\":\"Top 10 actors\",\"visualization\":\"table\"},\"name\":\"q-93b0d742\",\"customWidth\":\"50\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| summarize EventCount = count() by TargetType = tostring(Target.type)\\n| top 10 by EventCount\\n| render piechart\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"title\":\"Activity by target type\",\"visualization\":\"table\"},\"name\":\"q-12fbe2ab\",\"customWidth\":\"50\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"overview\"},\"name\":\"group-overview\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":1,\"content\":{\"json\":\"
Device inventory snapshot
\"},\"name\":\"div-device-inventory-snapshot-56b7ce\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"let base = Tailscale_Devices_CL | summarize arg_max(TimeGenerated, *) by DeviceId;\\nunion (base | summarize V=toreal(count()) | extend Metric=\\\"Total devices\\\", Order=1),(base | where Authorized == true | summarize V=toreal(count()) | extend Metric=\\\"Authorized\\\", Order=2),(base | where IsExternal == true | summarize V=toreal(count()) | extend Metric=\\\"External (shared)\\\", Order=3),(base | where KeyExpiryDisabled == true | summarize V=toreal(count()) | extend Metric=\\\"Key expiry disabled\\\", Order=4),(base | where UpdateAvailable == true | summarize V=toreal(count()) | extend Metric=\\\"Update available\\\", Order=5),(base | where array_length(AdvertisedRoutes) > 0 | summarize V=toreal(count()) | extend Metric=\\\"Subnet routers\\\", Order=6),(base | where LastSeen < ago(30d) | summarize V=toreal(count()) | extend Metric=\\\"Stale (30+ days)\\\", Order=7)\\n| order by Order asc | project Metric, Value=V\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"tiles\",\"tileSettings\":{\"titleContent\":{\"columnMatch\":\"Metric\",\"formatter\":1},\"leftContent\":{\"columnMatch\":\"Value\",\"formatter\":12,\"formatOptions\":{\"palette\":\"auto\"}},\"showBorder\":false}},\"name\":\"q-b8b431c0\"},{\"type\":1,\"content\":{\"json\":\"
Distribution
\"},\"name\":\"div-distribution-d18e9a\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| summarize Count = count() by Os\\n| order by Count desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"title\":\"Devices by OS\",\"visualization\":\"piechart\"},\"name\":\"q-acf35833\",\"customWidth\":\"33\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| summarize Count = count() by ClientVersion\\n| order by Count desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"title\":\"Devices by client version\",\"visualization\":\"barchart\"},\"name\":\"q-29ed4eb0\",\"customWidth\":\"33\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| mv-expand Tag = Tags to typeof(string)\\n| summarize Devices = dcount(DeviceId) by Tag = iff(isempty(Tag), \\\"(untagged)\\\", Tag)\\n| order by Devices desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"title\":\"Devices by tag\",\"visualization\":\"piechart\"},\"name\":\"q-71ed6d65\",\"customWidth\":\"33\"},{\"type\":1,\"content\":{\"json\":\"
Device inventory table
\"},\"name\":\"div-device-inventory-table-ba97ce\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| extend DaysSinceSeen = datetime_diff('day', now(), LastSeen)\\n| extend DaysToExpiry = iff(KeyExpiryDisabled == true, int(null), datetime_diff('day', Expires, now()))\\n| project DeviceName, Hostname, User, Os, ClientVersion, UpdateAvailable, LastSeen, DaysSinceSeen, DaysToExpiry, KeyExpiryDisabled, Tags, AdvertisedRoutes\\n| order by LastSeen desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"title\":\"All devices (latest snapshot per device)\",\"visualization\":\"table\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"DaysSinceSeen\",\"formatter\":8,\"formatOptions\":{\"palette\":\"redBright\",\"min\":0,\"max\":60}},{\"columnMatch\":\"DaysToExpiry\",\"formatter\":8,\"formatOptions\":{\"palette\":\"redBright\",\"min\":0,\"max\":30}},{\"columnMatch\":\"UpdateAvailable\",\"formatter\":11},{\"columnMatch\":\"KeyExpiryDisabled\",\"formatter\":11}]}},\"name\":\"q-d0f59836\"},{\"type\":1,\"content\":{\"json\":\"
Subnet routers and exit nodes
\"},\"name\":\"div-subnet-routers-and-exit-nodes-b80917\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| where array_length(AdvertisedRoutes) > 0 or array_length(EnabledRoutes) > 0\\n| project DeviceName, User, Os, AdvertisedRoutes, EnabledRoutes, Tags, LastSeen\\n| order by DeviceName asc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"title\":\"Devices advertising routes (subnet routers / exit nodes)\",\"visualization\":\"table\"},\"name\":\"q-374f7fdc\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"devices\"},\"name\":\"group-devices\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":1,\"content\":{\"json\":\"
Identity overview
\"},\"name\":\"div-identity-overview-2881ca\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"let base = Tailscale_Users_CL | summarize arg_max(TimeGenerated, *) by UserId;\\nunion (base | summarize V=toreal(count()) | extend Metric=\\\"Users\\\", Order=1),(base | where Status =~ \\\"active\\\" | summarize V=toreal(count()) | extend Metric=\\\"Active\\\", Order=2),(base | where Status =~ \\\"suspended\\\" | summarize V=toreal(count()) | extend Metric=\\\"Suspended\\\", Order=3),(base | where CurrentlyConnected == true | summarize V=toreal(count()) | extend Metric=\\\"Currently connected\\\", Order=4),(base | where DeviceCount == 0 | summarize V=toreal(count()) | extend Metric=\\\"Zero devices\\\", Order=5),(base | where Role in (\\\"owner\\\",\\\"admin\\\",\\\"network-admin\\\") | summarize V=toreal(count()) | extend Metric=\\\"Elevated\\\", Order=6)\\n| order by Order asc | project Metric, Value=V\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"tiles\",\"tileSettings\":{\"titleContent\":{\"columnMatch\":\"Metric\",\"formatter\":1},\"leftContent\":{\"columnMatch\":\"Value\",\"formatter\":12,\"formatOptions\":{\"palette\":\"auto\"}},\"showBorder\":false}},\"name\":\"q-fd38a1c2\"},{\"type\":1,\"content\":{\"json\":\"
Distribution
\"},\"name\":\"div-distribution-e6ff99\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Users_CL\\n| summarize arg_max(TimeGenerated, *) by UserId\\n| summarize Users = count() by Role\\n| order by Users desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"title\":\"Users by role\",\"visualization\":\"piechart\"},\"name\":\"q-776b4d9b\",\"customWidth\":\"50\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Users_CL\\n| summarize arg_max(TimeGenerated, *) by UserId\\n| summarize Users = count() by Status\\n| order by Users desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"title\":\"Users by status\",\"visualization\":\"piechart\"},\"name\":\"q-13d1cabf\",\"customWidth\":\"50\"},{\"type\":1,\"content\":{\"json\":\"
User inventory table
\"},\"name\":\"div-user-inventory-table-ddbb44\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Users_CL\\n| summarize arg_max(TimeGenerated, *) by UserId\\n| extend DaysSinceSeen = datetime_diff('day', now(), LastSeen)\\n| project LoginName, DisplayName, Role, Status, UserType, DeviceCount, CurrentlyConnected, LastSeen, DaysSinceSeen, Created\\n| order by LastSeen desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"title\":\"All users (latest snapshot per user)\",\"visualization\":\"table\"},\"name\":\"q-5b6e7b3a\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"users\"},\"name\":\"group-users\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":1,\"content\":{\"json\":\"
Credential inventory
\"},\"name\":\"div-credential-inventory-30455a\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"let base = Tailscale_Keys_CL | summarize arg_max(TimeGenerated, *) by KeyId | where isnull(Revoked);\\nunion (base | summarize V=toreal(count()) | extend Metric=\\\"Active credentials\\\", Order=1),(base | where KeyType =~ \\\"auth\\\" | summarize V=toreal(count()) | extend Metric=\\\"Auth keys\\\", Order=2),(base | where KeyType in~ (\\\"apiAccessToken\\\",\\\"api_access_token\\\",\\\"apikey\\\") | summarize V=toreal(count()) | extend Metric=\\\"API tokens\\\", Order=3),(base | where KeyType in~ (\\\"oauthClient\\\",\\\"oauth_client\\\") | summarize V=toreal(count()) | extend Metric=\\\"OAuth clients\\\", Order=4),(base | where isnull(Expires) | summarize V=toreal(count()) | extend Metric=\\\"No expiry\\\", Order=5),(base | where isnotnull(Expires) and Expires < now()+7d | summarize V=toreal(count()) | extend Metric=\\\"Expiring within 7 days\\\", Order=6)\\n| order by Order asc | project Metric, Value=V\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"tiles\",\"tileSettings\":{\"titleContent\":{\"columnMatch\":\"Metric\",\"formatter\":1},\"leftContent\":{\"columnMatch\":\"Value\",\"formatter\":12,\"formatOptions\":{\"palette\":\"auto\"}},\"showBorder\":false}},\"name\":\"q-64bd4801\"},{\"type\":1,\"content\":{\"json\":\"
Credentials by type
\"},\"name\":\"div-credentials-by-type-00cf87\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Keys_CL\\n| summarize arg_max(TimeGenerated, *) by KeyId\\n| where isnull(Revoked)\\n| summarize Count = count() by KeyType\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"title\":\"Credentials by type\",\"visualization\":\"piechart\"},\"name\":\"q-32c060b4\",\"customWidth\":\"50\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Keys_CL\\n| summarize arg_max(TimeGenerated, *) by KeyId\\n| where isnull(Revoked) and isnotnull(Expires)\\n| extend DaysToExpiry = datetime_diff('day', Expires, now())\\n| extend Bucket = case(DaysToExpiry < 0, '0 expired', DaysToExpiry < 7, '1 within 7d', DaysToExpiry < 30, '2 within 30d', DaysToExpiry < 90, '3 within 90d', '4 over 90d')\\n| summarize Keys = count() by Bucket\\n| order by Bucket asc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"title\":\"Credentials by expiry window\",\"visualization\":\"barchart\"},\"name\":\"q-43beab6b\",\"customWidth\":\"50\"},{\"type\":1,\"content\":{\"json\":\"
Credential inventory table
\"},\"name\":\"div-credential-inventory-table-adc7f0\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Keys_CL\\n| summarize arg_max(TimeGenerated, *) by KeyId\\n| extend DaysToExpiry = iff(isnull(Expires), int(null), datetime_diff('day', Expires, now()))\\n| project KeyId, KeyType, Description, Created, Expires, DaysToExpiry, Revoked, Capabilities\\n| order by Created desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"title\":\"All credentials (latest snapshot per ID)\",\"visualization\":\"table\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"DaysToExpiry\",\"formatter\":8,\"formatOptions\":{\"palette\":\"redBright\",\"min\":0,\"max\":90}}]}},\"name\":\"q-4d271c05\"},{\"type\":1,\"content\":{\"json\":\"
Audit log: credential lifecycle
\"},\"name\":\"div-audit-log:-credential-lifecycl-f6dfb7\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| where tostring(Target.type) in (\\\"AUTH_KEY\\\", \\\"API_KEY\\\", \\\"OAUTH_CLIENT\\\")\\n| extend ActorLogin = tostring(Actor.loginName)\\n| project TimeGenerated, ActorLogin, Action, TargetType = tostring(Target.type), TargetId = tostring(Target.id), Description = tostring(New.description)\\n| order by TimeGenerated desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"title\":\"Create / update / delete events on credentials\",\"visualization\":\"table\"},\"name\":\"q-ecc5f778\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"credentials\"},\"name\":\"group-credentials\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":1,\"content\":{\"json\":\"
Current DNS state
\"},\"name\":\"div-current-dns-state-400b06\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Dns_CL\\n| summarize arg_max(TimeGenerated, *) by ConfigType\\n| project ConfigType, Nameservers, MagicDNS, SearchPaths, LastUpdated = TimeGenerated\\n| order by ConfigType asc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"title\":\"DNS configuration snapshot (latest per config type)\",\"visualization\":\"table\"},\"name\":\"q-12240394\"},{\"type\":1,\"content\":{\"json\":\"
Tailnet settings snapshot
\"},\"name\":\"div-tailnet-settings-snapshot-b777cf\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Settings_CL\\n| top 1 by TimeGenerated\\n| project\\n ['Snapshot at'] = TimeGenerated,\\n ['Device approval required'] = iff(DevicesApprovalOn == true, 'On', 'Off'),\\n ['Device auto-updates'] = iff(DevicesAutoUpdatesOn == true, 'On', 'Off'),\\n ['Device key duration (days)'] = DevicesKeyDurationDays,\\n ['User approval required'] = iff(UsersApprovalOn == true, 'On', 'Off'),\\n ['Users allowed to join external tailnets'] = UsersRoleAllowedToJoinExternalTailnets,\\n ['Regional routing'] = iff(RegionalRoutingOn == true, 'On', 'Off'),\\n ['Network flow logging'] = iff(NetworkFlowLoggingOn == true, 'On', 'Off'),\\n ['Posture identity collection'] = iff(PostureIdentityCollectionOn == true, 'On', 'Off')\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"title\":\"Current tailnet settings\",\"visualization\":\"table\"},\"name\":\"q-69053c5e\"},{\"type\":1,\"content\":{\"json\":\"
DNS / ACL / Settings change history
\"},\"name\":\"div-dns-/-acl-/-settings-change-hi-3efe2e\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| where Action == \\\"UPDATE\\\" and tostring(Target.type) == \\\"TAILNET\\\"\\n| extend Property = tostring(Target.property), ActorLogin = tostring(Actor.loginName)\\n| where Property in (\\\"ACL\\\", \\\"DNS_NAMESERVERS\\\", \\\"DNS_SPLIT_DNS\\\", \\\"DNS_SEARCHPATHS\\\", \\\"MAGICDNS_PREFERENCES\\\", \\\"SETTINGS\\\", \\\"TAILNET_SETTINGS\\\")\\n| project TimeGenerated, ActorLogin, Property, Old, New, Origin\\n| order by TimeGenerated desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"title\":\"Tailnet configuration changes\",\"visualization\":\"table\"},\"name\":\"q-bf4dcbaf\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"dns\"},\"name\":\"group-dns\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":1,\"content\":{\"json\":\"
Webhook inventory
\"},\"name\":\"div-webhook-inventory-ac2dd4\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Webhooks_CL\\n| summarize arg_max(TimeGenerated, *) by EndpointId\\n| project EndpointId, EndpointUrl, ProviderType, CreatorLoginName, Created, LastModified, Subscriptions\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"title\":\"All webhooks (latest snapshot per endpoint)\",\"visualization\":\"table\"},\"name\":\"q-4f947b64\"},{\"type\":1,\"content\":{\"json\":\"
Webhook change history (from audit log)
\"},\"name\":\"div-webhook-change-history-(from-a-b5a420\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| where tostring(Target.type) == \\\"WEBHOOK\\\" or tostring(Target.property) =~ \\\"WEBHOOK\\\"\\n| extend ActorLogin = tostring(Actor.loginName)\\n| project TimeGenerated, ActorLogin, Action, Target, Old, New, Origin\\n| order by TimeGenerated desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"title\":\"Webhook create / update / delete events\",\"visualization\":\"table\"},\"name\":\"q-cbd16912\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"webhooks\"},\"name\":\"group-webhooks\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":1,\"content\":{\"json\":\"
Network flow summary
\"},\"name\":\"div-network-flow-summary-4f9133\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"let base = Tailscale_Network_CL | where TimeGenerated {TimeRange};\\nunion (base | summarize V=toreal(count()) | extend Metric=\\\"Messages\\\", Order=1),(base | summarize V=toreal(dcount(NodeId)) | extend Metric=\\\"Unique nodes\\\", Order=2),(base | mv-expand t = VirtualTraffic | summarize V=toreal(sum(tolong(t.txBytes) + tolong(t.rxBytes))) | extend Metric=\\\"Bytes total (B)\\\", Order=3),(base | mv-expand t = ExitTraffic | summarize V=toreal(sum(tolong(t.txBytes) + tolong(t.rxBytes))) | extend Metric=\\\"Exit egress (B)\\\", Order=4),(base | mv-expand t = SubnetTraffic | summarize V=toreal(sum(tolong(t.txBytes) + tolong(t.rxBytes))) | extend Metric=\\\"Subnet (B)\\\", Order=5)\\n| order by Order asc | project Metric, Value=V\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"tiles\",\"tileSettings\":{\"titleContent\":{\"columnMatch\":\"Metric\",\"formatter\":1},\"leftContent\":{\"columnMatch\":\"Value\",\"formatter\":12,\"formatOptions\":{\"palette\":\"auto\"}},\"showBorder\":false}},\"name\":\"q-6aeb3506\"},{\"type\":1,\"content\":{\"json\":\"
Top talkers
\"},\"name\":\"div-top-talkers-a1283e\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Network_CL\\n| where TimeGenerated {TimeRange}\\n| mv-expand t = VirtualTraffic\\n| extend Src = tostring(t.src), Dst = tostring(t.dst), Bytes = tolong(t.txBytes) + tolong(t.rxBytes)\\n| summarize TotalBytes = sum(Bytes) by Src, Dst\\n| top 25 by TotalBytes\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"title\":\"Top 25 src->dst pairs by bytes\",\"visualization\":\"barchart\"},\"name\":\"q-994f3cfe\"},{\"type\":1,\"content\":{\"json\":\"
Exit-node usage
\"},\"name\":\"div-exit-node-usage-73c3f0\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Network_CL\\n| where TimeGenerated {TimeRange}\\n| where array_length(ExitTraffic) > 0\\n| mv-expand t = ExitTraffic\\n| extend Bytes = tolong(t.txBytes) + tolong(t.rxBytes)\\n| summarize TotalBytes = sum(Bytes), Flows = count() by SrcNodeName = tostring(SrcNode.name), ExitDst = tostring(t.dst)\\n| top 25 by TotalBytes\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"title\":\"Exit-node egress\",\"visualization\":\"table\"},\"name\":\"q-0a4f4625\"},{\"type\":1,\"content\":{\"json\":\"
Subnet router throughput
\"},\"name\":\"div-subnet-router-throughput-590dd2\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Network_CL\\n| where TimeGenerated {TimeRange}\\n| where array_length(SubnetTraffic) > 0\\n| mv-expand t = SubnetTraffic\\n| extend Bytes = tolong(t.txBytes) + tolong(t.rxBytes)\\n| summarize TotalBytes = sum(Bytes), Flows = count() by SrcNodeName = tostring(SrcNode.name)\\n| top 15 by TotalBytes\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"title\":\"Subnet routers by throughput\",\"visualization\":\"table\"},\"name\":\"q-b912a9e0\"},{\"type\":1,\"content\":{\"json\":\"
Bytes over time
\"},\"name\":\"div-bytes-over-time-e5bc09\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Network_CL\\n| where TimeGenerated {TimeRange}\\n| mv-expand t = VirtualTraffic\\n| extend Bytes = tolong(t.txBytes) + tolong(t.rxBytes)\\n| summarize TotalBytes = sum(Bytes) by bin(TimeGenerated, 1h)\\n| render timechart\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"title\":\"Virtual traffic bytes per hour\",\"visualization\":\"table\"},\"name\":\"q-505f4fda\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"network\"},\"name\":\"group-network\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":1,\"content\":{\"json\":\"
Posture inventory
\"},\"name\":\"div-posture-inventory-09f2f0\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_PostureIntegrations_CL\\n| summarize arg_max(TimeGenerated, *) by IntegrationId\\n| project Provider, IntegrationId, ClientId, TenantId_Provider, Status, ConfigOverwrites\\n| order by Provider asc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"title\":\"Current posture integrations\",\"visualization\":\"table\"},\"name\":\"q-46b6e6de\"},{\"type\":1,\"content\":{\"json\":\"
Integrations by provider
\"},\"name\":\"div-integrations-by-provider-539100\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_PostureIntegrations_CL\\n| summarize arg_max(TimeGenerated, *) by IntegrationId\\n| summarize Count = count() by Provider\\n| order by Count desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"title\":\"By provider\",\"visualization\":\"piechart\"},\"name\":\"q-65328ab4\",\"customWidth\":\"50\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Settings_CL\\n| top 1 by TimeGenerated\\n| project PostureIdentityCollectionOn\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"title\":\"Posture identity collection\",\"visualization\":\"table\"},\"name\":\"q-c5867d42\",\"customWidth\":\"50\"},{\"type\":1,\"content\":{\"json\":\"
Posture integration change history
\"},\"name\":\"div-posture-integration-change-his-dce848\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| where Action in (\\\"CREATE\\\", \\\"UPDATE\\\", \\\"DELETE\\\")\\n| where tostring(Target.type) startswith \\\"POSTURE\\\" or tostring(Target.type) == \\\"POSTURE_INTEGRATION\\\"\\n| extend ActorLogin = tostring(Actor.loginName)\\n| project TimeGenerated, ActorLogin, Action, Provider = tostring(Target.name), Old, New\\n| order by TimeGenerated desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"title\":\"Audit log: posture-integration changes\",\"visualization\":\"table\"},\"name\":\"q-6b24da73\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"posture\"},\"name\":\"group-posture\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":1,\"content\":{\"json\":\"
Audit volume
\"},\"name\":\"div-audit-volume-73f015\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| summarize EventCount = count() by bin(TimeGenerated, 1h)\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"title\":\"Audit events per hour\",\"visualization\":\"timechart\"},\"name\":\"q-b71cf8c3\"},{\"type\":1,\"content\":{\"json\":\"
Actor and action distribution
\"},\"name\":\"div-actor-and-action-distribution-2156c2\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| extend ActorLogin = tostring(Actor.loginName), TargetType = tostring(Target.type)\\n| summarize EventCount = count() by ActorLogin, Action, TargetType\\n| order by EventCount desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"title\":\"Actor / Action / Target heatmap\",\"visualization\":\"table\"},\"name\":\"q-2eddf283\"},{\"type\":1,\"content\":{\"json\":\"
Recent activity
\"},\"name\":\"div-recent-activity-669c80\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| extend ActorLogin = tostring(Actor.loginName), TargetType = tostring(Target.type), Property = tostring(Target.property)\\n| project TimeGenerated, ActorLogin, Action, TargetType, Property, TargetId = tostring(Target.id), Origin\\n| order by TimeGenerated desc\\n| take 100\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"title\":\"Recent 100 audit events\",\"visualization\":\"table\"},\"name\":\"q-49c1df44\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"audit\"},\"name\":\"group-audit\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":1,\"content\":{\"json\":\"
Tailscale-related alerts
\"},\"name\":\"div-tailscale-related-alerts-871583\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"SecurityAlert\\n| where TimeGenerated {TimeRange}\\n| where AlertName startswith \\\"Tailscale\\\"\\n| summarize Count = count() by AlertName, AlertSeverity\\n| order by Count desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"title\":\"Alerts by rule\",\"visualization\":\"table\"},\"name\":\"q-40e4f77b\",\"customWidth\":\"50\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"SecurityAlert\\n| where TimeGenerated {TimeRange}\\n| where AlertName startswith \\\"Tailscale\\\"\\n| summarize Count = count() by AlertSeverity\\n| render piechart\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"title\":\"Severity distribution\",\"visualization\":\"table\"},\"name\":\"q-5fed33a4\",\"customWidth\":\"50\"},{\"type\":1,\"content\":{\"json\":\"
Recent Tailscale alerts
\"},\"name\":\"div-recent-tailscale-alerts-8e085b\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"SecurityAlert\\n| where TimeGenerated {TimeRange}\\n| where AlertName startswith \\\"Tailscale\\\"\\n| project TimeGenerated, AlertName, AlertSeverity, Description, Entities\\n| order by TimeGenerated desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"title\":\"Recent Tailscale alerts\",\"visualization\":\"table\"},\"name\":\"q-0bf0cedc\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"signals\"},\"name\":\"group-signals\"},{\"type\":1,\"content\":{\"json\":\"---\\n\\n**Tailscale Premium (CCF)** | All Standard telemetry plus network flow logs (`/logging/network`) and posture integrations (`/posture/integrations`) on Premium / Enterprise tier tailnets.\"},\"name\":\"footer\"}],\"fromTemplateId\":\"sentinel-TailscalePremiumOperations\",\"$schema\":\"https://github.com/Microsoft/Application-Insights-Workbooks/blob/master/schema/workbook.json\"}\n", + "serializedData": "{\"version\":\"Notebook/1.0\",\"items\":[{\"type\":9,\"content\":{\"version\":\"KqlParameterItem/1.0\",\"parameters\":[{\"id\":\"7b05a598-5120-43f4-bf5d-576c2a7ff28d\",\"version\":\"KqlParameterItem/1.0\",\"name\":\"TimeRange\",\"type\":4,\"isRequired\":true,\"value\":{\"durationMs\":86400000},\"typeSettings\":{\"selectableValues\":[{\"durationMs\":3600000},{\"durationMs\":14400000},{\"durationMs\":43200000},{\"durationMs\":86400000},{\"durationMs\":172800000},{\"durationMs\":604800000},{\"durationMs\":2592000000}]}}],\"style\":\"pills\",\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\"},\"name\":\"parameters\"},{\"type\":1,\"content\":{\"json\":\"
Tailscale Operations (Premium)
Single-pane visibility into your Tailscale tailnet on Personal (Free), Starter and Premium tiers: who, what, when, where, and what changed. Scope every panel with the time range below; the Investigate tab adds Actor and Device pickers for drilldown. Premium-tier panels (network flow logs, posture integrations) live in the separate Tailscale Operations (Premium) workbook.
\"},\"name\":\"header\"},{\"type\":11,\"content\":{\"version\":\"LinkItem/1.0\",\"style\":\"tabs\",\"links\":[{\"id\":\"9794e9fd-916b-494d-8e72-af63d2f4c6c7\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Overview\",\"subTarget\":\"overview\",\"style\":\"link\"},{\"id\":\"71cf49db-33c5-4d4b-920a-2ec0c6a258dc\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Investigate\",\"subTarget\":\"investigate\",\"style\":\"link\"},{\"id\":\"5c56bb37-2053-47a0-b6fd-9539768c144d\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Hunts\",\"subTarget\":\"hunts\",\"style\":\"link\"},{\"id\":\"d2004ded-07f8-446a-a720-f0a63d1d9dda\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Identity\",\"subTarget\":\"identity\",\"style\":\"link\"},{\"id\":\"f23b3e14-1511-4a29-bf5e-bd65e55dbb40\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Devices\",\"subTarget\":\"devices\",\"style\":\"link\"},{\"id\":\"724f8352-e21b-45a6-9029-39dc92693c05\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Credentials\",\"subTarget\":\"credentials\",\"style\":\"link\"},{\"id\":\"8400ec64-e118-44e0-ae29-84afe94b8e0e\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Admin Audit\",\"subTarget\":\"audit\",\"style\":\"link\"},{\"id\":\"b7e04861-edfa-4426-b6be-f1481ca569b6\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Network & DNS\",\"subTarget\":\"network\",\"style\":\"link\"},{\"id\":\"b8506d3d-e615-4775-8c6f-14d9cc7db943\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Network Flows\",\"subTarget\":\"network-flows\",\"style\":\"link\"},{\"id\":\"c98574ec-7a60-4c1a-b4c6-4d24c5bd02ce\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Posture\",\"subTarget\":\"posture\",\"style\":\"link\"},{\"id\":\"cfbc96e4-8585-4d89-8f65-8344c8cc6eb2\",\"cellValue\":\"selectedTab\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Pipeline Health\",\"subTarget\":\"pipeline\",\"style\":\"link\"}]},\"name\":\"tabs\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":1,\"content\":{\"json\":\"
Tailnet at a glance
\"},\"name\":\"div-tailnet-at-a-glance-04e62b\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"let DEV = Tailscale_Devices_CL | summarize arg_max(TimeGenerated, *) by DeviceId;\\nlet USR = Tailscale_Users_CL | summarize arg_max(TimeGenerated, *) by UserId;\\nlet KEY = Tailscale_Keys_CL | summarize arg_max(TimeGenerated, *) by KeyId;\\nunion\\n (DEV | summarize V=toreal(count()) | extend Metric=\\\"Devices\\\", Order=1),\\n (DEV | where Authorized == true | summarize V=toreal(count()) | extend Metric=\\\"Authorized\\\", Order=2),\\n (DEV | where UpdateAvailable == true | summarize V=toreal(count()) | extend Metric=\\\"Updates Available\\\", Order=3),\\n (DEV | where SshEnabled == true | summarize V=toreal(count()) | extend Metric=\\\"SSH-Enabled\\\", Order=4),\\n (USR | summarize V=toreal(count()) | extend Metric=\\\"Users\\\", Order=5),\\n (USR | where Role =~ \\\"admin\\\" or Role =~ \\\"owner\\\" or Role =~ \\\"network-admin\\\" | summarize V=toreal(count()) | extend Metric=\\\"Admins\\\", Order=6),\\n (KEY | where isnull(Revoked) and (isnull(Expires) or Expires > now()) | summarize V=toreal(count()) | extend Metric=\\\"Active Keys\\\", Order=7),\\n (Tailscale_Audit_CL | where TimeGenerated {TimeRange} | summarize V=toreal(count()) | extend Metric=\\\"Audit Events ({TimeRange:label})\\\", Order=8)\\n,\\n (Tailscale_Network_CL | where TimeGenerated {TimeRange} | summarize V=toreal(count()) | extend Metric=\\\"Flows ({TimeRange:label})\\\", Order=9),\\n (Tailscale_Network_CL | where TimeGenerated {TimeRange} | summarize V=toreal(dcount(SrcNodeName)) | extend Metric=\\\"Active Talkers\\\", Order=10),\\n (Tailscale_Network_CL | where TimeGenerated {TimeRange} | summarize V=toreal(iff(count()==0, 0.0, 100.0 * countif(IsRelayed) / count())) | extend Metric=\\\"DERP Relayed %\\\", Order=11),\\n (Tailscale_PostureIntegrations_CL | where TimeGenerated {TimeRange} | summarize arg_max(TimeGenerated, *) by IntegrationId | summarize V=toreal(count()) | extend Metric=\\\"Posture Integrations\\\", Order=12)\\n| order by Order asc | project Metric, Value=V\",\"size\":3,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"tiles\",\"tileSettings\":{\"titleContent\":{\"columnMatch\":\"Metric\",\"formatter\":1},\"leftContent\":{\"columnMatch\":\"Value\",\"formatter\":12,\"formatOptions\":{\"palette\":\"auto\"}},\"showBorder\":false}},\"name\":\"q-4e9d7cff\"},{\"type\":1,\"content\":{\"json\":\"
Audit activity over time
\"},\"name\":\"div-audit-activity-over--546688\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| summarize EventCount = count() by bin(TimeGenerated, 1h), Action\\n| order by TimeGenerated asc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"timechart\",\"title\":\"Audit events by action\",\"noDataMessage\":\"No audit events in the selected window. Widen the time range; remember the Tailscale audit poll runs every ~30 min.\",\"noDataMessageStyle\":5},\"name\":\"q-effcd498\"},{\"type\":1,\"content\":{\"json\":\"
Who's doing what
\"},\"name\":\"div-who's-doing-what-52144e\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| extend Actor=tostring(coalesce(Actor.loginName, Actor.displayName, Actor.type))\\n| where isnotempty(Actor)\\n| summarize Events=count(), DistinctActions=dcount(Action), LastSeen=max(TimeGenerated) by Actor\\n| order by Events desc | take 15\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Top actors (by event count)\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"Events\",\"formatter\":8,\"formatOptions\":{\"palette\":\"blue\"}},{\"columnMatch\":\"LastSeen\",\"formatter\":6}]}},\"name\":\"q-34c77fa9\",\"customWidth\":\"50\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| extend TargetType=tostring(Target.type)\\n| where isnotempty(TargetType)\\n| summarize Events=count() by TargetType\\n| order by Events desc | take 15\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"piechart\",\"title\":\"Activity by target type\"},\"name\":\"q-4b3b709d\",\"customWidth\":\"50\"},{\"type\":1,\"content\":{\"json\":\"
Recent admin events
\"},\"name\":\"div-recent-admin-events-87c9e0\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| extend Actor=tostring(coalesce(Actor.loginName, Actor.displayName, Actor.type))\\n| extend TargetType=tostring(Target.type), TargetName=tostring(coalesce(Target.name, Target.id))\\n| project TimeGenerated, Action, Actor, TargetType, TargetName, Origin\\n| order by TimeGenerated desc | take 30\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Most recent 30 audit events\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"TimeGenerated\",\"formatter\":6}]}},\"name\":\"q-5e7d6306\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"overview\"},\"name\":\"group-overview\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":9,\"content\":{\"version\":\"KqlParameterItem/1.0\",\"parameters\":[{\"id\":\"b83a25c1-da18-49a2-a444-6517f13d891c\",\"version\":\"KqlParameterItem/1.0\",\"name\":\"SelectedActor\",\"label\":\"Actor\",\"type\":2,\"isRequired\":false,\"query\":\"let opts = Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\\n| where isnotempty(ActorLogin)\\n| summarize Events=count() by ActorLogin\\n| project value=ActorLogin, label=strcat(ActorLogin, \\\" (\\\", tostring(Events), \\\" events)\\\");\\n(print value=\\\"__ALL__\\\", label=\\\"(All actors)\\\")\\n| union opts\",\"typeSettings\":{\"showDefault\":false},\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"value\":\"__ALL__\"}],\"style\":\"pills\",\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\"},\"name\":\"investigate-picker-actor\"},{\"type\":1,\"content\":{\"json\":\"
Actor activity timeline
\"},\"name\":\"div-actor-activity-timel-5ec305\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\\n| where \\\"{SelectedActor}\\\" == \\\"__ALL__\\\" or ActorLogin == \\\"{SelectedActor}\\\"\\n| summarize Events=count() by bin(TimeGenerated, 1h), Action\\n| order by TimeGenerated asc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"timechart\",\"title\":\"Actions over time -- actor: {SelectedActor:label}\",\"noDataMessage\":\"Select an actor from the Actor dropdown above, or leave on 'All' to see total activity.\",\"noDataMessageStyle\":5},\"name\":\"q-32d40848\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\\n| where \\\"{SelectedActor}\\\" == \\\"__ALL__\\\" or ActorLogin == \\\"{SelectedActor}\\\"\\n| extend TargetType=tostring(Target.type), TargetName=tostring(coalesce(Target.name, Target.id))\\n| project TimeGenerated, ActorLogin, Action, TargetType, TargetName, Origin\\n| order by TimeGenerated desc | take 100\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Recent events for actor: {SelectedActor:label}\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"TimeGenerated\",\"formatter\":6}]},\"noDataMessage\":\"No events for this actor in the selected window.\",\"noDataMessageStyle\":5},\"name\":\"q-a742f6fd\"},{\"type\":9,\"content\":{\"version\":\"KqlParameterItem/1.0\",\"parameters\":[{\"id\":\"a9cf7907-f201-4725-a072-a8bd34bef74e\",\"version\":\"KqlParameterItem/1.0\",\"name\":\"SelectedDevice\",\"label\":\"Device\",\"type\":2,\"isRequired\":false,\"query\":\"let opts = Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| order by LastSeen desc | take 100\\n| project value=DeviceName, label=strcat(coalesce(DeviceName, Hostname), \\\" (\\\", User, \\\")\\\");\\n(print value=\\\"__ALL__\\\", label=\\\"(All devices)\\\")\\n| union opts\",\"typeSettings\":{\"showDefault\":false},\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"value\":\"__ALL__\"}],\"style\":\"pills\",\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\"},\"name\":\"investigate-picker-device\"},{\"type\":1,\"content\":{\"json\":\"
Selected device timeline
\"},\"name\":\"div-selected-device-time-00bead\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| where \\\"{SelectedDevice}\\\" == \\\"__ALL__\\\" or DeviceName == \\\"{SelectedDevice}\\\"\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| extend OnlineNow = ClientConnectivity.endpoints != \\\"\\\" or ConnectedToControl == true\\n| project DeviceName, Hostname, User, Os, ClientVersion, UpdateAvailable, Authorized, IsExternal, SshEnabled, LastSeen, Expires, KeyExpiryDisabled, OnlineNow, Addresses, Tags, AdvertisedRoutes\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Summary for device: {SelectedDevice:label}\",\"noDataMessage\":\"Select a device from the Device dropdown above. Defaults to 'All'.\",\"noDataMessageStyle\":5},\"name\":\"q-11094dc8\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| extend TargetType=tostring(Target.type), TargetName=tostring(Target.name), TargetId=tostring(Target.id)\\n| where (\\\"{SelectedDevice}\\\" == \\\"__ALL__\\\" and TargetType == \\\"NODE\\\") or TargetName == \\\"{SelectedDevice}\\\"\\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\\n| project TimeGenerated, Action, ActorLogin, TargetName, TargetId, Origin\\n| order by TimeGenerated desc | take 100\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Audit events touching device: {SelectedDevice:label}\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"TimeGenerated\",\"formatter\":6}]},\"noDataMessage\":\"No audit events recorded against the selected device in this window. Tailscale tags device events with Target.type=NODE; the audit feed only emits NODE events on create/update/delete, so quiet devices stay quiet here.\",\"noDataMessageStyle\":5},\"name\":\"q-ead106ce\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"investigate\"},\"name\":\"group-investigate\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":1,\"content\":{\"json\":\"
First-seen actors in the last 24h
\"},\"name\":\"div-first-seen-actors-in-ce38d9\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"let recent = Tailscale_Audit_CL | where TimeGenerated > ago(24h) | extend A=tostring(coalesce(Actor.loginName, Actor.displayName)) | summarize FirstSeen24h=min(TimeGenerated), Events=count() by A;\\nlet historical = Tailscale_Audit_CL | where TimeGenerated between(ago(30d) .. ago(24h)) | extend A=tostring(coalesce(Actor.loginName, Actor.displayName)) | distinct A;\\nrecent | join kind=leftanti historical on A | where isnotempty(A) | project FirstSeen24h, Actor=A, Events | order by Events desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Actors who have NEVER appeared before (30d baseline)\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"FirstSeen24h\",\"formatter\":6},{\"columnMatch\":\"Events\",\"formatter\":8,\"formatOptions\":{\"palette\":\"orange\"}}]},\"noDataMessage\":\"Every actor seen in the last 24h has appeared at least once in the prior 30d. Healthy state.\",\"noDataMessageStyle\":1},\"name\":\"q-9ba7b85e\"},{\"type\":1,\"content\":{\"json\":\"
Off-hours configuration changes
\"},\"name\":\"div-off-hours-configurat-3c3787\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| extend Hour=hourofday(TimeGenerated), DayOfWeek=dayofweek(TimeGenerated)/1d\\n| where Hour < 7 or Hour > 19 or DayOfWeek in (0, 6)\\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\\n| extend TargetType=tostring(Target.type)\\n| where Action !in (\\\"LOGIN\\\", \\\"LOGOUT\\\")\\n| project TimeGenerated, ActorLogin, Action, TargetType, Origin\\n| order by TimeGenerated desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Admin actions outside 07:00-19:00 weekdays\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"TimeGenerated\",\"formatter\":6}]},\"noDataMessage\":\"No off-hours admin changes recorded - healthy state for an organisation working business hours.\",\"noDataMessageStyle\":1},\"name\":\"q-721ee490\"},{\"type\":1,\"content\":{\"json\":\"
Devices with key expiry disabled
\"},\"name\":\"div-devices-with-key-exp-dfe9c1\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| where KeyExpiryDisabled == true\\n| project DeviceName, Hostname, User, Os, ClientVersion, LastSeen, Authorized, Tags\\n| order by LastSeen desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Devices that will never re-authenticate (high-risk drift)\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"LastSeen\",\"formatter\":6}]},\"noDataMessage\":\"No devices have key expiry disabled - good. Disabling key expiry creates devices that never re-auth, drifting from policy.\",\"noDataMessageStyle\":1},\"name\":\"q-8a8e2fb5\"},{\"type\":1,\"content\":{\"json\":\"
Auth keys with no expiry
\"},\"name\":\"div-auth-keys-with-no-ex-b11207\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Keys_CL\\n| summarize arg_max(TimeGenerated, *) by KeyId\\n| where isnull(Revoked) and (isnull(Expires) or ExpirySeconds == 0)\\n| project KeyId, Description, UserId, KeyType, Created, Capabilities\\n| order by Created desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Active keys that never expire\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"Created\",\"formatter\":6}]},\"noDataMessage\":\"No never-expiring auth keys - rotation hygiene is good.\",\"noDataMessageStyle\":1},\"name\":\"q-1372740c\"},{\"type\":1,\"content\":{\"json\":\"
Devices running outdated clients
\"},\"name\":\"div-devices-running-outd-bcd077\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| where UpdateAvailable == true\\n| project DeviceName, Hostname, User, Os, ClientVersion, LastSeen, Tags\\n| order by LastSeen desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Devices flagged update-available by Tailscale\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"LastSeen\",\"formatter\":6}]},\"noDataMessage\":\"All devices on current client - nothing to patch.\",\"noDataMessageStyle\":1},\"name\":\"q-ecebf1f7\"},{\"type\":1,\"content\":{\"json\":\"
Dormant devices (LastSeen > 30 days)
\"},\"name\":\"div-dormant-devices-(las-761156\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| where LastSeen < ago(30d)\\n| extend DaysIdle = toint((now() - LastSeen) / 1d)\\n| project DeviceName, Hostname, User, Os, ClientVersion, LastSeen, DaysIdle, Authorized, Tags\\n| order by DaysIdle desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Devices idle 30+ days - candidates for retirement\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"LastSeen\",\"formatter\":6},{\"columnMatch\":\"DaysIdle\",\"formatter\":8,\"formatOptions\":{\"palette\":\"redBright\"}}]},\"noDataMessage\":\"No devices idle 30+ days - inventory is fresh.\",\"noDataMessageStyle\":1},\"name\":\"q-fb6c1fcc\"},{\"type\":1,\"content\":{\"json\":\"
Subnet route exposure
\"},\"name\":\"div-subnet-route-exposur-289c42\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| where array_length(AdvertisedRoutes) > 0 or array_length(EnabledRoutes) > 0\\n| extend Routes = tostring(EnabledRoutes), Advertised = tostring(AdvertisedRoutes)\\n| project DeviceName, Hostname, User, Os, Advertised, Routes, LastSeen, SshEnabled, Authorized\\n| order by LastSeen desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Devices advertising or running subnet routes / exit-node duty\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"LastSeen\",\"formatter\":6}]},\"noDataMessage\":\"No devices advertising subnet routes. Pure mesh topology.\",\"noDataMessageStyle\":1},\"name\":\"q-9533b081\"},{\"type\":1,\"content\":{\"json\":\"
Devices with SSH enabled
\"},\"name\":\"div-devices-with-ssh-ena-285238\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| where SshEnabled == true\\n| project DeviceName, Hostname, User, Os, ClientVersion, LastSeen, Authorized, Tags\\n| order by LastSeen desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Devices with Tailscale SSH enabled (Tailscale-managed remote-shell access)\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"LastSeen\",\"formatter\":6}]},\"noDataMessage\":\"No devices have Tailscale SSH enabled - no SSH-via-Tailscale risk surface.\",\"noDataMessageStyle\":1},\"name\":\"q-735368fb\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"hunts\"},\"name\":\"group-hunts\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":1,\"content\":{\"json\":\"
User inventory snapshot
\"},\"name\":\"div-user-inventory-snaps-9dc96c\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"let U = Tailscale_Users_CL | summarize arg_max(TimeGenerated, *) by UserId;\\nunion\\n (U | summarize V=toreal(count()) | extend Metric=\\\"Total users\\\", Order=1),\\n (U | where Role in~ (\\\"admin\\\",\\\"owner\\\",\\\"network-admin\\\",\\\"it-admin\\\",\\\"billing-admin\\\") | summarize V=toreal(count()) | extend Metric=\\\"Admin-tier users\\\", Order=2),\\n (U | where Status =~ \\\"active\\\" | summarize V=toreal(count()) | extend Metric=\\\"Active\\\", Order=3),\\n (U | where CurrentlyConnected == true | summarize V=toreal(count()) | extend Metric=\\\"Connected now\\\", Order=4),\\n (U | where Status =~ \\\"idle\\\" or LastSeen < ago(30d) | summarize V=toreal(count()) | extend Metric=\\\"Idle / dormant\\\", Order=5),\\n (U | where UserType =~ \\\"shared\\\" | summarize V=toreal(count()) | extend Metric=\\\"Shared (external)\\\", Order=6)\\n| order by Order asc | project Metric, Value=V\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"tiles\",\"tileSettings\":{\"titleContent\":{\"columnMatch\":\"Metric\",\"formatter\":1},\"leftContent\":{\"columnMatch\":\"Value\",\"formatter\":12,\"formatOptions\":{\"palette\":\"auto\"}},\"showBorder\":false}},\"name\":\"q-5689d9e8\"},{\"type\":1,\"content\":{\"json\":\"
Distribution
\"},\"name\":\"div-distribution-de67ec\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Users_CL\\n| summarize arg_max(TimeGenerated, *) by UserId\\n| summarize Count=count() by Role\\n| order by Count desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"piechart\",\"title\":\"Users by role\"},\"name\":\"q-0c47912a\",\"customWidth\":\"33\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Users_CL\\n| summarize arg_max(TimeGenerated, *) by UserId\\n| summarize Count=count() by Status\\n| order by Count desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"piechart\",\"title\":\"Users by status\"},\"name\":\"q-e8c20a69\",\"customWidth\":\"33\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Users_CL\\n| summarize arg_max(TimeGenerated, *) by UserId\\n| summarize Count=count() by UserType\\n| order by Count desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"piechart\",\"title\":\"Users by type (member / shared)\"},\"name\":\"q-2c51776d\",\"customWidth\":\"33\"},{\"type\":1,\"content\":{\"json\":\"
Activity heatmap
\"},\"name\":\"div-activity-heatmap-ca6a21\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Users_CL\\n| summarize arg_max(TimeGenerated, *) by UserId\\n| extend DaysSinceLogin = toint((now() - LastSeen) / 1d)\\n| extend Bucket = case(\\n DaysSinceLogin < 1, \\\"Today\\\",\\n DaysSinceLogin < 7, \\\"This week\\\",\\n DaysSinceLogin < 30, \\\"This month\\\",\\n DaysSinceLogin < 90, \\\"Past quarter\\\",\\n \\\"90+ days\\\")\\n| summarize Users=count() by Bucket\\n| order by case(Bucket==\\\"Today\\\",1, Bucket==\\\"This week\\\",2, Bucket==\\\"This month\\\",3, Bucket==\\\"Past quarter\\\",4, 5) asc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"barchart\",\"title\":\"Users by recency of last login\"},\"name\":\"q-350e118d\"},{\"type\":1,\"content\":{\"json\":\"
Full user list
\"},\"name\":\"div-full-user-list-136aac\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Users_CL\\n| summarize arg_max(TimeGenerated, *) by UserId\\n| project DisplayName, LoginName, Role, Status, UserType, DeviceCount, CurrentlyConnected, Created, LastSeen\\n| order by LastSeen desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"All users (latest snapshot per user ID)\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"Created\",\"formatter\":6},{\"columnMatch\":\"LastSeen\",\"formatter\":6},{\"columnMatch\":\"Role\",\"formatter\":1},{\"columnMatch\":\"Status\",\"formatter\":1},{\"columnMatch\":\"DeviceCount\",\"formatter\":8,\"formatOptions\":{\"palette\":\"blue\"}}]}},\"name\":\"q-cee23d3c\"},{\"type\":1,\"content\":{\"json\":\"
Orphaned users (active but no devices)
\"},\"name\":\"div-orphaned-users-(acti-56d6f8\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Users_CL\\n| summarize arg_max(TimeGenerated, *) by UserId\\n| where Status =~ \\\"active\\\" and DeviceCount == 0\\n| project DisplayName, LoginName, Role, UserType, Created, LastSeen\\n| order by Created desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Active accounts with zero devices - candidates for offboarding review\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"Created\",\"formatter\":6},{\"columnMatch\":\"LastSeen\",\"formatter\":6}]},\"noDataMessage\":\"Every active account has at least one device - good hygiene.\",\"noDataMessageStyle\":1},\"name\":\"q-83fcd942\"},{\"type\":1,\"content\":{\"json\":\"
Role escalation history
\"},\"name\":\"div-role-escalation-hist-bd8df4\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| where Action == \\\"USER_ROLE_UPDATE\\\" or Action == \\\"USER_ROLES_ASSIGNED\\\" or Action contains \\\"ROLE\\\"\\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\\n| extend TargetName=tostring(coalesce(Target.name, Target.id))\\n| extend FromRole=tostring(Old.role), ToRole=tostring(New.role)\\n| project TimeGenerated, ActorLogin, Action, TargetName, FromRole, ToRole, Origin\\n| order by TimeGenerated desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Recent role changes\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"TimeGenerated\",\"formatter\":6},{\"columnMatch\":\"ToRole\",\"formatter\":1}]},\"noDataMessage\":\"No role changes in this window.\",\"noDataMessageStyle\":1},\"name\":\"q-f6c8358a\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"identity\"},\"name\":\"group-identity\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":1,\"content\":{\"json\":\"
Device fleet snapshot
\"},\"name\":\"div-device-fleet-snapsho-939675\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"let D = Tailscale_Devices_CL | summarize arg_max(TimeGenerated, *) by DeviceId;\\nunion\\n (D | summarize V=toreal(count()) | extend Metric=\\\"Total devices\\\", Order=1),\\n (D | where Authorized == true | summarize V=toreal(count()) | extend Metric=\\\"Authorized\\\", Order=2),\\n (D | where IsExternal == true | summarize V=toreal(count()) | extend Metric=\\\"External (shared)\\\", Order=3),\\n (D | where UpdateAvailable == true | summarize V=toreal(count()) | extend Metric=\\\"Updates available\\\", Order=4),\\n (D | where SshEnabled == true | summarize V=toreal(count()) | extend Metric=\\\"SSH-enabled\\\", Order=5),\\n (D | where KeyExpiryDisabled == true | summarize V=toreal(count()) | extend Metric=\\\"No key expiry\\\", Order=6),\\n (D | where array_length(AdvertisedRoutes) > 0 | summarize V=toreal(count()) | extend Metric=\\\"Subnet/exit-node\\\", Order=7),\\n (D | where LastSeen < ago(30d) | summarize V=toreal(count()) | extend Metric=\\\"Stale (30+ days)\\\", Order=8)\\n| order by Order asc | project Metric, Value=V\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"tiles\",\"tileSettings\":{\"titleContent\":{\"columnMatch\":\"Metric\",\"formatter\":1},\"leftContent\":{\"columnMatch\":\"Value\",\"formatter\":12,\"formatOptions\":{\"palette\":\"auto\"}},\"showBorder\":false}},\"name\":\"q-366964fc\"},{\"type\":1,\"content\":{\"json\":\"
Distribution
\"},\"name\":\"div-distribution-396d03\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| summarize Count=count() by Os\\n| order by Count desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"piechart\",\"title\":\"Devices by OS\"},\"name\":\"q-0c8f5988\",\"customWidth\":\"33\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| summarize Count=count() by ClientVersion\\n| order by Count desc | take 10\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"barchart\",\"title\":\"Top 10 client versions\"},\"name\":\"q-af1c45cd\",\"customWidth\":\"33\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| mv-expand Tag = Tags to typeof(string)\\n| summarize Devices=dcount(DeviceId) by Tag=iff(isempty(Tag), \\\"(untagged)\\\", Tag)\\n| order by Devices desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"piechart\",\"title\":\"Devices by tag\"},\"name\":\"q-c3a0ef5b\",\"customWidth\":\"33\"},{\"type\":1,\"content\":{\"json\":\"
Devices needing attention
\"},\"name\":\"div-devices-needing-atte-f04a47\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| where UpdateAvailable == true or KeyExpiryDisabled == true or LastSeen < ago(30d) or Authorized == false\\n| extend Issues = strcat_array(pack_array(\\n iff(UpdateAvailable == true, \\\"needs-update\\\", \\\"\\\"),\\n iff(KeyExpiryDisabled == true, \\\"key-never-expires\\\", \\\"\\\"),\\n iff(LastSeen < ago(30d), \\\"stale\\\", \\\"\\\"),\\n iff(Authorized == false, \\\"unauthorized\\\", \\\"\\\")), \\\",\\\")\\n| extend Issues = trim(\\\",\\\", trim_start(\\\",\\\", trim_end(\\\",\\\", replace_string(Issues, \\\",,\\\", \\\",\\\"))))\\n| project DeviceName, Hostname, User, Os, ClientVersion, LastSeen, Issues\\n| order by LastSeen desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Devices flagged with one or more issues\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"LastSeen\",\"formatter\":6},{\"columnMatch\":\"Issues\",\"formatter\":1}]},\"noDataMessage\":\"No devices need attention - all updated, fresh, authorized, and key-rotating.\",\"noDataMessageStyle\":1},\"name\":\"q-dc5db84c\"},{\"type\":1,\"content\":{\"json\":\"
Full device inventory
\"},\"name\":\"div-full-device-inventor-b70642\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| project DeviceName, Hostname, User, Os, ClientVersion, UpdateAvailable, Authorized, IsExternal, SshEnabled, LastSeen, KeyExpiryDisabled, Tags, AdvertisedRoutes\\n| order by LastSeen desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"All devices (latest snapshot per device ID)\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"LastSeen\",\"formatter\":6},{\"columnMatch\":\"Os\",\"formatter\":1},{\"columnMatch\":\"ClientVersion\",\"formatter\":1}]}},\"name\":\"q-7f7e7a9a\"},{\"type\":1,\"content\":{\"json\":\"
Subnet routers / exit nodes
\"},\"name\":\"div-subnet-routers-/-exi-b082d6\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| where array_length(AdvertisedRoutes) > 0\\n| extend AdvertisedSummary = tostring(AdvertisedRoutes), EnabledSummary = tostring(EnabledRoutes)\\n| project DeviceName, Hostname, User, Os, AdvertisedSummary, EnabledSummary, LastSeen, Authorized\\n| order by LastSeen desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Devices advertising subnet routes or exit-node capability\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"LastSeen\",\"formatter\":6}]},\"noDataMessage\":\"No subnet routers in this tailnet - pure mesh topology.\",\"noDataMessageStyle\":1},\"name\":\"q-5004df25\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"devices\"},\"name\":\"group-devices\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":1,\"content\":{\"json\":\"
Credentials snapshot
\"},\"name\":\"div-credentials-snapshot-fd464a\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"let K = Tailscale_Keys_CL | summarize arg_max(TimeGenerated, *) by KeyId;\\nunion\\n (K | summarize V=toreal(count()) | extend Metric=\\\"Total keys\\\", Order=1),\\n (K | where isnull(Revoked) and (isnull(Expires) or Expires > now()) | summarize V=toreal(count()) | extend Metric=\\\"Active\\\", Order=2),\\n (K | where isnotnull(Revoked) | summarize V=toreal(count()) | extend Metric=\\\"Revoked\\\", Order=3),\\n (K | where Expires < now() and isnull(Revoked) | summarize V=toreal(count()) | extend Metric=\\\"Expired\\\", Order=4),\\n (K | where isnull(Revoked) and Expires between(now() .. ago(-7d)) | summarize V=toreal(count()) | extend Metric=\\\"Expiring in 7d\\\", Order=5),\\n (K | where isnull(Revoked) and (isnull(Expires) or ExpirySeconds==0) | summarize V=toreal(count()) | extend Metric=\\\"Never expire\\\", Order=6),\\n (K | where KeyType =~ \\\"auth\\\" | summarize V=toreal(count()) | extend Metric=\\\"Auth keys\\\", Order=7),\\n (K | where KeyType =~ \\\"api\\\" or KeyType contains \\\"oauth\\\" | summarize V=toreal(count()) | extend Metric=\\\"API / OAuth\\\", Order=8)\\n| order by Order asc | project Metric, Value=V\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"tiles\",\"tileSettings\":{\"titleContent\":{\"columnMatch\":\"Metric\",\"formatter\":1},\"leftContent\":{\"columnMatch\":\"Value\",\"formatter\":12,\"formatOptions\":{\"palette\":\"auto\"}},\"showBorder\":false}},\"name\":\"q-79398bc0\"},{\"type\":1,\"content\":{\"json\":\"
Distribution
\"},\"name\":\"div-distribution-15f665\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Keys_CL\\n| summarize arg_max(TimeGenerated, *) by KeyId\\n| summarize Count=count() by KeyType\\n| order by Count desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"piechart\",\"title\":\"Keys by type\"},\"name\":\"q-23e6618b\",\"customWidth\":\"50\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Keys_CL\\n| summarize arg_max(TimeGenerated, *) by KeyId\\n| where isnull(Revoked)\\n| extend Bucket = case(\\n isnull(Expires) or ExpirySeconds == 0, \\\"Never\\\",\\n Expires < now(), \\\"Already expired\\\",\\n Expires < ago(-1d), \\\"<24h\\\",\\n Expires < ago(-7d), \\\"1-7d\\\",\\n Expires < ago(-30d), \\\"8-30d\\\",\\n Expires < ago(-90d), \\\"31-90d\\\",\\n \\\"90+d\\\")\\n| summarize Keys=count() by Bucket\\n| order by case(Bucket==\\\"Already expired\\\",1, Bucket==\\\"<24h\\\",2, Bucket==\\\"1-7d\\\",3, Bucket==\\\"8-30d\\\",4, Bucket==\\\"31-90d\\\",5, Bucket==\\\"90+d\\\",6, Bucket==\\\"Never\\\",7, 8) asc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"barchart\",\"title\":\"Active key expiry distribution\"},\"name\":\"q-7484d1a0\",\"customWidth\":\"50\"},{\"type\":1,\"content\":{\"json\":\"
Active credential register
\"},\"name\":\"div-active-credential-re-c27539\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Keys_CL\\n| summarize arg_max(TimeGenerated, *) by KeyId\\n| where isnull(Revoked)\\n| extend ExpiryStatus = case(\\n isnull(Expires) or ExpirySeconds == 0, \\\"Never expires\\\",\\n Expires < now(), \\\"Expired\\\",\\n Expires < ago(-7d), \\\"Expires in 7d\\\",\\n Expires < ago(-30d), \\\"Expires in 30d\\\",\\n \\\"OK\\\")\\n| project KeyId, KeyType, Description, UserId, Created, Expires, ExpiryStatus, Capabilities\\n| order by Created desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"All active credentials with computed expiry status\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"Created\",\"formatter\":6},{\"columnMatch\":\"Expires\",\"formatter\":6},{\"columnMatch\":\"ExpiryStatus\",\"formatter\":1},{\"columnMatch\":\"KeyType\",\"formatter\":1}]}},\"name\":\"q-4b27a750\"},{\"type\":1,\"content\":{\"json\":\"
Credential CRUD events
\"},\"name\":\"div-credential-crud-even-e455b0\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| where Action contains \\\"API_KEY\\\" or Action contains \\\"AUTH_KEY\\\" or Action contains \\\"OAUTH\\\" or Action contains \\\"KEY_CREATE\\\" or Action contains \\\"KEY_REVOKE\\\"\\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\\n| extend TargetId=tostring(Target.id), TargetType=tostring(Target.type)\\n| project TimeGenerated, Action, ActorLogin, TargetType, TargetId, Origin\\n| order by TimeGenerated desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Recent credential create / revoke / rotate events\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"TimeGenerated\",\"formatter\":6}]},\"noDataMessage\":\"No credential CRUD activity in this window.\",\"noDataMessageStyle\":1},\"name\":\"q-777693bd\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"credentials\"},\"name\":\"group-credentials\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":1,\"content\":{\"json\":\"
Audit volume
\"},\"name\":\"div-audit-volume-de29a2\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| summarize Events=count() by bin(TimeGenerated, 1h)\\n| order by TimeGenerated asc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"timechart\",\"title\":\"Audit events per hour\",\"noDataMessage\":\"No audit events in this window.\",\"noDataMessageStyle\":5},\"name\":\"q-f5eee265\"},{\"type\":1,\"content\":{\"json\":\"
Action heatmap by hour of day
\"},\"name\":\"div-action-heatmap-by-ho-c8bd5e\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| extend Hour=hourofday(TimeGenerated)\\n| summarize Events=count() by Hour, Action\\n| order by Hour asc, Events desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"categoricalbar\",\"title\":\"When are admin actions happening?\"},\"name\":\"q-c6f45d95\"},{\"type\":1,\"content\":{\"json\":\"
Actor / Action heatmap
\"},\"name\":\"div-actor-/-action-heatm-d820ba\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\\n| where isnotempty(ActorLogin)\\n| summarize Events=count() by ActorLogin, Action\\n| order by Events desc | take 100\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Who is firing which action\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"Events\",\"formatter\":4,\"formatOptions\":{\"palette\":\"blue\"}}]},\"noDataMessage\":\"No audit events in this window.\",\"noDataMessageStyle\":5},\"name\":\"q-06c13b3b\"},{\"type\":1,\"content\":{\"json\":\"
Recent activity
\"},\"name\":\"div-recent-activity-63b210\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\\n| extend TargetType=tostring(Target.type), TargetName=tostring(coalesce(Target.name, Target.id))\\n| project TimeGenerated, Action, ActorLogin, TargetType, TargetName, Origin, EventGroupID\\n| order by TimeGenerated desc | take 100\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Last 100 audit events\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"TimeGenerated\",\"formatter\":6},{\"columnMatch\":\"Action\",\"formatter\":1}]},\"noDataMessage\":\"No audit events in this window.\",\"noDataMessageStyle\":5},\"name\":\"q-07a25a40\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"audit\"},\"name\":\"group-audit\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":1,\"content\":{\"json\":\"
DNS configuration (current state)
\"},\"name\":\"div-dns-configuration-(c-5f2e1e\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Dns_CL\\n| summarize arg_max(TimeGenerated, *) by ConfigType\\n| project ConfigType, Nameservers, MagicDNS, SearchPaths, LastSnapshot=TimeGenerated\\n| order by ConfigType asc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"MagicDNS, nameservers, search paths\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"LastSnapshot\",\"formatter\":6},{\"columnMatch\":\"ConfigType\",\"formatter\":1}]},\"noDataMessage\":\"No DNS snapshots in the workspace yet. DNS polls runs at ~30 min cadence.\",\"noDataMessageStyle\":5},\"name\":\"q-d6a6a358\"},{\"type\":1,\"content\":{\"json\":\"
Tailnet settings (current)
\"},\"name\":\"div-tailnet-settings-(cu-e03b16\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Settings_CL\\n| summarize arg_max(TimeGenerated, *) by TenantId\\n| project DevicesApprovalOn, DevicesAutoUpdatesOn, DevicesKeyDurationDays, UsersApprovalOn, NetworkFlowLoggingOn, RegionalRoutingOn, PostureIdentityCollectionOn, UsersRoleAllowedToJoinExternalTailnets, LastSnapshot=TimeGenerated\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Tailnet policy gates\",\"noDataMessage\":\"No settings snapshot yet.\",\"noDataMessageStyle\":5},\"name\":\"q-0622cfc3\"},{\"type\":1,\"content\":{\"json\":\"
DNS change history
\"},\"name\":\"div-dns-change-history-9dd376\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| extend TargetProperty=tostring(Target.property)\\n| where Action contains \\\"DNS\\\" or TargetProperty has_any (\\\"DNS_NAMESERVERS\\\", \\\"DNS_SPLIT_DNS\\\", \\\"MAGICDNS\\\", \\\"DNS_SEARCH_PATHS\\\")\\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\\n| project TimeGenerated, ActorLogin, Action, TargetProperty, Origin\\n| order by TimeGenerated desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Recent DNS-related admin changes\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"TimeGenerated\",\"formatter\":6}]},\"noDataMessage\":\"No DNS changes in this window.\",\"noDataMessageStyle\":1},\"name\":\"q-a132ff6a\"},{\"type\":1,\"content\":{\"json\":\"
ACL policy changes
\"},\"name\":\"div-acl-policy-changes-ff0e68\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| where Action == \\\"ACL_UPDATE\\\" or Action contains \\\"ACL\\\"\\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\\n| project TimeGenerated, ActorLogin, Action, Origin, EventGroupID\\n| order by TimeGenerated desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Recent ACL / policy file modifications\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"TimeGenerated\",\"formatter\":6}]},\"noDataMessage\":\"No ACL changes in this window.\",\"noDataMessageStyle\":1},\"name\":\"q-94818c53\"},{\"type\":1,\"content\":{\"json\":\"
Subnet routes & exit nodes
\"},\"name\":\"div-subnet-routes-and-ex-eef47f\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Devices_CL\\n| summarize arg_max(TimeGenerated, *) by DeviceId\\n| where array_length(EnabledRoutes) > 0\\n| project DeviceName, User, Os, EnabledRoutes=tostring(EnabledRoutes), AdvertisedRoutes=tostring(AdvertisedRoutes), LastSeen\\n| order by LastSeen desc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Routes currently being served from devices\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"LastSeen\",\"formatter\":6}]},\"noDataMessage\":\"No subnet routers active in this tailnet.\",\"noDataMessageStyle\":1},\"name\":\"q-61a792cd\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"network\"},\"name\":\"group-network\"},{\"type\":12,\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"network-flows\"},\"name\":\"group-network-flows\",\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":1,\"content\":{\"json\":\"
Activity snapshot
\"},\"name\":\"div-flow-snapshot\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"let NET = Tailscale_Network_CL | where TimeGenerated {TimeRange};\\nunion\\n (NET | summarize V=toreal(count()) | extend Metric=\\\"Total flows\\\", Order=1),\\n (NET | summarize V=toreal(dcount(SrcNodeName)) | extend Metric=\\\"Src nodes\\\", Order=2),\\n (NET | summarize V=toreal(dcount(DstNodeName)) | extend Metric=\\\"Dst nodes\\\", Order=3),\\n (NET | where HasVirtualTraffic | summarize V=toreal(count())| extend Metric=\\\"Virtual\\\", Order=4),\\n (NET | where HasSubnetTraffic | summarize V=toreal(count())| extend Metric=\\\"Subnet\\\", Order=5),\\n (NET | where HasExitTraffic | summarize V=toreal(count())| extend Metric=\\\"Exit\\\", Order=6),\\n (NET | where IsRelayed | summarize V=toreal(count())| extend Metric=\\\"Relayed (DERP)\\\", Order=7)\\n| order by Order asc | project Metric, Value=V\",\"size\":3,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"tiles\",\"title\":\"Activity (selected time range)\",\"noDataMessage\":\"No data in this window.\",\"noDataMessageStyle\":5,\"tileSettings\":{\"titleContent\":{\"columnMatch\":\"Metric\",\"formatter\":1},\"leftContent\":{\"columnMatch\":\"Value\",\"formatter\":12,\"formatOptions\":{\"palette\":\"auto\"},\"numberFormat\":{\"unit\":17,\"options\":{\"style\":\"decimal\",\"maximumFractionDigits\":0}}},\"showBorder\":false}},\"name\":\"q-flow-tiles\"},{\"type\":1,\"content\":{\"json\":\"
Top talkers
\"},\"name\":\"div-flow-top-talkers\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Network_CL\\n| where TimeGenerated {TimeRange}\\n| extend SrcLabel = case(\\n isnotempty(SrcUser), strcat(SrcNodeName, \\\" - \\\", SrcUser),\\n isnotempty(SrcTags), strcat(SrcNodeName, \\\" \\\", tostring(SrcTags)),\\n SrcNodeName)\\n| summarize Flows=count() by SrcLabel\\n| top 10 by Flows\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"barchart\",\"title\":\"Top source nodes by flow count\",\"noDataMessage\":\"No flow records in this window.\",\"noDataMessageStyle\":5},\"name\":\"q-flow-top-src\",\"customWidth\":\"50\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Network_CL\\n| where TimeGenerated {TimeRange}\\n| extend DstLabel = case(\\n isnotempty(DstUser), strcat(DstNodeName, \\\" - \\\", DstUser),\\n isnotempty(DstTags), strcat(DstNodeName, \\\" \\\", tostring(DstTags)),\\n DstNodeName)\\n| extend DstKind = case(\\n isnotempty(DstUser), \\\"User device\\\",\\n isnotempty(DstTags), \\\"Tagged service\\\",\\n \\\"Other\\\")\\n| summarize Flows=count() by DstLabel, DstKind\\n| top 10 by Flows\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"categoricalbar\",\"title\":\"Top destination nodes (split by user vs tagged service)\",\"noDataMessage\":\"No flow records in this window.\",\"noDataMessageStyle\":5},\"name\":\"q-flow-top-dst\",\"customWidth\":\"50\"},{\"type\":1,\"content\":{\"json\":\"
Traffic mix over time (stacked area: Virtual / Subnet / Exit / Physical)
\"},\"name\":\"div-flow-time-mix\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"let NET = Tailscale_Network_CL | where TimeGenerated {TimeRange};\\nunion\\n (NET | where HasVirtualTraffic | summarize Flows=count() by bin(TimeGenerated, 10m) | extend Kind=\\\"Virtual\\\"),\\n (NET | where HasSubnetTraffic | summarize Flows=count() by bin(TimeGenerated, 10m) | extend Kind=\\\"Subnet\\\"),\\n (NET | where HasExitTraffic | summarize Flows=count() by bin(TimeGenerated, 10m) | extend Kind=\\\"Exit\\\"),\\n (NET | where HasPhysicalTraffic | summarize Flows=count() by bin(TimeGenerated, 10m) | extend Kind=\\\"Physical\\\")\\n| project TimeGenerated, Kind, Flows\\n| order by TimeGenerated asc\",\"size\":4,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"timechart\",\"title\":\"Flow count by traffic kind over time\",\"noDataMessage\":\"No flow records.\",\"noDataMessageStyle\":5,\"chartSettings\":{\"group\":\"Kind\",\"createOtherGroup\":10,\"showLegend\":true,\"ySettings\":{\"min\":0},\"chartType\":\"Area\"}},\"name\":\"q-flow-traffic-mix\"},{\"type\":1,\"content\":{\"json\":\"
DERP relay health
\"},\"name\":\"div-flow-derp-watch\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Network_CL\\n| where TimeGenerated {TimeRange}\\n| summarize Count=count() by Path = iff(IsRelayed, \\\"Relayed (DERP)\\\", \\\"Direct (P2P)\\\")\\n| project Path, Count\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"piechart\",\"title\":\"Direct vs relayed\",\"noDataMessage\":\"No flow records.\",\"noDataMessageStyle\":5},\"name\":\"q-flow-derp-pie\",\"customWidth\":\"40\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Network_CL\\n| where TimeGenerated {TimeRange}\\n| where IsRelayed\\n| extend SrcLabel = strcat(SrcNodeName, iff(isempty(SrcUser),\\\"\\\",strcat(\\\" (\\\",SrcUser,\\\")\\\")))\\n| summarize RelayedFlows=count(), LastRelay=max(TimeGenerated) by SrcLabel, SrcOs\\n| top 10 by RelayedFlows\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Devices stuck on DERP (potential NAT/firewall issue)\",\"noDataMessage\":\"No data in this window.\",\"noDataMessageStyle\":5,\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"RelayedFlows\",\"formatter\":4,\"formatOptions\":{\"palette\":\"orange\"}},{\"columnMatch\":\"LastRelay\",\"formatter\":6}]}},\"name\":\"q-flow-derp-devices\",\"customWidth\":\"60\"},{\"type\":1,\"content\":{\"json\":\"
Tagged-service flows + anomaly hunt
\"},\"name\":\"div-flow-tag-anomaly\"},{\"type\":12,\"name\":\"row-tag-anomaly-row\",\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Network_CL\\n| where TimeGenerated {TimeRange}\\n| extend SrcKind = case(\\n isnotempty(SrcTags), tostring(SrcTags),\\n isnotempty(SrcUser), \\\"\\\",\\n \\\"\\\")\\n| extend DstKind = case(\\n isnotempty(DstTags), tostring(DstTags),\\n isnotempty(DstUser), \\\"\\\",\\n \\\"\\\")\\n| summarize Flows=count() by SrcKind, DstKind\\n| order by Flows desc\",\"size\":1,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Tagged-service flow matrix\",\"noDataMessage\":\"No data in this window.\",\"noDataMessageStyle\":5,\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"Flows\",\"formatter\":4,\"formatOptions\":{\"palette\":\"blue\"}}]}},\"name\":\"q-flow-tag-matrix\",\"customWidth\":\"50\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"let baseline = Tailscale_Network_CL\\n | where TimeGenerated between (ago(7d) .. ago(1h))\\n | distinct SrcNodeName, DstNodeName;\\nTailscale_Network_CL\\n| where TimeGenerated {TimeRange}\\n| where HasVirtualTraffic or HasSubnetTraffic or HasExitTraffic\\n| extend ShortSrc = tostring(split(SrcNodeName, \\\".\\\")[0])\\n| extend ShortDst = tostring(split(DstNodeName, \\\".\\\")[0])\\n| extend ShortSrcUser = tostring(split(SrcUser, \\\"@\\\")[0])\\n| extend ShortDstUser = tostring(split(DstUser, \\\"@\\\")[0])\\n| extend Src = strcat(ShortSrc, iff(isempty(ShortSrcUser),\\\"\\\",strcat(\\\" - \\\",ShortSrcUser)))\\n| extend Dst = strcat(ShortDst, iff(isempty(ShortDstUser),\\\"\\\",strcat(\\\" - \\\",ShortDstUser)))\\n| summarize FirstSeen=min(TimeGenerated), Flows=count() by Src, Dst, SrcNodeName, DstNodeName\\n| join kind=leftanti baseline on SrcNodeName, DstNodeName\\n| project FirstSeen, Src, Dst, Flows\\n| order by FirstSeen desc\",\"size\":1,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Anomaly: pairs NOT seen in past 7 days\",\"noDataMessage\":\"No new src/dst pairs - all flows match the 7-day baseline.\",\"noDataMessageStyle\":5,\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"FirstSeen\",\"formatter\":6},{\"columnMatch\":\"Flows\",\"formatter\":4,\"formatOptions\":{\"palette\":\"red\"}}]}},\"name\":\"q-flow-new-pairs\",\"customWidth\":\"50\"}]}},{\"type\":1,\"content\":{\"json\":\"
Egress destinations - exit nodes + subnet routes
\"},\"name\":\"div-flow-egress\"},{\"type\":12,\"name\":\"row-egress-row\",\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Network_CL\\n| where TimeGenerated {TimeRange}\\n| where HasExitTraffic\\n| extend ExitTag = iff(isempty(DstTags), DstNodeName, tostring(DstTags))\\n| summarize Flows=count() by ExitTag\",\"size\":1,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"piechart\",\"title\":\"Exit-node tag distribution\",\"noDataMessage\":\"No exit-node traffic in this window.\",\"noDataMessageStyle\":5},\"name\":\"q-flow-exit-providers\",\"customWidth\":\"40\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Network_CL\\n| where TimeGenerated {TimeRange}\\n| where HasSubnetTraffic\\n| mv-expand s=SubnetTraffic\\n| extend DstHost = tostring(split(tostring(s.dst), \\\":\\\")[0])\\n| extend Bytes = toint(coalesce(s.txBytes,0)) + toint(coalesce(s.rxBytes,0))\\n| extend Pkts = toint(coalesce(s.txPkts,0)) + toint(coalesce(s.rxPkts,0))\\n| summarize TotalBytes=sum(Bytes), TotalPkts=sum(Pkts), Talkers=dcount(SrcNodeName) by DstHost\\n| top 10 by TotalBytes\\n| project DstHost, TotalBytes, TotalPkts, Talkers\",\"size\":1,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Top subnet route destinations\",\"noDataMessage\":\"No subnet-route traffic in this window.\",\"noDataMessageStyle\":5,\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"TotalBytes\",\"formatter\":4,\"formatOptions\":{\"palette\":\"green\"}},{\"columnMatch\":\"TotalPkts\",\"formatter\":4,\"formatOptions\":{\"palette\":\"blue\"}},{\"columnMatch\":\"Talkers\",\"formatter\":4,\"formatOptions\":{\"palette\":\"purple\"}}]}},\"name\":\"q-flow-subnet-dests\",\"customWidth\":\"60\"}]}},{\"type\":1,\"content\":{\"json\":\"
Recent flow detail (last 100)
\"},\"name\":\"div-flow-recent-detail\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Network_CL\\n| where TimeGenerated {TimeRange}\\n| order by TimeGenerated desc\\n| take 100\\n| project TimeGenerated,\\n Src=SrcNodeName, SrcUser, SrcTags=tostring(SrcTags),\\n Dst=DstNodeName, DstUser, DstTags=tostring(DstTags),\\n Vir=HasVirtualTraffic, Sub=HasSubnetTraffic, Exi=HasExitTraffic, Phy=HasPhysicalTraffic, IsRelayed\",\"size\":4,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Last 100 flow records in time range\",\"noDataMessage\":\"No data in this window.\",\"noDataMessageStyle\":5,\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"TimeGenerated\",\"formatter\":6},{\"columnMatch\":\"IsRelayed\",\"formatter\":11}]}},\"name\":\"q-flow-recent\"}]}},{\"type\":12,\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"posture\"},\"name\":\"group-posture\",\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":1,\"content\":{\"json\":\"
Posture integrations - MDM/EDR providers configured for device posture
\"},\"name\":\"div-posture-overview\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"let CUR = Tailscale_PostureIntegrations_CL\\n | where TimeGenerated {TimeRange}\\n | summarize arg_max(TimeGenerated, *) by IntegrationId;\\nunion\\n (CUR | summarize V=toreal(count()) | extend Metric=\\\"Integrations\\\", Order=1),\\n (CUR | summarize V=toreal(dcount(Provider)) | extend Metric=\\\"Distinct providers\\\", Order=2),\\n (CUR | where tostring(Status) has \\\"healthy\\\" or tostring(Status) has \\\"ok\\\"\\n | summarize V=toreal(count()) | extend Metric=\\\"Healthy\\\", Order=3),\\n (CUR | where not(tostring(Status) has \\\"healthy\\\" or tostring(Status) has \\\"ok\\\")\\n | summarize V=toreal(count()) | extend Metric=\\\"Unhealthy / unknown\\\", Order=4)\\n| order by Order asc | project Metric, Value=V\",\"size\":3,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"tiles\",\"title\":\"Integration inventory (selected time range)\",\"noDataMessage\":\"No posture integrations configured. Set them up at https://login.tailscale.com/admin/settings/posture-integrations.\",\"noDataMessageStyle\":5,\"tileSettings\":{\"titleContent\":{\"columnMatch\":\"Metric\",\"formatter\":1},\"leftContent\":{\"columnMatch\":\"Value\",\"formatter\":12,\"formatOptions\":{\"palette\":\"auto\"},\"numberFormat\":{\"unit\":17,\"options\":{\"style\":\"decimal\",\"maximumFractionDigits\":0}}},\"showBorder\":false}},\"name\":\"q-posture-tile\"},{\"type\":1,\"content\":{\"json\":\"
Distribution
\"},\"name\":\"div-posture-distribution\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_PostureIntegrations_CL\\n| where TimeGenerated {TimeRange}\\n| summarize arg_max(TimeGenerated, *) by IntegrationId\\n| summarize Count=count() by Provider\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"piechart\",\"title\":\"Integrations by provider\",\"noDataMessage\":\"No posture integrations configured.\",\"noDataMessageStyle\":5},\"name\":\"q-posture-by-provider\",\"customWidth\":\"50\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_PostureIntegrations_CL\\n| where TimeGenerated {TimeRange}\\n| summarize arg_max(TimeGenerated, *) by IntegrationId\\n| extend Health = case(\\n tostring(Status) has \\\"healthy\\\" or tostring(Status) has \\\"ok\\\", \\\"Healthy\\\",\\n tostring(Status) has \\\"error\\\" or tostring(Status) has \\\"failed\\\", \\\"Error\\\",\\n isnotempty(tostring(Status)), \\\"Other\\\",\\n \\\"Unknown\\\")\\n| summarize Count=count() by Health\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"piechart\",\"title\":\"Health status across integrations\",\"noDataMessage\":\"No posture integrations configured.\",\"noDataMessageStyle\":5},\"name\":\"q-posture-by-status\",\"customWidth\":\"50\"},{\"type\":1,\"content\":{\"json\":\"
Inventory detail
\"},\"name\":\"div-posture-inventory\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_PostureIntegrations_CL\\n| where TimeGenerated {TimeRange}\\n| summarize arg_max(TimeGenerated, *) by IntegrationId\\n| project IntegrationId, Provider, CloudId, ClientId, TenantId_Provider, Status, LastSnapshot=TimeGenerated\",\"size\":4,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Configured integrations (latest snapshot per IntegrationId)\",\"noDataMessage\":\"No posture integrations configured.\",\"noDataMessageStyle\":5,\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"Provider\",\"formatter\":11},{\"columnMatch\":\"LastSnapshot\",\"formatter\":6}]}},\"name\":\"q-posture-inventory\"},{\"type\":1,\"content\":{\"json\":\"
Lifecycle (from audit log)
\"},\"name\":\"div-posture-lifecycle\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"Tailscale_Audit_CL\\n| where TimeGenerated {TimeRange}\\n| where EventType == \\\"CONFIG\\\"\\n| where tostring(Target.type) contains \\\"POSTURE\\\" or tostring(Target.type) contains \\\"INTEGRATION\\\"\\n| project TimeGenerated, Actor=tostring(Actor.loginName), Action,\\n Target=tostring(Target.name), TargetType=tostring(Target.type), ActionDetails\\n| order by TimeGenerated desc\",\"size\":4,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Recent posture integration create/update/delete events\",\"noDataMessage\":\"No posture-integration audit events in this window.\",\"noDataMessageStyle\":5,\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"TimeGenerated\",\"formatter\":6},{\"columnMatch\":\"Action\",\"formatter\":11}]}},\"name\":\"q-posture-audit\"}]}},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":1,\"content\":{\"json\":\"
Ingest rate per table
\"},\"name\":\"div-ingest-rate-per-tabl-24e5f6\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"union withsource=Table Tailscale_Audit_CL, Tailscale_Devices_CL, Tailscale_Users_CL, Tailscale_Keys_CL, Tailscale_Dns_CL, Tailscale_Settings_CL\\n| where TimeGenerated > ago(24h)\\n| summarize Rows=count() by Table, bin(TimeGenerated, 1h)\\n| order by TimeGenerated asc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"timechart\",\"title\":\"Rows ingested per Tailscale table per hour (last 24h)\",\"noDataMessage\":\"No Tailscale data ingested in the last 24h - check the connector card under Sentinel Data Connectors.\",\"noDataMessageStyle\":5},\"name\":\"q-c6fd0143\"},{\"type\":1,\"content\":{\"json\":\"
Last poll time per table
\"},\"name\":\"div-last-poll-time-per-t-49b051\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"union withsource=Table Tailscale_Audit_CL, Tailscale_Devices_CL, Tailscale_Users_CL, Tailscale_Keys_CL, Tailscale_Dns_CL, Tailscale_Settings_CL\\n| summarize LastRow=max(TimeGenerated), TotalRows=count() by Table\\n| extend MinutesAgo=toint((now() - LastRow) / 1m)\\n| extend Status=case(MinutesAgo < 60, \\\"Fresh\\\", MinutesAgo < 360, \\\"Recent\\\", MinutesAgo < 1440, \\\"Stale\\\", \\\"Very Stale\\\")\\n| project Table, LastRow, MinutesAgo, TotalRows, Status\\n| order by MinutesAgo asc\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Per-table freshness\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"LastRow\",\"formatter\":6},{\"columnMatch\":\"MinutesAgo\",\"formatter\":8,\"formatOptions\":{\"palette\":\"redBright\"}},{\"columnMatch\":\"TotalRows\",\"formatter\":8,\"formatOptions\":{\"palette\":\"blue\"}},{\"columnMatch\":\"Status\",\"formatter\":1}]}},\"name\":\"q-a02e37a8\"},{\"type\":1,\"content\":{\"json\":\"
Log Analytics operational events
\"},\"name\":\"div-log-analytics-operat-630749\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"_LogOperation\\n| where TimeGenerated > ago(24h)\\n| where _ResourceId contains \\\"tailscale\\\" or Detail contains \\\"Tailscale_\\\"\\n| project TimeGenerated, Operation, Level, Detail\\n| order by TimeGenerated desc | take 100\",\"size\":0,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"table\",\"title\":\"Log Analytics operational events touching Tailscale tables\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"TimeGenerated\",\"formatter\":6},{\"columnMatch\":\"Level\",\"formatter\":1}]},\"noDataMessage\":\"No operational issues recorded in the last 24h.\",\"noDataMessageStyle\":1},\"name\":\"q-b492f150\"}]},\"conditionalVisibility\":{\"parameterName\":\"selectedTab\",\"comparison\":\"isEqualTo\",\"value\":\"pipeline\"},\"name\":\"group-pipeline\"},{\"type\":1,\"content\":{\"json\":\"
Tailscale Operations (Premium) (CCF) - Microsoft Sentinel content from the Tailscale (CCF) solution, Premium-tier surface. Tables polled from the Tailscale REST API: audit, devices, users, keys, dns, settings, network flows (/logging/network), posture integrations. Filter every panel via the time range above; the Investigate tab adds Actor and Device pickers for drilldown. The Network Flows and Posture tabs use the 15 promoted columns added in 3.1.0 (SrcUser, SrcTags, DstUser, DstTags, HasVirtualTraffic, HasSubnetTraffic, HasExitTraffic, HasPhysicalTraffic, IsRelayed, etc.). Companion workbook: Tailscale Operations (Standard) for Personal / Starter tailnets.
\"},\"name\":\"footer\"}],\"fallbackResourceIds\":[\"Azure Monitor\"],\"fromTemplateId\":\"sentinel-TailscalePremiumWorkbook\",\"$schema\":\"https://github.com/Microsoft/Application-Insights-Workbooks/blob/master/schema/workbook.json\"}\n", "version": "1.0", "sourceId": "[variables('workspaceResourceId')]", "category": "sentinel" @@ -8608,7 +9508,7 @@ "email": "[variables('_email')]" }, "support": { - "name": "Community", + "name": "Tailscale (CCF)", "tier": "Community", "email": "ccfconnectors.county118@passmail.com", "link": "https://github.com/Azure/Azure-Sentinel/issues" @@ -8680,12 +9580,12 @@ "apiVersion": "2023-04-01-preview", "location": "[parameters('workspace-location')]", "properties": { - "version": "3.0.5", + "version": "3.0.0", "kind": "Solution", "contentSchemaVersion": "3.0.0", "displayName": "Tailscale (CCF)", - "publisherDisplayName": "Community", - "descriptionHtml": "

Note: Please refer to the following before installing the solution:

\n
\n

• There may be known issues pertaining to this Solution, please refer to them before installing.

\n

The Tailscale solution for Microsoft Sentinel ingests Tailscale identity, device, configuration, audit and (Premium) network-flow telemetry via OAuth2-secured APIs. Built on the Codeless Connector Framework (CCF) - no Function App or container required.

\n

Data connectors in this solution (install the one matching your Tailscale plan):

\n
    \n
  • Tailscale Standard (CCF) - Configuration audit, devices, users, keys, webhooks, DNS, settings. Use on Personal (Free), Starter and Premium tailnets.
  • \n
  • Tailscale Premium (CCF) - Everything in Standard plus network flow logs and posture integrations. Use on Premium and Enterprise tailnets for full coverage.
  • \n
\n

Pre-requisites:

\n
    \n
  1. Sign in to Tailscale OAuth settings
  2. \n
  3. Create an OAuth client with the scopes for your tier (see the README in this solution).
  4. \n
  5. Copy the client ID and client secret (secret shown once).
  6. \n
  7. Note your tailnet name (e.g. tailb094d7.ts.net) from the Keys page.
  8. \n
\n

Data Connectors: 2, Workbooks: 2, Analytic Rules: 22, Hunting Queries: 17

\n

Learn more about Microsoft Sentinel | Learn more about Solutions

\n", + "publisherDisplayName": "Tailscale (CCF)", + "descriptionHtml": "

Note: Please refer to the following before installing the solution:

\n

\u2022 Review the solution Release Notes

\n

\u2022 There may be known issues pertaining to this Solution, please refer to them before installing.

\n

The Tailscale solution for Microsoft Sentinel ingests Tailscale identity, device, configuration, audit and (Premium) network-flow telemetry via OAuth2-secured APIs. Built on the Codeless Connector Framework (CCF) - no Function App or container required.

\n

Data connectors in this solution (install the one matching your Tailscale plan):

\n
    \n
  • Tailscale Standard (CCF) - Configuration audit, devices, users, keys, webhooks, DNS, settings. Use on Personal (Free), Starter and Premium tailnets.
  • \n
  • Tailscale Premium (CCF) - Everything in Standard plus network flow logs and posture integrations. Use on Premium and Enterprise tailnets for full coverage.
  • \n
\n

Pre-requisites:

\n
    \n
  1. Sign in to Tailscale OAuth settings
  2. \n
  3. Create an OAuth client with the scopes for your tier (see the README in this solution).
  4. \n
  5. Copy the client ID and client secret (secret shown once).
  6. \n
  7. Note your tailnet name (e.g. tailb094d7.ts.net) from the Keys page.
  8. \n
\n

Data Connectors: 2, Workbooks: 2, Analytic Rules: 24, Hunting Queries: 22

\n

Learn more about Microsoft Sentinel | Learn more about Solutions

\n", "contentKind": "Solution", "contentProductId": "[variables('_solutioncontentProductId')]", "id": "[variables('_solutioncontentProductId')]", @@ -8702,7 +9602,7 @@ "email": "[variables('_email')]" }, "support": { - "name": "Community", + "name": "Tailscale (CCF)", "email": "ccfconnectors.county118@passmail.com", "tier": "Community", "link": "https://github.com/Azure/Azure-Sentinel/issues" @@ -8830,6 +9730,16 @@ "contentId": "[variables('analyticRuleObject22')._analyticRulecontentId22]", "version": "[variables('analyticRuleObject22').analyticRuleVersion22]" }, + { + "kind": "AnalyticsRule", + "contentId": "[variables('analyticRuleObject23')._analyticRulecontentId23]", + "version": "[variables('analyticRuleObject23').analyticRuleVersion23]" + }, + { + "kind": "AnalyticsRule", + "contentId": "[variables('analyticRuleObject24')._analyticRulecontentId24]", + "version": "[variables('analyticRuleObject24').analyticRuleVersion24]" + }, { "kind": "HuntingQuery", "contentId": "[variables('huntingQueryObject1')._huntingQuerycontentId1]", @@ -8915,6 +9825,31 @@ "contentId": "[variables('huntingQueryObject17')._huntingQuerycontentId17]", "version": "[variables('huntingQueryObject17').huntingQueryVersion17]" }, + { + "kind": "HuntingQuery", + "contentId": "[variables('huntingQueryObject18')._huntingQuerycontentId18]", + "version": "[variables('huntingQueryObject18').huntingQueryVersion18]" + }, + { + "kind": "HuntingQuery", + "contentId": "[variables('huntingQueryObject19')._huntingQuerycontentId19]", + "version": "[variables('huntingQueryObject19').huntingQueryVersion19]" + }, + { + "kind": "HuntingQuery", + "contentId": "[variables('huntingQueryObject20')._huntingQuerycontentId20]", + "version": "[variables('huntingQueryObject20').huntingQueryVersion20]" + }, + { + "kind": "HuntingQuery", + "contentId": "[variables('huntingQueryObject21')._huntingQuerycontentId21]", + "version": "[variables('huntingQueryObject21').huntingQueryVersion21]" + }, + { + "kind": "HuntingQuery", + "contentId": "[variables('huntingQueryObject22')._huntingQuerycontentId22]", + "version": "[variables('huntingQueryObject22').huntingQueryVersion22]" + }, { "kind": "Workbook", "contentId": "[variables('_workbookContentId1')]", @@ -8927,8 +9862,8 @@ } ] }, - "firstPublishDate": "2026-05-11", - "lastPublishDate": "2026-05-11", + "firstPublishDate": "2026-05-19", + "lastPublishDate": "2026-05-19", "providers": [ "Community" ], @@ -8944,4 +9879,4 @@ } ], "outputs": {} -} +} \ No newline at end of file diff --git a/Solutions/Tailscale (CCF)/ReleaseNotes.md b/Solutions/Tailscale (CCF)/ReleaseNotes.md index 9d06c4786eb..b62de57a294 100644 --- a/Solutions/Tailscale (CCF)/ReleaseNotes.md +++ b/Solutions/Tailscale (CCF)/ReleaseNotes.md @@ -1,3 +1,3 @@ | **Version** | **Date Modified (DD-MM-YYYY)** | **Change History** | |---|---|---| -| 3.0.0 | 11-05-2026 | Initial Solution Release | +| 3.0.0 | 19-05-2026 | Initial Solution Release | diff --git a/Solutions/Tailscale (CCF)/SolutionMetadata.json b/Solutions/Tailscale (CCF)/SolutionMetadata.json index c818a8a0597..d619d1ac7cf 100644 --- a/Solutions/Tailscale (CCF)/SolutionMetadata.json +++ b/Solutions/Tailscale (CCF)/SolutionMetadata.json @@ -1,8 +1,8 @@ { "publisherId": "noodlemctwoodle", "offerId": "azure-sentinel-solution-tailscale-ccf", - "firstPublishDate": "2026-05-11", - "lastPublishDate": "2026-05-11", + "firstPublishDate": "2026-05-19", + "lastPublishDate": "2026-05-19", "providers": [ "Community" ], @@ -14,7 +14,7 @@ ] }, "support": { - "name": "Community", + "name": "Tailscale (CCF)", "tier": "Community", "email": "ccfconnectors.county118@passmail.com", "link": "https://github.com/Azure/Azure-Sentinel/issues" diff --git a/Solutions/Tailscale (CCF)/Workbooks/TailscalePremiumOperations.json b/Solutions/Tailscale (CCF)/Workbooks/TailscalePremiumOperations.json index be0dc76cd08..7a3e59eadcc 100644 --- a/Solutions/Tailscale (CCF)/Workbooks/TailscalePremiumOperations.json +++ b/Solutions/Tailscale (CCF)/Workbooks/TailscalePremiumOperations.json @@ -7,7 +7,7 @@ "version": "KqlParameterItem/1.0", "parameters": [ { - "id": "5c59168c-014b-4a83-8a8f-73cc60997e6b", + "id": "7b05a598-5120-43f4-bf5d-576c2a7ff28d", "version": "KqlParameterItem/1.0", "name": "TimeRange", "type": 4, @@ -51,7 +51,7 @@ { "type": 1, "content": { - "json": "
Tailscale Premium (CCF)
Comprehensive visibility for Premium and Enterprise tier tailnets. All Standard telemetry plus network flow logs and posture-integration inventory. Scope every panel with the time range below.
" + "json": "
Tailscale Operations (Premium)
Single-pane visibility into your Tailscale tailnet on Personal (Free), Starter and Premium tiers: who, what, when, where, and what changed. Scope every panel with the time range below; the Investigate tab adds Actor and Device pickers for drilldown. Premium-tier panels (network flow logs, posture integrations) live in the separate Tailscale Operations (Premium) workbook.
" }, "name": "header" }, @@ -62,7 +62,7 @@ "style": "tabs", "links": [ { - "id": "c1c44fc1-1b89-45a5-92cb-8e2374d9a855", + "id": "9794e9fd-916b-494d-8e72-af63d2f4c6c7", "cellValue": "selectedTab", "linkTarget": "parameter", "linkLabel": "Overview", @@ -70,75 +70,83 @@ "style": "link" }, { - "id": "d1147d96-d052-47f7-b048-2b471f6f091c", + "id": "71cf49db-33c5-4d4b-920a-2ec0c6a258dc", "cellValue": "selectedTab", "linkTarget": "parameter", - "linkLabel": "Devices", - "subTarget": "devices", + "linkLabel": "Investigate", + "subTarget": "investigate", "style": "link" }, { - "id": "52c56980-5cfe-456c-a499-9cfd8317ad69", + "id": "5c56bb37-2053-47a0-b6fd-9539768c144d", "cellValue": "selectedTab", "linkTarget": "parameter", - "linkLabel": "Users & Identity", - "subTarget": "users", + "linkLabel": "Hunts", + "subTarget": "hunts", "style": "link" }, { - "id": "9eb1d383-09e7-4aed-af3c-0dbe8c73dac6", + "id": "d2004ded-07f8-446a-a720-f0a63d1d9dda", "cellValue": "selectedTab", "linkTarget": "parameter", - "linkLabel": "Credentials", - "subTarget": "credentials", + "linkLabel": "Identity", + "subTarget": "identity", + "style": "link" + }, + { + "id": "f23b3e14-1511-4a29-bf5e-bd65e55dbb40", + "cellValue": "selectedTab", + "linkTarget": "parameter", + "linkLabel": "Devices", + "subTarget": "devices", "style": "link" }, { - "id": "7cd3279a-4b91-430a-a68d-fb1fa2d74d9b", + "id": "724f8352-e21b-45a6-9029-39dc92693c05", "cellValue": "selectedTab", "linkTarget": "parameter", - "linkLabel": "DNS & Tailnet", - "subTarget": "dns", + "linkLabel": "Credentials", + "subTarget": "credentials", "style": "link" }, { - "id": "e5179782-bc17-42b3-9f8c-c2e0fbfd1f30", + "id": "8400ec64-e118-44e0-ae29-84afe94b8e0e", "cellValue": "selectedTab", "linkTarget": "parameter", - "linkLabel": "Webhooks", - "subTarget": "webhooks", + "linkLabel": "Admin Audit", + "subTarget": "audit", "style": "link" }, { - "id": "6cce0298-c5ec-4092-bfc8-b84a6764fe62", + "id": "b7e04861-edfa-4426-b6be-f1481ca569b6", "cellValue": "selectedTab", "linkTarget": "parameter", - "linkLabel": "Network Flows", + "linkLabel": "Network & DNS", "subTarget": "network", "style": "link" }, { - "id": "8d8bbfed-b08e-401b-9f62-8eaa597261d3", + "id": "b8506d3d-e615-4775-8c6f-14d9cc7db943", "cellValue": "selectedTab", "linkTarget": "parameter", - "linkLabel": "Posture", - "subTarget": "posture", + "linkLabel": "Network Flows", + "subTarget": "network-flows", "style": "link" }, { - "id": "81d1910b-f903-4811-b762-cabeb7740c88", + "id": "c98574ec-7a60-4c1a-b4c6-4d24c5bd02ce", "cellValue": "selectedTab", "linkTarget": "parameter", - "linkLabel": "Audit Activity", - "subTarget": "audit", + "linkLabel": "Posture", + "subTarget": "posture", "style": "link" }, { - "id": "f685a8e3-878e-4cd6-b6d3-5340d57eb32b", + "id": "cfbc96e4-8585-4d89-8f65-8344c8cc6eb2", "cellValue": "selectedTab", "linkTarget": "parameter", - "linkLabel": "Security Signals", - "subTarget": "signals", + "linkLabel": "Pipeline Health", + "subTarget": "pipeline", "style": "link" } ] @@ -154,16 +162,16 @@ { "type": 1, "content": { - "json": "
At a glance
" + "json": "
Tailnet at a glance
" }, - "name": "div-at-a-glance-75296e" + "name": "div-tailnet-at-a-glance-04e62b" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "union (Tailscale_Devices_CL | where TimeGenerated {TimeRange} | summarize arg_max(TimeGenerated, *) by DeviceId | summarize V=toreal(count()) | extend Metric=\"Devices\", Order=1),(Tailscale_Users_CL | where TimeGenerated {TimeRange} | summarize arg_max(TimeGenerated, *) by UserId | summarize V=toreal(count()) | extend Metric=\"Users\", Order=2),(Tailscale_Keys_CL | where TimeGenerated {TimeRange} | summarize arg_max(TimeGenerated, *) by KeyId | summarize V=toreal(countif(isnull(Revoked))) | extend Metric=\"Active keys\", Order=3),(Tailscale_Audit_CL | where TimeGenerated {TimeRange} | summarize V=toreal(count()) | extend Metric=\"Audit events\", Order=4),(Tailscale_Audit_CL | where TimeGenerated {TimeRange} | where tostring(Target.property) == \"ACL\" or tostring(Target.property) == \"DNS_SPLIT_DNS\" or tostring(Target.property) == \"DNS_NAMESERVERS\" or tostring(Target.property) == \"MAGICDNS_PREFERENCES\" | summarize V=toreal(count()) | extend Metric=\"DNS / ACL changes\", Order=5)\n| order by Order asc | project Metric, Value=V", - "size": 0, + "query": "let DEV = Tailscale_Devices_CL | summarize arg_max(TimeGenerated, *) by DeviceId;\nlet USR = Tailscale_Users_CL | summarize arg_max(TimeGenerated, *) by UserId;\nlet KEY = Tailscale_Keys_CL | summarize arg_max(TimeGenerated, *) by KeyId;\nunion\n (DEV | summarize V=toreal(count()) | extend Metric=\"Devices\", Order=1),\n (DEV | where Authorized == true | summarize V=toreal(count()) | extend Metric=\"Authorized\", Order=2),\n (DEV | where UpdateAvailable == true | summarize V=toreal(count()) | extend Metric=\"Updates Available\", Order=3),\n (DEV | where SshEnabled == true | summarize V=toreal(count()) | extend Metric=\"SSH-Enabled\", Order=4),\n (USR | summarize V=toreal(count()) | extend Metric=\"Users\", Order=5),\n (USR | where Role =~ \"admin\" or Role =~ \"owner\" or Role =~ \"network-admin\" | summarize V=toreal(count()) | extend Metric=\"Admins\", Order=6),\n (KEY | where isnull(Revoked) and (isnull(Expires) or Expires > now()) | summarize V=toreal(count()) | extend Metric=\"Active Keys\", Order=7),\n (Tailscale_Audit_CL | where TimeGenerated {TimeRange} | summarize V=toreal(count()) | extend Metric=\"Audit Events ({TimeRange:label})\", Order=8)\n,\n (Tailscale_Network_CL | where TimeGenerated {TimeRange} | summarize V=toreal(count()) | extend Metric=\"Flows ({TimeRange:label})\", Order=9),\n (Tailscale_Network_CL | where TimeGenerated {TimeRange} | summarize V=toreal(dcount(SrcNodeName)) | extend Metric=\"Active Talkers\", Order=10),\n (Tailscale_Network_CL | where TimeGenerated {TimeRange} | summarize V=toreal(iff(count()==0, 0.0, 100.0 * countif(IsRelayed) / count())) | extend Metric=\"DERP Relayed %\", Order=11),\n (Tailscale_PostureIntegrations_CL | where TimeGenerated {TimeRange} | summarize arg_max(TimeGenerated, *) by IntegrationId | summarize V=toreal(count()) | extend Metric=\"Posture Integrations\", Order=12)\n| order by Order asc | project Metric, Value=V", + "size": 3, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", "visualization": "tiles", @@ -182,62 +190,107 @@ "showBorder": false } }, - "name": "q-b71f9f4a" + "name": "q-4e9d7cff" }, { "type": 1, "content": { - "json": "
Activity over time
" + "json": "
Audit activity over time
" }, - "name": "div-activity-over-time-bcc7c5" + "name": "div-audit-activity-over--546688" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| summarize EventCount = count() by bin(TimeGenerated, 1h), Action", + "query": "Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| summarize EventCount = count() by bin(TimeGenerated, 1h), Action\n| order by TimeGenerated asc", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "timechart", "title": "Audit events by action", - "visualization": "timechart" + "noDataMessage": "No audit events in the selected window. Widen the time range; remember the Tailscale audit poll runs every ~30 min.", + "noDataMessageStyle": 5 }, - "name": "q-8fc88ccb" + "name": "q-effcd498" }, { "type": 1, "content": { - "json": "
Top actors
" + "json": "
Who's doing what
" }, - "name": "div-top-actors-bd287b" + "name": "div-who's-doing-what-52144e" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| extend ActorLogin = tostring(Actor.loginName)\n| where isnotempty(ActorLogin)\n| summarize EventCount = count() by ActorLogin\n| top 10 by EventCount\n| render barchart", + "query": "Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| extend Actor=tostring(coalesce(Actor.loginName, Actor.displayName, Actor.type))\n| where isnotempty(Actor)\n| summarize Events=count(), DistinctActions=dcount(Action), LastSeen=max(TimeGenerated) by Actor\n| order by Events desc | take 15", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", - "title": "Top 10 actors", - "visualization": "table" + "visualization": "table", + "title": "Top actors (by event count)", + "gridSettings": { + "formatters": [ + { + "columnMatch": "Events", + "formatter": 8, + "formatOptions": { + "palette": "blue" + } + }, + { + "columnMatch": "LastSeen", + "formatter": 6 + } + ] + } }, - "name": "q-93b0d742", + "name": "q-34c77fa9", "customWidth": "50" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| summarize EventCount = count() by TargetType = tostring(Target.type)\n| top 10 by EventCount\n| render piechart", + "query": "Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| extend TargetType=tostring(Target.type)\n| where isnotempty(TargetType)\n| summarize Events=count() by TargetType\n| order by Events desc | take 15", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", - "title": "Activity by target type", - "visualization": "table" + "visualization": "piechart", + "title": "Activity by target type" }, - "name": "q-12fbe2ab", + "name": "q-4b3b709d", "customWidth": "50" + }, + { + "type": 1, + "content": { + "json": "
Recent admin events
" + }, + "name": "div-recent-admin-events-87c9e0" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| extend Actor=tostring(coalesce(Actor.loginName, Actor.displayName, Actor.type))\n| extend TargetType=tostring(Target.type), TargetName=tostring(coalesce(Target.name, Target.id))\n| project TimeGenerated, Action, Actor, TargetType, TargetName, Origin\n| order by TimeGenerated desc | take 30", + "size": 0, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "table", + "title": "Most recent 30 audit events", + "gridSettings": { + "formatters": [ + { + "columnMatch": "TimeGenerated", + "formatter": 6 + } + ] + } + }, + "name": "q-5e7d6306" } ] }, @@ -255,165 +308,157 @@ "groupType": "editable", "items": [ { - "type": 1, - "content": { - "json": "
Device inventory snapshot
" - }, - "name": "div-device-inventory-snapshot-56b7ce" - }, - { - "type": 3, + "type": 9, "content": { - "version": "KqlItem/1.0", - "query": "let base = Tailscale_Devices_CL | summarize arg_max(TimeGenerated, *) by DeviceId;\nunion (base | summarize V=toreal(count()) | extend Metric=\"Total devices\", Order=1),(base | where Authorized == true | summarize V=toreal(count()) | extend Metric=\"Authorized\", Order=2),(base | where IsExternal == true | summarize V=toreal(count()) | extend Metric=\"External (shared)\", Order=3),(base | where KeyExpiryDisabled == true | summarize V=toreal(count()) | extend Metric=\"Key expiry disabled\", Order=4),(base | where UpdateAvailable == true | summarize V=toreal(count()) | extend Metric=\"Update available\", Order=5),(base | where array_length(AdvertisedRoutes) > 0 | summarize V=toreal(count()) | extend Metric=\"Subnet routers\", Order=6),(base | where LastSeen < ago(30d) | summarize V=toreal(count()) | extend Metric=\"Stale (30+ days)\", Order=7)\n| order by Order asc | project Metric, Value=V", - "size": 0, + "version": "KqlParameterItem/1.0", + "parameters": [ + { + "id": "b83a25c1-da18-49a2-a444-6517f13d891c", + "version": "KqlParameterItem/1.0", + "name": "SelectedActor", + "label": "Actor", + "type": 2, + "isRequired": false, + "query": "let opts = Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\n| where isnotempty(ActorLogin)\n| summarize Events=count() by ActorLogin\n| project value=ActorLogin, label=strcat(ActorLogin, \" (\", tostring(Events), \" events)\");\n(print value=\"__ALL__\", label=\"(All actors)\")\n| union opts", + "typeSettings": { + "showDefault": false + }, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "value": "__ALL__" + } + ], + "style": "pills", "queryType": 0, - "resourceType": "microsoft.operationalinsights/workspaces", - "visualization": "tiles", - "tileSettings": { - "titleContent": { - "columnMatch": "Metric", - "formatter": 1 - }, - "leftContent": { - "columnMatch": "Value", - "formatter": 12, - "formatOptions": { - "palette": "auto" - } - }, - "showBorder": false - } + "resourceType": "microsoft.operationalinsights/workspaces" }, - "name": "q-b8b431c0" + "name": "investigate-picker-actor" }, { "type": 1, "content": { - "json": "
Distribution
" + "json": "
Actor activity timeline
" }, - "name": "div-distribution-d18e9a" + "name": "div-actor-activity-timel-5ec305" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "Tailscale_Devices_CL\n| summarize arg_max(TimeGenerated, *) by DeviceId\n| summarize Count = count() by Os\n| order by Count desc", + "query": "Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\n| where \"{SelectedActor}\" == \"__ALL__\" or ActorLogin == \"{SelectedActor}\"\n| summarize Events=count() by bin(TimeGenerated, 1h), Action\n| order by TimeGenerated asc", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", - "title": "Devices by OS", - "visualization": "piechart" + "visualization": "timechart", + "title": "Actions over time -- actor: {SelectedActor:label}", + "noDataMessage": "Select an actor from the Actor dropdown above, or leave on 'All' to see total activity.", + "noDataMessageStyle": 5 }, - "name": "q-acf35833", - "customWidth": "33" + "name": "q-32d40848" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "Tailscale_Devices_CL\n| summarize arg_max(TimeGenerated, *) by DeviceId\n| summarize Count = count() by ClientVersion\n| order by Count desc", + "query": "Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\n| where \"{SelectedActor}\" == \"__ALL__\" or ActorLogin == \"{SelectedActor}\"\n| extend TargetType=tostring(Target.type), TargetName=tostring(coalesce(Target.name, Target.id))\n| project TimeGenerated, ActorLogin, Action, TargetType, TargetName, Origin\n| order by TimeGenerated desc | take 100", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", - "title": "Devices by client version", - "visualization": "barchart" + "visualization": "table", + "title": "Recent events for actor: {SelectedActor:label}", + "gridSettings": { + "formatters": [ + { + "columnMatch": "TimeGenerated", + "formatter": 6 + } + ] + }, + "noDataMessage": "No events for this actor in the selected window.", + "noDataMessageStyle": 5 }, - "name": "q-29ed4eb0", - "customWidth": "33" + "name": "q-a742f6fd" }, { - "type": 3, + "type": 9, "content": { - "version": "KqlItem/1.0", - "query": "Tailscale_Devices_CL\n| summarize arg_max(TimeGenerated, *) by DeviceId\n| mv-expand Tag = Tags to typeof(string)\n| summarize Devices = dcount(DeviceId) by Tag = iff(isempty(Tag), \"(untagged)\", Tag)\n| order by Devices desc", - "size": 0, + "version": "KqlParameterItem/1.0", + "parameters": [ + { + "id": "a9cf7907-f201-4725-a072-a8bd34bef74e", + "version": "KqlParameterItem/1.0", + "name": "SelectedDevice", + "label": "Device", + "type": 2, + "isRequired": false, + "query": "let opts = Tailscale_Devices_CL\n| summarize arg_max(TimeGenerated, *) by DeviceId\n| order by LastSeen desc | take 100\n| project value=DeviceName, label=strcat(coalesce(DeviceName, Hostname), \" (\", User, \")\");\n(print value=\"__ALL__\", label=\"(All devices)\")\n| union opts", + "typeSettings": { + "showDefault": false + }, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "value": "__ALL__" + } + ], + "style": "pills", "queryType": 0, - "resourceType": "microsoft.operationalinsights/workspaces", - "title": "Devices by tag", - "visualization": "piechart" + "resourceType": "microsoft.operationalinsights/workspaces" }, - "name": "q-71ed6d65", - "customWidth": "33" + "name": "investigate-picker-device" }, { "type": 1, "content": { - "json": "
Device inventory table
" + "json": "
Selected device timeline
" }, - "name": "div-device-inventory-table-ba97ce" + "name": "div-selected-device-time-00bead" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "Tailscale_Devices_CL\n| summarize arg_max(TimeGenerated, *) by DeviceId\n| extend DaysSinceSeen = datetime_diff('day', now(), LastSeen)\n| extend DaysToExpiry = iff(KeyExpiryDisabled == true, int(null), datetime_diff('day', Expires, now()))\n| project DeviceName, Hostname, User, Os, ClientVersion, UpdateAvailable, LastSeen, DaysSinceSeen, DaysToExpiry, KeyExpiryDisabled, Tags, AdvertisedRoutes\n| order by LastSeen desc", + "query": "Tailscale_Devices_CL\n| where \"{SelectedDevice}\" == \"__ALL__\" or DeviceName == \"{SelectedDevice}\"\n| summarize arg_max(TimeGenerated, *) by DeviceId\n| extend OnlineNow = ClientConnectivity.endpoints != \"\" or ConnectedToControl == true\n| project DeviceName, Hostname, User, Os, ClientVersion, UpdateAvailable, Authorized, IsExternal, SshEnabled, LastSeen, Expires, KeyExpiryDisabled, OnlineNow, Addresses, Tags, AdvertisedRoutes", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", - "title": "All devices (latest snapshot per device)", "visualization": "table", - "gridSettings": { - "formatters": [ - { - "columnMatch": "DaysSinceSeen", - "formatter": 8, - "formatOptions": { - "palette": "redBright", - "min": 0, - "max": 60 - } - }, - { - "columnMatch": "DaysToExpiry", - "formatter": 8, - "formatOptions": { - "palette": "redBright", - "min": 0, - "max": 30 - } - }, - { - "columnMatch": "UpdateAvailable", - "formatter": 11 - }, - { - "columnMatch": "KeyExpiryDisabled", - "formatter": 11 - } - ] - } - }, - "name": "q-d0f59836" - }, - { - "type": 1, - "content": { - "json": "
Subnet routers and exit nodes
" + "title": "Summary for device: {SelectedDevice:label}", + "noDataMessage": "Select a device from the Device dropdown above. Defaults to 'All'.", + "noDataMessageStyle": 5 }, - "name": "div-subnet-routers-and-exit-nodes-b80917" + "name": "q-11094dc8" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "Tailscale_Devices_CL\n| summarize arg_max(TimeGenerated, *) by DeviceId\n| where array_length(AdvertisedRoutes) > 0 or array_length(EnabledRoutes) > 0\n| project DeviceName, User, Os, AdvertisedRoutes, EnabledRoutes, Tags, LastSeen\n| order by DeviceName asc", + "query": "Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| extend TargetType=tostring(Target.type), TargetName=tostring(Target.name), TargetId=tostring(Target.id)\n| where (\"{SelectedDevice}\" == \"__ALL__\" and TargetType == \"NODE\") or TargetName == \"{SelectedDevice}\"\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\n| project TimeGenerated, Action, ActorLogin, TargetName, TargetId, Origin\n| order by TimeGenerated desc | take 100", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", - "title": "Devices advertising routes (subnet routers / exit nodes)", - "visualization": "table" + "visualization": "table", + "title": "Audit events touching device: {SelectedDevice:label}", + "gridSettings": { + "formatters": [ + { + "columnMatch": "TimeGenerated", + "formatter": 6 + } + ] + }, + "noDataMessage": "No audit events recorded against the selected device in this window. Tailscale tags device events with Target.type=NODE; the audit feed only emits NODE events on create/update/delete, so quiet devices stay quiet here.", + "noDataMessageStyle": 5 }, - "name": "q-374f7fdc" + "name": "q-ead106ce" } ] }, "conditionalVisibility": { "parameterName": "selectedTab", "comparison": "isEqualTo", - "value": "devices" + "value": "investigate" }, - "name": "group-devices" + "name": "group-investigate" }, { "type": 12, @@ -424,235 +469,265 @@ { "type": 1, "content": { - "json": "
Identity overview
" + "json": "
First-seen actors in the last 24h
" }, - "name": "div-identity-overview-2881ca" + "name": "div-first-seen-actors-in-ce38d9" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "let base = Tailscale_Users_CL | summarize arg_max(TimeGenerated, *) by UserId;\nunion (base | summarize V=toreal(count()) | extend Metric=\"Users\", Order=1),(base | where Status =~ \"active\" | summarize V=toreal(count()) | extend Metric=\"Active\", Order=2),(base | where Status =~ \"suspended\" | summarize V=toreal(count()) | extend Metric=\"Suspended\", Order=3),(base | where CurrentlyConnected == true | summarize V=toreal(count()) | extend Metric=\"Currently connected\", Order=4),(base | where DeviceCount == 0 | summarize V=toreal(count()) | extend Metric=\"Zero devices\", Order=5),(base | where Role in (\"owner\",\"admin\",\"network-admin\") | summarize V=toreal(count()) | extend Metric=\"Elevated\", Order=6)\n| order by Order asc | project Metric, Value=V", + "query": "let recent = Tailscale_Audit_CL | where TimeGenerated > ago(24h) | extend A=tostring(coalesce(Actor.loginName, Actor.displayName)) | summarize FirstSeen24h=min(TimeGenerated), Events=count() by A;\nlet historical = Tailscale_Audit_CL | where TimeGenerated between(ago(30d) .. ago(24h)) | extend A=tostring(coalesce(Actor.loginName, Actor.displayName)) | distinct A;\nrecent | join kind=leftanti historical on A | where isnotempty(A) | project FirstSeen24h, Actor=A, Events | order by Events desc", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", - "visualization": "tiles", - "tileSettings": { - "titleContent": { - "columnMatch": "Metric", - "formatter": 1 - }, - "leftContent": { - "columnMatch": "Value", - "formatter": 12, - "formatOptions": { - "palette": "auto" + "visualization": "table", + "title": "Actors who have NEVER appeared before (30d baseline)", + "gridSettings": { + "formatters": [ + { + "columnMatch": "FirstSeen24h", + "formatter": 6 + }, + { + "columnMatch": "Events", + "formatter": 8, + "formatOptions": { + "palette": "orange" + } } - }, - "showBorder": false - } + ] + }, + "noDataMessage": "Every actor seen in the last 24h has appeared at least once in the prior 30d. Healthy state.", + "noDataMessageStyle": 1 }, - "name": "q-fd38a1c2" + "name": "q-9ba7b85e" }, { "type": 1, "content": { - "json": "
Distribution
" - }, - "name": "div-distribution-e6ff99" - }, - { - "type": 3, - "content": { - "version": "KqlItem/1.0", - "query": "Tailscale_Users_CL\n| summarize arg_max(TimeGenerated, *) by UserId\n| summarize Users = count() by Role\n| order by Users desc", - "size": 0, - "queryType": 0, - "resourceType": "microsoft.operationalinsights/workspaces", - "title": "Users by role", - "visualization": "piechart" + "json": "
Off-hours configuration changes
" }, - "name": "q-776b4d9b", - "customWidth": "50" + "name": "div-off-hours-configurat-3c3787" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "Tailscale_Users_CL\n| summarize arg_max(TimeGenerated, *) by UserId\n| summarize Users = count() by Status\n| order by Users desc", + "query": "Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| extend Hour=hourofday(TimeGenerated), DayOfWeek=dayofweek(TimeGenerated)/1d\n| where Hour < 7 or Hour > 19 or DayOfWeek in (0, 6)\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\n| extend TargetType=tostring(Target.type)\n| where Action !in (\"LOGIN\", \"LOGOUT\")\n| project TimeGenerated, ActorLogin, Action, TargetType, Origin\n| order by TimeGenerated desc", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", - "title": "Users by status", - "visualization": "piechart" + "visualization": "table", + "title": "Admin actions outside 07:00-19:00 weekdays", + "gridSettings": { + "formatters": [ + { + "columnMatch": "TimeGenerated", + "formatter": 6 + } + ] + }, + "noDataMessage": "No off-hours admin changes recorded - healthy state for an organisation working business hours.", + "noDataMessageStyle": 1 }, - "name": "q-13d1cabf", - "customWidth": "50" + "name": "q-721ee490" }, { "type": 1, "content": { - "json": "
User inventory table
" + "json": "
Devices with key expiry disabled
" }, - "name": "div-user-inventory-table-ddbb44" + "name": "div-devices-with-key-exp-dfe9c1" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "Tailscale_Users_CL\n| summarize arg_max(TimeGenerated, *) by UserId\n| extend DaysSinceSeen = datetime_diff('day', now(), LastSeen)\n| project LoginName, DisplayName, Role, Status, UserType, DeviceCount, CurrentlyConnected, LastSeen, DaysSinceSeen, Created\n| order by LastSeen desc", + "query": "Tailscale_Devices_CL\n| summarize arg_max(TimeGenerated, *) by DeviceId\n| where KeyExpiryDisabled == true\n| project DeviceName, Hostname, User, Os, ClientVersion, LastSeen, Authorized, Tags\n| order by LastSeen desc", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", - "title": "All users (latest snapshot per user)", - "visualization": "table" + "visualization": "table", + "title": "Devices that will never re-authenticate (high-risk drift)", + "gridSettings": { + "formatters": [ + { + "columnMatch": "LastSeen", + "formatter": 6 + } + ] + }, + "noDataMessage": "No devices have key expiry disabled - good. Disabling key expiry creates devices that never re-auth, drifting from policy.", + "noDataMessageStyle": 1 }, - "name": "q-5b6e7b3a" - } - ] - }, - "conditionalVisibility": { - "parameterName": "selectedTab", - "comparison": "isEqualTo", - "value": "users" - }, - "name": "group-users" - }, - { - "type": 12, - "content": { - "version": "NotebookGroup/1.0", - "groupType": "editable", - "items": [ + "name": "q-8a8e2fb5" + }, { "type": 1, "content": { - "json": "
Credential inventory
" + "json": "
Auth keys with no expiry
" }, - "name": "div-credential-inventory-30455a" + "name": "div-auth-keys-with-no-ex-b11207" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "let base = Tailscale_Keys_CL | summarize arg_max(TimeGenerated, *) by KeyId | where isnull(Revoked);\nunion (base | summarize V=toreal(count()) | extend Metric=\"Active credentials\", Order=1),(base | where KeyType =~ \"auth\" | summarize V=toreal(count()) | extend Metric=\"Auth keys\", Order=2),(base | where KeyType in~ (\"apiAccessToken\",\"api_access_token\",\"apikey\") | summarize V=toreal(count()) | extend Metric=\"API tokens\", Order=3),(base | where KeyType in~ (\"oauthClient\",\"oauth_client\") | summarize V=toreal(count()) | extend Metric=\"OAuth clients\", Order=4),(base | where isnull(Expires) | summarize V=toreal(count()) | extend Metric=\"No expiry\", Order=5),(base | where isnotnull(Expires) and Expires < now()+7d | summarize V=toreal(count()) | extend Metric=\"Expiring within 7 days\", Order=6)\n| order by Order asc | project Metric, Value=V", + "query": "Tailscale_Keys_CL\n| summarize arg_max(TimeGenerated, *) by KeyId\n| where isnull(Revoked) and (isnull(Expires) or ExpirySeconds == 0)\n| project KeyId, Description, UserId, KeyType, Created, Capabilities\n| order by Created desc", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", - "visualization": "tiles", - "tileSettings": { - "titleContent": { - "columnMatch": "Metric", - "formatter": 1 - }, - "leftContent": { - "columnMatch": "Value", - "formatter": 12, - "formatOptions": { - "palette": "auto" + "visualization": "table", + "title": "Active keys that never expire", + "gridSettings": { + "formatters": [ + { + "columnMatch": "Created", + "formatter": 6 } - }, - "showBorder": false - } + ] + }, + "noDataMessage": "No never-expiring auth keys - rotation hygiene is good.", + "noDataMessageStyle": 1 }, - "name": "q-64bd4801" + "name": "q-1372740c" }, { "type": 1, "content": { - "json": "
Credentials by type
" + "json": "
Devices running outdated clients
" }, - "name": "div-credentials-by-type-00cf87" + "name": "div-devices-running-outd-bcd077" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "Tailscale_Keys_CL\n| summarize arg_max(TimeGenerated, *) by KeyId\n| where isnull(Revoked)\n| summarize Count = count() by KeyType", + "query": "Tailscale_Devices_CL\n| summarize arg_max(TimeGenerated, *) by DeviceId\n| where UpdateAvailable == true\n| project DeviceName, Hostname, User, Os, ClientVersion, LastSeen, Tags\n| order by LastSeen desc", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", - "title": "Credentials by type", - "visualization": "piechart" + "visualization": "table", + "title": "Devices flagged update-available by Tailscale", + "gridSettings": { + "formatters": [ + { + "columnMatch": "LastSeen", + "formatter": 6 + } + ] + }, + "noDataMessage": "All devices on current client - nothing to patch.", + "noDataMessageStyle": 1 }, - "name": "q-32c060b4", - "customWidth": "50" + "name": "q-ecebf1f7" + }, + { + "type": 1, + "content": { + "json": "
Dormant devices (LastSeen > 30 days)
" + }, + "name": "div-dormant-devices-(las-761156" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "Tailscale_Keys_CL\n| summarize arg_max(TimeGenerated, *) by KeyId\n| where isnull(Revoked) and isnotnull(Expires)\n| extend DaysToExpiry = datetime_diff('day', Expires, now())\n| extend Bucket = case(DaysToExpiry < 0, '0 expired', DaysToExpiry < 7, '1 within 7d', DaysToExpiry < 30, '2 within 30d', DaysToExpiry < 90, '3 within 90d', '4 over 90d')\n| summarize Keys = count() by Bucket\n| order by Bucket asc", + "query": "Tailscale_Devices_CL\n| summarize arg_max(TimeGenerated, *) by DeviceId\n| where LastSeen < ago(30d)\n| extend DaysIdle = toint((now() - LastSeen) / 1d)\n| project DeviceName, Hostname, User, Os, ClientVersion, LastSeen, DaysIdle, Authorized, Tags\n| order by DaysIdle desc", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", - "title": "Credentials by expiry window", - "visualization": "barchart" + "visualization": "table", + "title": "Devices idle 30+ days - candidates for retirement", + "gridSettings": { + "formatters": [ + { + "columnMatch": "LastSeen", + "formatter": 6 + }, + { + "columnMatch": "DaysIdle", + "formatter": 8, + "formatOptions": { + "palette": "redBright" + } + } + ] + }, + "noDataMessage": "No devices idle 30+ days - inventory is fresh.", + "noDataMessageStyle": 1 }, - "name": "q-43beab6b", - "customWidth": "50" + "name": "q-fb6c1fcc" }, { "type": 1, "content": { - "json": "
Credential inventory table
" + "json": "
Subnet route exposure
" }, - "name": "div-credential-inventory-table-adc7f0" + "name": "div-subnet-route-exposur-289c42" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "Tailscale_Keys_CL\n| summarize arg_max(TimeGenerated, *) by KeyId\n| extend DaysToExpiry = iff(isnull(Expires), int(null), datetime_diff('day', Expires, now()))\n| project KeyId, KeyType, Description, Created, Expires, DaysToExpiry, Revoked, Capabilities\n| order by Created desc", + "query": "Tailscale_Devices_CL\n| summarize arg_max(TimeGenerated, *) by DeviceId\n| where array_length(AdvertisedRoutes) > 0 or array_length(EnabledRoutes) > 0\n| extend Routes = tostring(EnabledRoutes), Advertised = tostring(AdvertisedRoutes)\n| project DeviceName, Hostname, User, Os, Advertised, Routes, LastSeen, SshEnabled, Authorized\n| order by LastSeen desc", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", - "title": "All credentials (latest snapshot per ID)", "visualization": "table", + "title": "Devices advertising or running subnet routes / exit-node duty", "gridSettings": { "formatters": [ { - "columnMatch": "DaysToExpiry", - "formatter": 8, - "formatOptions": { - "palette": "redBright", - "min": 0, - "max": 90 - } + "columnMatch": "LastSeen", + "formatter": 6 } ] - } + }, + "noDataMessage": "No devices advertising subnet routes. Pure mesh topology.", + "noDataMessageStyle": 1 }, - "name": "q-4d271c05" + "name": "q-9533b081" }, { "type": 1, "content": { - "json": "
Audit log: credential lifecycle
" + "json": "
Devices with SSH enabled
" }, - "name": "div-audit-log:-credential-lifecycl-f6dfb7" + "name": "div-devices-with-ssh-ena-285238" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| where tostring(Target.type) in (\"AUTH_KEY\", \"API_KEY\", \"OAUTH_CLIENT\")\n| extend ActorLogin = tostring(Actor.loginName)\n| project TimeGenerated, ActorLogin, Action, TargetType = tostring(Target.type), TargetId = tostring(Target.id), Description = tostring(New.description)\n| order by TimeGenerated desc", + "query": "Tailscale_Devices_CL\n| summarize arg_max(TimeGenerated, *) by DeviceId\n| where SshEnabled == true\n| project DeviceName, Hostname, User, Os, ClientVersion, LastSeen, Authorized, Tags\n| order by LastSeen desc", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", - "title": "Create / update / delete events on credentials", - "visualization": "table" + "visualization": "table", + "title": "Devices with Tailscale SSH enabled (Tailscale-managed remote-shell access)", + "gridSettings": { + "formatters": [ + { + "columnMatch": "LastSeen", + "formatter": 6 + } + ] + }, + "noDataMessage": "No devices have Tailscale SSH enabled - no SSH-via-Tailscale risk surface.", + "noDataMessageStyle": 1 }, - "name": "q-ecc5f778" + "name": "q-735368fb" } ] }, "conditionalVisibility": { "parameterName": "selectedTab", "comparison": "isEqualTo", - "value": "credentials" + "value": "hunts" }, - "name": "group-credentials" + "name": "group-hunts" }, { "type": 12, @@ -663,126 +738,228 @@ { "type": 1, "content": { - "json": "
Current DNS state
" + "json": "
User inventory snapshot
" }, - "name": "div-current-dns-state-400b06" + "name": "div-user-inventory-snaps-9dc96c" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "Tailscale_Dns_CL\n| summarize arg_max(TimeGenerated, *) by ConfigType\n| project ConfigType, Nameservers, MagicDNS, SearchPaths, LastUpdated = TimeGenerated\n| order by ConfigType asc", + "query": "let U = Tailscale_Users_CL | summarize arg_max(TimeGenerated, *) by UserId;\nunion\n (U | summarize V=toreal(count()) | extend Metric=\"Total users\", Order=1),\n (U | where Role in~ (\"admin\",\"owner\",\"network-admin\",\"it-admin\",\"billing-admin\") | summarize V=toreal(count()) | extend Metric=\"Admin-tier users\", Order=2),\n (U | where Status =~ \"active\" | summarize V=toreal(count()) | extend Metric=\"Active\", Order=3),\n (U | where CurrentlyConnected == true | summarize V=toreal(count()) | extend Metric=\"Connected now\", Order=4),\n (U | where Status =~ \"idle\" or LastSeen < ago(30d) | summarize V=toreal(count()) | extend Metric=\"Idle / dormant\", Order=5),\n (U | where UserType =~ \"shared\" | summarize V=toreal(count()) | extend Metric=\"Shared (external)\", Order=6)\n| order by Order asc | project Metric, Value=V", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", - "title": "DNS configuration snapshot (latest per config type)", - "visualization": "table" - }, - "name": "q-12240394" - }, - { - "type": 1, - "content": { - "json": "
Tailnet settings snapshot
" - }, - "name": "div-tailnet-settings-snapshot-b777cf" + "visualization": "tiles", + "tileSettings": { + "titleContent": { + "columnMatch": "Metric", + "formatter": 1 + }, + "leftContent": { + "columnMatch": "Value", + "formatter": 12, + "formatOptions": { + "palette": "auto" + } + }, + "showBorder": false + } + }, + "name": "q-5689d9e8" + }, + { + "type": 1, + "content": { + "json": "
Distribution
" + }, + "name": "div-distribution-de67ec" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_Users_CL\n| summarize arg_max(TimeGenerated, *) by UserId\n| summarize Count=count() by Role\n| order by Count desc", + "size": 0, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "piechart", + "title": "Users by role" + }, + "name": "q-0c47912a", + "customWidth": "33" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "Tailscale_Settings_CL\n| top 1 by TimeGenerated\n| project\n ['Snapshot at'] = TimeGenerated,\n ['Device approval required'] = iff(DevicesApprovalOn == true, 'On', 'Off'),\n ['Device auto-updates'] = iff(DevicesAutoUpdatesOn == true, 'On', 'Off'),\n ['Device key duration (days)'] = DevicesKeyDurationDays,\n ['User approval required'] = iff(UsersApprovalOn == true, 'On', 'Off'),\n ['Users allowed to join external tailnets'] = UsersRoleAllowedToJoinExternalTailnets,\n ['Regional routing'] = iff(RegionalRoutingOn == true, 'On', 'Off'),\n ['Network flow logging'] = iff(NetworkFlowLoggingOn == true, 'On', 'Off'),\n ['Posture identity collection'] = iff(PostureIdentityCollectionOn == true, 'On', 'Off')", + "query": "Tailscale_Users_CL\n| summarize arg_max(TimeGenerated, *) by UserId\n| summarize Count=count() by Status\n| order by Count desc", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", - "title": "Current tailnet settings", - "visualization": "table" + "visualization": "piechart", + "title": "Users by status" }, - "name": "q-69053c5e" + "name": "q-e8c20a69", + "customWidth": "33" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_Users_CL\n| summarize arg_max(TimeGenerated, *) by UserId\n| summarize Count=count() by UserType\n| order by Count desc", + "size": 0, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "piechart", + "title": "Users by type (member / shared)" + }, + "name": "q-2c51776d", + "customWidth": "33" }, { "type": 1, "content": { - "json": "
DNS / ACL / Settings change history
" + "json": "
Activity heatmap
" }, - "name": "div-dns-/-acl-/-settings-change-hi-3efe2e" + "name": "div-activity-heatmap-ca6a21" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| where Action == \"UPDATE\" and tostring(Target.type) == \"TAILNET\"\n| extend Property = tostring(Target.property), ActorLogin = tostring(Actor.loginName)\n| where Property in (\"ACL\", \"DNS_NAMESERVERS\", \"DNS_SPLIT_DNS\", \"DNS_SEARCHPATHS\", \"MAGICDNS_PREFERENCES\", \"SETTINGS\", \"TAILNET_SETTINGS\")\n| project TimeGenerated, ActorLogin, Property, Old, New, Origin\n| order by TimeGenerated desc", + "query": "Tailscale_Users_CL\n| summarize arg_max(TimeGenerated, *) by UserId\n| extend DaysSinceLogin = toint((now() - LastSeen) / 1d)\n| extend Bucket = case(\n DaysSinceLogin < 1, \"Today\",\n DaysSinceLogin < 7, \"This week\",\n DaysSinceLogin < 30, \"This month\",\n DaysSinceLogin < 90, \"Past quarter\",\n \"90+ days\")\n| summarize Users=count() by Bucket\n| order by case(Bucket==\"Today\",1, Bucket==\"This week\",2, Bucket==\"This month\",3, Bucket==\"Past quarter\",4, 5) asc", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", - "title": "Tailnet configuration changes", - "visualization": "table" + "visualization": "barchart", + "title": "Users by recency of last login" }, - "name": "q-bf4dcbaf" - } - ] - }, - "conditionalVisibility": { - "parameterName": "selectedTab", - "comparison": "isEqualTo", - "value": "dns" - }, - "name": "group-dns" - }, - { - "type": 12, - "content": { - "version": "NotebookGroup/1.0", - "groupType": "editable", - "items": [ + "name": "q-350e118d" + }, + { + "type": 1, + "content": { + "json": "
Full user list
" + }, + "name": "div-full-user-list-136aac" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_Users_CL\n| summarize arg_max(TimeGenerated, *) by UserId\n| project DisplayName, LoginName, Role, Status, UserType, DeviceCount, CurrentlyConnected, Created, LastSeen\n| order by LastSeen desc", + "size": 0, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "table", + "title": "All users (latest snapshot per user ID)", + "gridSettings": { + "formatters": [ + { + "columnMatch": "Created", + "formatter": 6 + }, + { + "columnMatch": "LastSeen", + "formatter": 6 + }, + { + "columnMatch": "Role", + "formatter": 1 + }, + { + "columnMatch": "Status", + "formatter": 1 + }, + { + "columnMatch": "DeviceCount", + "formatter": 8, + "formatOptions": { + "palette": "blue" + } + } + ] + } + }, + "name": "q-cee23d3c" + }, { "type": 1, "content": { - "json": "
Webhook inventory
" + "json": "
Orphaned users (active but no devices)
" }, - "name": "div-webhook-inventory-ac2dd4" + "name": "div-orphaned-users-(acti-56d6f8" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "Tailscale_Webhooks_CL\n| summarize arg_max(TimeGenerated, *) by EndpointId\n| project EndpointId, EndpointUrl, ProviderType, CreatorLoginName, Created, LastModified, Subscriptions", + "query": "Tailscale_Users_CL\n| summarize arg_max(TimeGenerated, *) by UserId\n| where Status =~ \"active\" and DeviceCount == 0\n| project DisplayName, LoginName, Role, UserType, Created, LastSeen\n| order by Created desc", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", - "title": "All webhooks (latest snapshot per endpoint)", - "visualization": "table" + "visualization": "table", + "title": "Active accounts with zero devices - candidates for offboarding review", + "gridSettings": { + "formatters": [ + { + "columnMatch": "Created", + "formatter": 6 + }, + { + "columnMatch": "LastSeen", + "formatter": 6 + } + ] + }, + "noDataMessage": "Every active account has at least one device - good hygiene.", + "noDataMessageStyle": 1 }, - "name": "q-4f947b64" + "name": "q-83fcd942" }, { "type": 1, "content": { - "json": "
Webhook change history (from audit log)
" + "json": "
Role escalation history
" }, - "name": "div-webhook-change-history-(from-a-b5a420" + "name": "div-role-escalation-hist-bd8df4" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| where tostring(Target.type) == \"WEBHOOK\" or tostring(Target.property) =~ \"WEBHOOK\"\n| extend ActorLogin = tostring(Actor.loginName)\n| project TimeGenerated, ActorLogin, Action, Target, Old, New, Origin\n| order by TimeGenerated desc", + "query": "Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| where Action == \"USER_ROLE_UPDATE\" or Action == \"USER_ROLES_ASSIGNED\" or Action contains \"ROLE\"\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\n| extend TargetName=tostring(coalesce(Target.name, Target.id))\n| extend FromRole=tostring(Old.role), ToRole=tostring(New.role)\n| project TimeGenerated, ActorLogin, Action, TargetName, FromRole, ToRole, Origin\n| order by TimeGenerated desc", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", - "title": "Webhook create / update / delete events", - "visualization": "table" + "visualization": "table", + "title": "Recent role changes", + "gridSettings": { + "formatters": [ + { + "columnMatch": "TimeGenerated", + "formatter": 6 + }, + { + "columnMatch": "ToRole", + "formatter": 1 + } + ] + }, + "noDataMessage": "No role changes in this window.", + "noDataMessageStyle": 1 }, - "name": "q-cbd16912" + "name": "q-f6c8358a" } ] }, "conditionalVisibility": { "parameterName": "selectedTab", "comparison": "isEqualTo", - "value": "webhooks" + "value": "identity" }, - "name": "group-webhooks" + "name": "group-identity" }, { "type": 12, @@ -793,15 +970,15 @@ { "type": 1, "content": { - "json": "
Network flow summary
" + "json": "
Device fleet snapshot
" }, - "name": "div-network-flow-summary-4f9133" + "name": "div-device-fleet-snapsho-939675" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "let base = Tailscale_Network_CL | where TimeGenerated {TimeRange};\nunion (base | summarize V=toreal(count()) | extend Metric=\"Messages\", Order=1),(base | summarize V=toreal(dcount(NodeId)) | extend Metric=\"Unique nodes\", Order=2),(base | mv-expand t = VirtualTraffic | summarize V=toreal(sum(tolong(t.txBytes) + tolong(t.rxBytes))) | extend Metric=\"Bytes total (B)\", Order=3),(base | mv-expand t = ExitTraffic | summarize V=toreal(sum(tolong(t.txBytes) + tolong(t.rxBytes))) | extend Metric=\"Exit egress (B)\", Order=4),(base | mv-expand t = SubnetTraffic | summarize V=toreal(sum(tolong(t.txBytes) + tolong(t.rxBytes))) | extend Metric=\"Subnet (B)\", Order=5)\n| order by Order asc | project Metric, Value=V", + "query": "let D = Tailscale_Devices_CL | summarize arg_max(TimeGenerated, *) by DeviceId;\nunion\n (D | summarize V=toreal(count()) | extend Metric=\"Total devices\", Order=1),\n (D | where Authorized == true | summarize V=toreal(count()) | extend Metric=\"Authorized\", Order=2),\n (D | where IsExternal == true | summarize V=toreal(count()) | extend Metric=\"External (shared)\", Order=3),\n (D | where UpdateAvailable == true | summarize V=toreal(count()) | extend Metric=\"Updates available\", Order=4),\n (D | where SshEnabled == true | summarize V=toreal(count()) | extend Metric=\"SSH-enabled\", Order=5),\n (D | where KeyExpiryDisabled == true | summarize V=toreal(count()) | extend Metric=\"No key expiry\", Order=6),\n (D | where array_length(AdvertisedRoutes) > 0 | summarize V=toreal(count()) | extend Metric=\"Subnet/exit-node\", Order=7),\n (D | where LastSeen < ago(30d) | summarize V=toreal(count()) | extend Metric=\"Stale (30+ days)\", Order=8)\n| order by Order asc | project Metric, Value=V", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", @@ -821,96 +998,165 @@ "showBorder": false } }, - "name": "q-6aeb3506" + "name": "q-366964fc" }, { "type": 1, "content": { - "json": "
Top talkers
" + "json": "
Distribution
" + }, + "name": "div-distribution-396d03" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_Devices_CL\n| summarize arg_max(TimeGenerated, *) by DeviceId\n| summarize Count=count() by Os\n| order by Count desc", + "size": 0, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "piechart", + "title": "Devices by OS" + }, + "name": "q-0c8f5988", + "customWidth": "33" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_Devices_CL\n| summarize arg_max(TimeGenerated, *) by DeviceId\n| summarize Count=count() by ClientVersion\n| order by Count desc | take 10", + "size": 0, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "barchart", + "title": "Top 10 client versions" }, - "name": "div-top-talkers-a1283e" + "name": "q-af1c45cd", + "customWidth": "33" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "Tailscale_Network_CL\n| where TimeGenerated {TimeRange}\n| mv-expand t = VirtualTraffic\n| extend Src = tostring(t.src), Dst = tostring(t.dst), Bytes = tolong(t.txBytes) + tolong(t.rxBytes)\n| summarize TotalBytes = sum(Bytes) by Src, Dst\n| top 25 by TotalBytes", + "query": "Tailscale_Devices_CL\n| summarize arg_max(TimeGenerated, *) by DeviceId\n| mv-expand Tag = Tags to typeof(string)\n| summarize Devices=dcount(DeviceId) by Tag=iff(isempty(Tag), \"(untagged)\", Tag)\n| order by Devices desc", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", - "title": "Top 25 src->dst pairs by bytes", - "visualization": "barchart" + "visualization": "piechart", + "title": "Devices by tag" }, - "name": "q-994f3cfe" + "name": "q-c3a0ef5b", + "customWidth": "33" }, { "type": 1, "content": { - "json": "
Exit-node usage
" + "json": "
Devices needing attention
" }, - "name": "div-exit-node-usage-73c3f0" + "name": "div-devices-needing-atte-f04a47" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "Tailscale_Network_CL\n| where TimeGenerated {TimeRange}\n| where array_length(ExitTraffic) > 0\n| mv-expand t = ExitTraffic\n| extend Bytes = tolong(t.txBytes) + tolong(t.rxBytes)\n| summarize TotalBytes = sum(Bytes), Flows = count() by SrcNodeName = tostring(SrcNode.name), ExitDst = tostring(t.dst)\n| top 25 by TotalBytes", + "query": "Tailscale_Devices_CL\n| summarize arg_max(TimeGenerated, *) by DeviceId\n| where UpdateAvailable == true or KeyExpiryDisabled == true or LastSeen < ago(30d) or Authorized == false\n| extend Issues = strcat_array(pack_array(\n iff(UpdateAvailable == true, \"needs-update\", \"\"),\n iff(KeyExpiryDisabled == true, \"key-never-expires\", \"\"),\n iff(LastSeen < ago(30d), \"stale\", \"\"),\n iff(Authorized == false, \"unauthorized\", \"\")), \",\")\n| extend Issues = trim(\",\", trim_start(\",\", trim_end(\",\", replace_string(Issues, \",,\", \",\"))))\n| project DeviceName, Hostname, User, Os, ClientVersion, LastSeen, Issues\n| order by LastSeen desc", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", - "title": "Exit-node egress", - "visualization": "table" + "visualization": "table", + "title": "Devices flagged with one or more issues", + "gridSettings": { + "formatters": [ + { + "columnMatch": "LastSeen", + "formatter": 6 + }, + { + "columnMatch": "Issues", + "formatter": 1 + } + ] + }, + "noDataMessage": "No devices need attention - all updated, fresh, authorized, and key-rotating.", + "noDataMessageStyle": 1 }, - "name": "q-0a4f4625" + "name": "q-dc5db84c" }, { "type": 1, "content": { - "json": "
Subnet router throughput
" + "json": "
Full device inventory
" }, - "name": "div-subnet-router-throughput-590dd2" + "name": "div-full-device-inventor-b70642" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "Tailscale_Network_CL\n| where TimeGenerated {TimeRange}\n| where array_length(SubnetTraffic) > 0\n| mv-expand t = SubnetTraffic\n| extend Bytes = tolong(t.txBytes) + tolong(t.rxBytes)\n| summarize TotalBytes = sum(Bytes), Flows = count() by SrcNodeName = tostring(SrcNode.name)\n| top 15 by TotalBytes", + "query": "Tailscale_Devices_CL\n| summarize arg_max(TimeGenerated, *) by DeviceId\n| project DeviceName, Hostname, User, Os, ClientVersion, UpdateAvailable, Authorized, IsExternal, SshEnabled, LastSeen, KeyExpiryDisabled, Tags, AdvertisedRoutes\n| order by LastSeen desc", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", - "title": "Subnet routers by throughput", - "visualization": "table" + "visualization": "table", + "title": "All devices (latest snapshot per device ID)", + "gridSettings": { + "formatters": [ + { + "columnMatch": "LastSeen", + "formatter": 6 + }, + { + "columnMatch": "Os", + "formatter": 1 + }, + { + "columnMatch": "ClientVersion", + "formatter": 1 + } + ] + } }, - "name": "q-b912a9e0" + "name": "q-7f7e7a9a" }, { "type": 1, "content": { - "json": "
Bytes over time
" + "json": "
Subnet routers / exit nodes
" }, - "name": "div-bytes-over-time-e5bc09" + "name": "div-subnet-routers-/-exi-b082d6" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "Tailscale_Network_CL\n| where TimeGenerated {TimeRange}\n| mv-expand t = VirtualTraffic\n| extend Bytes = tolong(t.txBytes) + tolong(t.rxBytes)\n| summarize TotalBytes = sum(Bytes) by bin(TimeGenerated, 1h)\n| render timechart", + "query": "Tailscale_Devices_CL\n| summarize arg_max(TimeGenerated, *) by DeviceId\n| where array_length(AdvertisedRoutes) > 0\n| extend AdvertisedSummary = tostring(AdvertisedRoutes), EnabledSummary = tostring(EnabledRoutes)\n| project DeviceName, Hostname, User, Os, AdvertisedSummary, EnabledSummary, LastSeen, Authorized\n| order by LastSeen desc", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", - "title": "Virtual traffic bytes per hour", - "visualization": "table" + "visualization": "table", + "title": "Devices advertising subnet routes or exit-node capability", + "gridSettings": { + "formatters": [ + { + "columnMatch": "LastSeen", + "formatter": 6 + } + ] + }, + "noDataMessage": "No subnet routers in this tailnet - pure mesh topology.", + "noDataMessageStyle": 1 }, - "name": "q-505f4fda" + "name": "q-5004df25" } ] }, "conditionalVisibility": { "parameterName": "selectedTab", "comparison": "isEqualTo", - "value": "network" + "value": "devices" }, - "name": "group-network" + "name": "group-devices" }, { "type": 12, @@ -921,86 +1167,149 @@ { "type": 1, "content": { - "json": "
Posture inventory
" + "json": "
Credentials snapshot
" }, - "name": "div-posture-inventory-09f2f0" + "name": "div-credentials-snapshot-fd464a" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "Tailscale_PostureIntegrations_CL\n| summarize arg_max(TimeGenerated, *) by IntegrationId\n| project Provider, IntegrationId, ClientId, TenantId_Provider, Status, ConfigOverwrites\n| order by Provider asc", + "query": "let K = Tailscale_Keys_CL | summarize arg_max(TimeGenerated, *) by KeyId;\nunion\n (K | summarize V=toreal(count()) | extend Metric=\"Total keys\", Order=1),\n (K | where isnull(Revoked) and (isnull(Expires) or Expires > now()) | summarize V=toreal(count()) | extend Metric=\"Active\", Order=2),\n (K | where isnotnull(Revoked) | summarize V=toreal(count()) | extend Metric=\"Revoked\", Order=3),\n (K | where Expires < now() and isnull(Revoked) | summarize V=toreal(count()) | extend Metric=\"Expired\", Order=4),\n (K | where isnull(Revoked) and Expires between(now() .. ago(-7d)) | summarize V=toreal(count()) | extend Metric=\"Expiring in 7d\", Order=5),\n (K | where isnull(Revoked) and (isnull(Expires) or ExpirySeconds==0) | summarize V=toreal(count()) | extend Metric=\"Never expire\", Order=6),\n (K | where KeyType =~ \"auth\" | summarize V=toreal(count()) | extend Metric=\"Auth keys\", Order=7),\n (K | where KeyType =~ \"api\" or KeyType contains \"oauth\" | summarize V=toreal(count()) | extend Metric=\"API / OAuth\", Order=8)\n| order by Order asc | project Metric, Value=V", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", - "title": "Current posture integrations", - "visualization": "table" + "visualization": "tiles", + "tileSettings": { + "titleContent": { + "columnMatch": "Metric", + "formatter": 1 + }, + "leftContent": { + "columnMatch": "Value", + "formatter": 12, + "formatOptions": { + "palette": "auto" + } + }, + "showBorder": false + } }, - "name": "q-46b6e6de" + "name": "q-79398bc0" }, { "type": 1, "content": { - "json": "
Integrations by provider
" + "json": "
Distribution
" }, - "name": "div-integrations-by-provider-539100" + "name": "div-distribution-15f665" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "Tailscale_PostureIntegrations_CL\n| summarize arg_max(TimeGenerated, *) by IntegrationId\n| summarize Count = count() by Provider\n| order by Count desc", + "query": "Tailscale_Keys_CL\n| summarize arg_max(TimeGenerated, *) by KeyId\n| summarize Count=count() by KeyType\n| order by Count desc", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", - "title": "By provider", - "visualization": "piechart" + "visualization": "piechart", + "title": "Keys by type" }, - "name": "q-65328ab4", + "name": "q-23e6618b", "customWidth": "50" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "Tailscale_Settings_CL\n| top 1 by TimeGenerated\n| project PostureIdentityCollectionOn", + "query": "Tailscale_Keys_CL\n| summarize arg_max(TimeGenerated, *) by KeyId\n| where isnull(Revoked)\n| extend Bucket = case(\n isnull(Expires) or ExpirySeconds == 0, \"Never\",\n Expires < now(), \"Already expired\",\n Expires < ago(-1d), \"<24h\",\n Expires < ago(-7d), \"1-7d\",\n Expires < ago(-30d), \"8-30d\",\n Expires < ago(-90d), \"31-90d\",\n \"90+d\")\n| summarize Keys=count() by Bucket\n| order by case(Bucket==\"Already expired\",1, Bucket==\"<24h\",2, Bucket==\"1-7d\",3, Bucket==\"8-30d\",4, Bucket==\"31-90d\",5, Bucket==\"90+d\",6, Bucket==\"Never\",7, 8) asc", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", - "title": "Posture identity collection", - "visualization": "table" + "visualization": "barchart", + "title": "Active key expiry distribution" }, - "name": "q-c5867d42", + "name": "q-7484d1a0", "customWidth": "50" }, { "type": 1, "content": { - "json": "
Posture integration change history
" + "json": "
Active credential register
" + }, + "name": "div-active-credential-re-c27539" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_Keys_CL\n| summarize arg_max(TimeGenerated, *) by KeyId\n| where isnull(Revoked)\n| extend ExpiryStatus = case(\n isnull(Expires) or ExpirySeconds == 0, \"Never expires\",\n Expires < now(), \"Expired\",\n Expires < ago(-7d), \"Expires in 7d\",\n Expires < ago(-30d), \"Expires in 30d\",\n \"OK\")\n| project KeyId, KeyType, Description, UserId, Created, Expires, ExpiryStatus, Capabilities\n| order by Created desc", + "size": 0, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "table", + "title": "All active credentials with computed expiry status", + "gridSettings": { + "formatters": [ + { + "columnMatch": "Created", + "formatter": 6 + }, + { + "columnMatch": "Expires", + "formatter": 6 + }, + { + "columnMatch": "ExpiryStatus", + "formatter": 1 + }, + { + "columnMatch": "KeyType", + "formatter": 1 + } + ] + } + }, + "name": "q-4b27a750" + }, + { + "type": 1, + "content": { + "json": "
Credential CRUD events
" }, - "name": "div-posture-integration-change-his-dce848" + "name": "div-credential-crud-even-e455b0" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| where Action in (\"CREATE\", \"UPDATE\", \"DELETE\")\n| where tostring(Target.type) startswith \"POSTURE\" or tostring(Target.type) == \"POSTURE_INTEGRATION\"\n| extend ActorLogin = tostring(Actor.loginName)\n| project TimeGenerated, ActorLogin, Action, Provider = tostring(Target.name), Old, New\n| order by TimeGenerated desc", + "query": "Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| where Action contains \"API_KEY\" or Action contains \"AUTH_KEY\" or Action contains \"OAUTH\" or Action contains \"KEY_CREATE\" or Action contains \"KEY_REVOKE\"\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\n| extend TargetId=tostring(Target.id), TargetType=tostring(Target.type)\n| project TimeGenerated, Action, ActorLogin, TargetType, TargetId, Origin\n| order by TimeGenerated desc", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", - "title": "Audit log: posture-integration changes", - "visualization": "table" + "visualization": "table", + "title": "Recent credential create / revoke / rotate events", + "gridSettings": { + "formatters": [ + { + "columnMatch": "TimeGenerated", + "formatter": 6 + } + ] + }, + "noDataMessage": "No credential CRUD activity in this window.", + "noDataMessageStyle": 1 }, - "name": "q-6b24da73" + "name": "q-777693bd" } ] }, "conditionalVisibility": { "parameterName": "selectedTab", "comparison": "isEqualTo", - "value": "posture" + "value": "credentials" }, - "name": "group-posture" + "name": "group-credentials" }, { "type": 12, @@ -1013,60 +1322,109 @@ "content": { "json": "
Audit volume
" }, - "name": "div-audit-volume-73f015" + "name": "div-audit-volume-de29a2" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| summarize EventCount = count() by bin(TimeGenerated, 1h)", + "query": "Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| summarize Events=count() by bin(TimeGenerated, 1h)\n| order by TimeGenerated asc", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "timechart", "title": "Audit events per hour", - "visualization": "timechart" + "noDataMessage": "No audit events in this window.", + "noDataMessageStyle": 5 + }, + "name": "q-f5eee265" + }, + { + "type": 1, + "content": { + "json": "
Action heatmap by hour of day
" + }, + "name": "div-action-heatmap-by-ho-c8bd5e" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| extend Hour=hourofday(TimeGenerated)\n| summarize Events=count() by Hour, Action\n| order by Hour asc, Events desc", + "size": 0, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "categoricalbar", + "title": "When are admin actions happening?" }, - "name": "q-b71cf8c3" + "name": "q-c6f45d95" }, { "type": 1, "content": { - "json": "
Actor and action distribution
" + "json": "
Actor / Action heatmap
" }, - "name": "div-actor-and-action-distribution-2156c2" + "name": "div-actor-/-action-heatm-d820ba" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| extend ActorLogin = tostring(Actor.loginName), TargetType = tostring(Target.type)\n| summarize EventCount = count() by ActorLogin, Action, TargetType\n| order by EventCount desc", + "query": "Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\n| where isnotempty(ActorLogin)\n| summarize Events=count() by ActorLogin, Action\n| order by Events desc | take 100", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", - "title": "Actor / Action / Target heatmap", - "visualization": "table" + "visualization": "table", + "title": "Who is firing which action", + "gridSettings": { + "formatters": [ + { + "columnMatch": "Events", + "formatter": 4, + "formatOptions": { + "palette": "blue" + } + } + ] + }, + "noDataMessage": "No audit events in this window.", + "noDataMessageStyle": 5 }, - "name": "q-2eddf283" + "name": "q-06c13b3b" }, { "type": 1, "content": { "json": "
Recent activity
" }, - "name": "div-recent-activity-669c80" + "name": "div-recent-activity-63b210" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| extend ActorLogin = tostring(Actor.loginName), TargetType = tostring(Target.type), Property = tostring(Target.property)\n| project TimeGenerated, ActorLogin, Action, TargetType, Property, TargetId = tostring(Target.id), Origin\n| order by TimeGenerated desc\n| take 100", + "query": "Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\n| extend TargetType=tostring(Target.type), TargetName=tostring(coalesce(Target.name, Target.id))\n| project TimeGenerated, Action, ActorLogin, TargetType, TargetName, Origin, EventGroupID\n| order by TimeGenerated desc | take 100", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", - "title": "Recent 100 audit events", - "visualization": "table" + "visualization": "table", + "title": "Last 100 audit events", + "gridSettings": { + "formatters": [ + { + "columnMatch": "TimeGenerated", + "formatter": 6 + }, + { + "columnMatch": "Action", + "formatter": 1 + } + ] + }, + "noDataMessage": "No audit events in this window.", + "noDataMessageStyle": 5 }, - "name": "q-49c1df44" + "name": "q-07a25a40" } ] }, @@ -1086,76 +1444,816 @@ { "type": 1, "content": { - "json": "
Tailscale-related alerts
" + "json": "
DNS configuration (current state)
" }, - "name": "div-tailscale-related-alerts-871583" + "name": "div-dns-configuration-(c-5f2e1e" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "SecurityAlert\n| where TimeGenerated {TimeRange}\n| where AlertName startswith \"Tailscale\"\n| summarize Count = count() by AlertName, AlertSeverity\n| order by Count desc", + "query": "Tailscale_Dns_CL\n| summarize arg_max(TimeGenerated, *) by ConfigType\n| project ConfigType, Nameservers, MagicDNS, SearchPaths, LastSnapshot=TimeGenerated\n| order by ConfigType asc", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", - "title": "Alerts by rule", - "visualization": "table" + "visualization": "table", + "title": "MagicDNS, nameservers, search paths", + "gridSettings": { + "formatters": [ + { + "columnMatch": "LastSnapshot", + "formatter": 6 + }, + { + "columnMatch": "ConfigType", + "formatter": 1 + } + ] + }, + "noDataMessage": "No DNS snapshots in the workspace yet. DNS polls runs at ~30 min cadence.", + "noDataMessageStyle": 5 }, - "name": "q-40e4f77b", - "customWidth": "50" + "name": "q-d6a6a358" + }, + { + "type": 1, + "content": { + "json": "
Tailnet settings (current)
" + }, + "name": "div-tailnet-settings-(cu-e03b16" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "SecurityAlert\n| where TimeGenerated {TimeRange}\n| where AlertName startswith \"Tailscale\"\n| summarize Count = count() by AlertSeverity\n| render piechart", + "query": "Tailscale_Settings_CL\n| summarize arg_max(TimeGenerated, *) by TenantId\n| project DevicesApprovalOn, DevicesAutoUpdatesOn, DevicesKeyDurationDays, UsersApprovalOn, NetworkFlowLoggingOn, RegionalRoutingOn, PostureIdentityCollectionOn, UsersRoleAllowedToJoinExternalTailnets, LastSnapshot=TimeGenerated", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", - "title": "Severity distribution", - "visualization": "table" + "visualization": "table", + "title": "Tailnet policy gates", + "noDataMessage": "No settings snapshot yet.", + "noDataMessageStyle": 5 }, - "name": "q-5fed33a4", - "customWidth": "50" + "name": "q-0622cfc3" }, { "type": 1, "content": { - "json": "
Recent Tailscale alerts
" + "json": "
DNS change history
" }, - "name": "div-recent-tailscale-alerts-8e085b" + "name": "div-dns-change-history-9dd376" }, { "type": 3, "content": { "version": "KqlItem/1.0", - "query": "SecurityAlert\n| where TimeGenerated {TimeRange}\n| where AlertName startswith \"Tailscale\"\n| project TimeGenerated, AlertName, AlertSeverity, Description, Entities\n| order by TimeGenerated desc", + "query": "Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| extend TargetProperty=tostring(Target.property)\n| where Action contains \"DNS\" or TargetProperty has_any (\"DNS_NAMESERVERS\", \"DNS_SPLIT_DNS\", \"MAGICDNS\", \"DNS_SEARCH_PATHS\")\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\n| project TimeGenerated, ActorLogin, Action, TargetProperty, Origin\n| order by TimeGenerated desc", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", - "title": "Recent Tailscale alerts", - "visualization": "table" + "visualization": "table", + "title": "Recent DNS-related admin changes", + "gridSettings": { + "formatters": [ + { + "columnMatch": "TimeGenerated", + "formatter": 6 + } + ] + }, + "noDataMessage": "No DNS changes in this window.", + "noDataMessageStyle": 1 + }, + "name": "q-a132ff6a" + }, + { + "type": 1, + "content": { + "json": "
ACL policy changes
" }, - "name": "q-0bf0cedc" + "name": "div-acl-policy-changes-ff0e68" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| where Action == \"ACL_UPDATE\" or Action contains \"ACL\"\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\n| project TimeGenerated, ActorLogin, Action, Origin, EventGroupID\n| order by TimeGenerated desc", + "size": 0, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "table", + "title": "Recent ACL / policy file modifications", + "gridSettings": { + "formatters": [ + { + "columnMatch": "TimeGenerated", + "formatter": 6 + } + ] + }, + "noDataMessage": "No ACL changes in this window.", + "noDataMessageStyle": 1 + }, + "name": "q-94818c53" + }, + { + "type": 1, + "content": { + "json": "
Subnet routes & exit nodes
" + }, + "name": "div-subnet-routes-and-ex-eef47f" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_Devices_CL\n| summarize arg_max(TimeGenerated, *) by DeviceId\n| where array_length(EnabledRoutes) > 0\n| project DeviceName, User, Os, EnabledRoutes=tostring(EnabledRoutes), AdvertisedRoutes=tostring(AdvertisedRoutes), LastSeen\n| order by LastSeen desc", + "size": 0, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "table", + "title": "Routes currently being served from devices", + "gridSettings": { + "formatters": [ + { + "columnMatch": "LastSeen", + "formatter": 6 + } + ] + }, + "noDataMessage": "No subnet routers active in this tailnet.", + "noDataMessageStyle": 1 + }, + "name": "q-61a792cd" } ] }, "conditionalVisibility": { "parameterName": "selectedTab", "comparison": "isEqualTo", - "value": "signals" + "value": "network" }, - "name": "group-signals" + "name": "group-network" + }, + { + "type": 12, + "conditionalVisibility": { + "parameterName": "selectedTab", + "comparison": "isEqualTo", + "value": "network-flows" + }, + "name": "group-network-flows", + "content": { + "version": "NotebookGroup/1.0", + "groupType": "editable", + "items": [ + { + "type": 1, + "content": { + "json": "
Activity snapshot
" + }, + "name": "div-flow-snapshot" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "let NET = Tailscale_Network_CL | where TimeGenerated {TimeRange};\nunion\n (NET | summarize V=toreal(count()) | extend Metric=\"Total flows\", Order=1),\n (NET | summarize V=toreal(dcount(SrcNodeName)) | extend Metric=\"Src nodes\", Order=2),\n (NET | summarize V=toreal(dcount(DstNodeName)) | extend Metric=\"Dst nodes\", Order=3),\n (NET | where HasVirtualTraffic | summarize V=toreal(count())| extend Metric=\"Virtual\", Order=4),\n (NET | where HasSubnetTraffic | summarize V=toreal(count())| extend Metric=\"Subnet\", Order=5),\n (NET | where HasExitTraffic | summarize V=toreal(count())| extend Metric=\"Exit\", Order=6),\n (NET | where IsRelayed | summarize V=toreal(count())| extend Metric=\"Relayed (DERP)\", Order=7)\n| order by Order asc | project Metric, Value=V", + "size": 3, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "tiles", + "title": "Activity (selected time range)", + "noDataMessage": "No data in this window.", + "noDataMessageStyle": 5, + "tileSettings": { + "titleContent": { + "columnMatch": "Metric", + "formatter": 1 + }, + "leftContent": { + "columnMatch": "Value", + "formatter": 12, + "formatOptions": { + "palette": "auto" + }, + "numberFormat": { + "unit": 17, + "options": { + "style": "decimal", + "maximumFractionDigits": 0 + } + } + }, + "showBorder": false + } + }, + "name": "q-flow-tiles" + }, + { + "type": 1, + "content": { + "json": "
Top talkers
" + }, + "name": "div-flow-top-talkers" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_Network_CL\n| where TimeGenerated {TimeRange}\n| extend SrcLabel = case(\n isnotempty(SrcUser), strcat(SrcNodeName, \" - \", SrcUser),\n isnotempty(SrcTags), strcat(SrcNodeName, \" \", tostring(SrcTags)),\n SrcNodeName)\n| summarize Flows=count() by SrcLabel\n| top 10 by Flows", + "size": 0, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "barchart", + "title": "Top source nodes by flow count", + "noDataMessage": "No flow records in this window.", + "noDataMessageStyle": 5 + }, + "name": "q-flow-top-src", + "customWidth": "50" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_Network_CL\n| where TimeGenerated {TimeRange}\n| extend DstLabel = case(\n isnotempty(DstUser), strcat(DstNodeName, \" - \", DstUser),\n isnotempty(DstTags), strcat(DstNodeName, \" \", tostring(DstTags)),\n DstNodeName)\n| extend DstKind = case(\n isnotempty(DstUser), \"User device\",\n isnotempty(DstTags), \"Tagged service\",\n \"Other\")\n| summarize Flows=count() by DstLabel, DstKind\n| top 10 by Flows", + "size": 0, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "categoricalbar", + "title": "Top destination nodes (split by user vs tagged service)", + "noDataMessage": "No flow records in this window.", + "noDataMessageStyle": 5 + }, + "name": "q-flow-top-dst", + "customWidth": "50" + }, + { + "type": 1, + "content": { + "json": "
Traffic mix over time (stacked area: Virtual / Subnet / Exit / Physical)
" + }, + "name": "div-flow-time-mix" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "let NET = Tailscale_Network_CL | where TimeGenerated {TimeRange};\nunion\n (NET | where HasVirtualTraffic | summarize Flows=count() by bin(TimeGenerated, 10m) | extend Kind=\"Virtual\"),\n (NET | where HasSubnetTraffic | summarize Flows=count() by bin(TimeGenerated, 10m) | extend Kind=\"Subnet\"),\n (NET | where HasExitTraffic | summarize Flows=count() by bin(TimeGenerated, 10m) | extend Kind=\"Exit\"),\n (NET | where HasPhysicalTraffic | summarize Flows=count() by bin(TimeGenerated, 10m) | extend Kind=\"Physical\")\n| project TimeGenerated, Kind, Flows\n| order by TimeGenerated asc", + "size": 4, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "timechart", + "title": "Flow count by traffic kind over time", + "noDataMessage": "No flow records.", + "noDataMessageStyle": 5, + "chartSettings": { + "group": "Kind", + "createOtherGroup": 10, + "showLegend": true, + "ySettings": { + "min": 0 + }, + "chartType": "Area" + } + }, + "name": "q-flow-traffic-mix" + }, + { + "type": 1, + "content": { + "json": "
DERP relay health
" + }, + "name": "div-flow-derp-watch" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_Network_CL\n| where TimeGenerated {TimeRange}\n| summarize Count=count() by Path = iff(IsRelayed, \"Relayed (DERP)\", \"Direct (P2P)\")\n| project Path, Count", + "size": 0, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "piechart", + "title": "Direct vs relayed", + "noDataMessage": "No flow records.", + "noDataMessageStyle": 5 + }, + "name": "q-flow-derp-pie", + "customWidth": "40" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_Network_CL\n| where TimeGenerated {TimeRange}\n| where IsRelayed\n| extend SrcLabel = strcat(SrcNodeName, iff(isempty(SrcUser),\"\",strcat(\" (\",SrcUser,\")\")))\n| summarize RelayedFlows=count(), LastRelay=max(TimeGenerated) by SrcLabel, SrcOs\n| top 10 by RelayedFlows", + "size": 0, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "table", + "title": "Devices stuck on DERP (potential NAT/firewall issue)", + "noDataMessage": "No data in this window.", + "noDataMessageStyle": 5, + "gridSettings": { + "formatters": [ + { + "columnMatch": "RelayedFlows", + "formatter": 4, + "formatOptions": { + "palette": "orange" + } + }, + { + "columnMatch": "LastRelay", + "formatter": 6 + } + ] + } + }, + "name": "q-flow-derp-devices", + "customWidth": "60" + }, + { + "type": 1, + "content": { + "json": "
Tagged-service flows + anomaly hunt
" + }, + "name": "div-flow-tag-anomaly" + }, + { + "type": 12, + "name": "row-tag-anomaly-row", + "content": { + "version": "NotebookGroup/1.0", + "groupType": "editable", + "items": [ + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_Network_CL\n| where TimeGenerated {TimeRange}\n| extend SrcKind = case(\n isnotempty(SrcTags), tostring(SrcTags),\n isnotempty(SrcUser), \"\",\n \"\")\n| extend DstKind = case(\n isnotempty(DstTags), tostring(DstTags),\n isnotempty(DstUser), \"\",\n \"\")\n| summarize Flows=count() by SrcKind, DstKind\n| order by Flows desc", + "size": 1, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "table", + "title": "Tagged-service flow matrix", + "noDataMessage": "No data in this window.", + "noDataMessageStyle": 5, + "gridSettings": { + "formatters": [ + { + "columnMatch": "Flows", + "formatter": 4, + "formatOptions": { + "palette": "blue" + } + } + ] + } + }, + "name": "q-flow-tag-matrix", + "customWidth": "50" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "let baseline = Tailscale_Network_CL\n | where TimeGenerated between (ago(7d) .. ago(1h))\n | distinct SrcNodeName, DstNodeName;\nTailscale_Network_CL\n| where TimeGenerated {TimeRange}\n| where HasVirtualTraffic or HasSubnetTraffic or HasExitTraffic\n| extend ShortSrc = tostring(split(SrcNodeName, \".\")[0])\n| extend ShortDst = tostring(split(DstNodeName, \".\")[0])\n| extend ShortSrcUser = tostring(split(SrcUser, \"@\")[0])\n| extend ShortDstUser = tostring(split(DstUser, \"@\")[0])\n| extend Src = strcat(ShortSrc, iff(isempty(ShortSrcUser),\"\",strcat(\" - \",ShortSrcUser)))\n| extend Dst = strcat(ShortDst, iff(isempty(ShortDstUser),\"\",strcat(\" - \",ShortDstUser)))\n| summarize FirstSeen=min(TimeGenerated), Flows=count() by Src, Dst, SrcNodeName, DstNodeName\n| join kind=leftanti baseline on SrcNodeName, DstNodeName\n| project FirstSeen, Src, Dst, Flows\n| order by FirstSeen desc", + "size": 1, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "table", + "title": "Anomaly: pairs NOT seen in past 7 days", + "noDataMessage": "No new src/dst pairs - all flows match the 7-day baseline.", + "noDataMessageStyle": 5, + "gridSettings": { + "formatters": [ + { + "columnMatch": "FirstSeen", + "formatter": 6 + }, + { + "columnMatch": "Flows", + "formatter": 4, + "formatOptions": { + "palette": "red" + } + } + ] + } + }, + "name": "q-flow-new-pairs", + "customWidth": "50" + } + ] + } + }, + { + "type": 1, + "content": { + "json": "
Egress destinations - exit nodes + subnet routes
" + }, + "name": "div-flow-egress" + }, + { + "type": 12, + "name": "row-egress-row", + "content": { + "version": "NotebookGroup/1.0", + "groupType": "editable", + "items": [ + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_Network_CL\n| where TimeGenerated {TimeRange}\n| where HasExitTraffic\n| extend ExitTag = iff(isempty(DstTags), DstNodeName, tostring(DstTags))\n| summarize Flows=count() by ExitTag", + "size": 1, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "piechart", + "title": "Exit-node tag distribution", + "noDataMessage": "No exit-node traffic in this window.", + "noDataMessageStyle": 5 + }, + "name": "q-flow-exit-providers", + "customWidth": "40" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_Network_CL\n| where TimeGenerated {TimeRange}\n| where HasSubnetTraffic\n| mv-expand s=SubnetTraffic\n| extend DstHost = tostring(split(tostring(s.dst), \":\")[0])\n| extend Bytes = toint(coalesce(s.txBytes,0)) + toint(coalesce(s.rxBytes,0))\n| extend Pkts = toint(coalesce(s.txPkts,0)) + toint(coalesce(s.rxPkts,0))\n| summarize TotalBytes=sum(Bytes), TotalPkts=sum(Pkts), Talkers=dcount(SrcNodeName) by DstHost\n| top 10 by TotalBytes\n| project DstHost, TotalBytes, TotalPkts, Talkers", + "size": 1, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "table", + "title": "Top subnet route destinations", + "noDataMessage": "No subnet-route traffic in this window.", + "noDataMessageStyle": 5, + "gridSettings": { + "formatters": [ + { + "columnMatch": "TotalBytes", + "formatter": 4, + "formatOptions": { + "palette": "green" + } + }, + { + "columnMatch": "TotalPkts", + "formatter": 4, + "formatOptions": { + "palette": "blue" + } + }, + { + "columnMatch": "Talkers", + "formatter": 4, + "formatOptions": { + "palette": "purple" + } + } + ] + } + }, + "name": "q-flow-subnet-dests", + "customWidth": "60" + } + ] + } + }, + { + "type": 1, + "content": { + "json": "
Recent flow detail (last 100)
" + }, + "name": "div-flow-recent-detail" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_Network_CL\n| where TimeGenerated {TimeRange}\n| order by TimeGenerated desc\n| take 100\n| project TimeGenerated,\n Src=SrcNodeName, SrcUser, SrcTags=tostring(SrcTags),\n Dst=DstNodeName, DstUser, DstTags=tostring(DstTags),\n Vir=HasVirtualTraffic, Sub=HasSubnetTraffic, Exi=HasExitTraffic, Phy=HasPhysicalTraffic, IsRelayed", + "size": 4, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "table", + "title": "Last 100 flow records in time range", + "noDataMessage": "No data in this window.", + "noDataMessageStyle": 5, + "gridSettings": { + "formatters": [ + { + "columnMatch": "TimeGenerated", + "formatter": 6 + }, + { + "columnMatch": "IsRelayed", + "formatter": 11 + } + ] + } + }, + "name": "q-flow-recent" + } + ] + } + }, + { + "type": 12, + "conditionalVisibility": { + "parameterName": "selectedTab", + "comparison": "isEqualTo", + "value": "posture" + }, + "name": "group-posture", + "content": { + "version": "NotebookGroup/1.0", + "groupType": "editable", + "items": [ + { + "type": 1, + "content": { + "json": "
Posture integrations - MDM/EDR providers configured for device posture
" + }, + "name": "div-posture-overview" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "let CUR = Tailscale_PostureIntegrations_CL\n | where TimeGenerated {TimeRange}\n | summarize arg_max(TimeGenerated, *) by IntegrationId;\nunion\n (CUR | summarize V=toreal(count()) | extend Metric=\"Integrations\", Order=1),\n (CUR | summarize V=toreal(dcount(Provider)) | extend Metric=\"Distinct providers\", Order=2),\n (CUR | where tostring(Status) has \"healthy\" or tostring(Status) has \"ok\"\n | summarize V=toreal(count()) | extend Metric=\"Healthy\", Order=3),\n (CUR | where not(tostring(Status) has \"healthy\" or tostring(Status) has \"ok\")\n | summarize V=toreal(count()) | extend Metric=\"Unhealthy / unknown\", Order=4)\n| order by Order asc | project Metric, Value=V", + "size": 3, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "tiles", + "title": "Integration inventory (selected time range)", + "noDataMessage": "No posture integrations configured. Set them up at https://login.tailscale.com/admin/settings/posture-integrations.", + "noDataMessageStyle": 5, + "tileSettings": { + "titleContent": { + "columnMatch": "Metric", + "formatter": 1 + }, + "leftContent": { + "columnMatch": "Value", + "formatter": 12, + "formatOptions": { + "palette": "auto" + }, + "numberFormat": { + "unit": 17, + "options": { + "style": "decimal", + "maximumFractionDigits": 0 + } + } + }, + "showBorder": false + } + }, + "name": "q-posture-tile" + }, + { + "type": 1, + "content": { + "json": "
Distribution
" + }, + "name": "div-posture-distribution" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_PostureIntegrations_CL\n| where TimeGenerated {TimeRange}\n| summarize arg_max(TimeGenerated, *) by IntegrationId\n| summarize Count=count() by Provider", + "size": 0, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "piechart", + "title": "Integrations by provider", + "noDataMessage": "No posture integrations configured.", + "noDataMessageStyle": 5 + }, + "name": "q-posture-by-provider", + "customWidth": "50" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_PostureIntegrations_CL\n| where TimeGenerated {TimeRange}\n| summarize arg_max(TimeGenerated, *) by IntegrationId\n| extend Health = case(\n tostring(Status) has \"healthy\" or tostring(Status) has \"ok\", \"Healthy\",\n tostring(Status) has \"error\" or tostring(Status) has \"failed\", \"Error\",\n isnotempty(tostring(Status)), \"Other\",\n \"Unknown\")\n| summarize Count=count() by Health", + "size": 0, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "piechart", + "title": "Health status across integrations", + "noDataMessage": "No posture integrations configured.", + "noDataMessageStyle": 5 + }, + "name": "q-posture-by-status", + "customWidth": "50" + }, + { + "type": 1, + "content": { + "json": "
Inventory detail
" + }, + "name": "div-posture-inventory" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_PostureIntegrations_CL\n| where TimeGenerated {TimeRange}\n| summarize arg_max(TimeGenerated, *) by IntegrationId\n| project IntegrationId, Provider, CloudId, ClientId, TenantId_Provider, Status, LastSnapshot=TimeGenerated", + "size": 4, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "table", + "title": "Configured integrations (latest snapshot per IntegrationId)", + "noDataMessage": "No posture integrations configured.", + "noDataMessageStyle": 5, + "gridSettings": { + "formatters": [ + { + "columnMatch": "Provider", + "formatter": 11 + }, + { + "columnMatch": "LastSnapshot", + "formatter": 6 + } + ] + } + }, + "name": "q-posture-inventory" + }, + { + "type": 1, + "content": { + "json": "
Lifecycle (from audit log)
" + }, + "name": "div-posture-lifecycle" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| where EventType == \"CONFIG\"\n| where tostring(Target.type) contains \"POSTURE\" or tostring(Target.type) contains \"INTEGRATION\"\n| project TimeGenerated, Actor=tostring(Actor.loginName), Action,\n Target=tostring(Target.name), TargetType=tostring(Target.type), ActionDetails\n| order by TimeGenerated desc", + "size": 4, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "table", + "title": "Recent posture integration create/update/delete events", + "noDataMessage": "No posture-integration audit events in this window.", + "noDataMessageStyle": 5, + "gridSettings": { + "formatters": [ + { + "columnMatch": "TimeGenerated", + "formatter": 6 + }, + { + "columnMatch": "Action", + "formatter": 11 + } + ] + } + }, + "name": "q-posture-audit" + } + ] + } + }, + { + "type": 12, + "content": { + "version": "NotebookGroup/1.0", + "groupType": "editable", + "items": [ + { + "type": 1, + "content": { + "json": "
Ingest rate per table
" + }, + "name": "div-ingest-rate-per-tabl-24e5f6" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "union withsource=Table Tailscale_Audit_CL, Tailscale_Devices_CL, Tailscale_Users_CL, Tailscale_Keys_CL, Tailscale_Dns_CL, Tailscale_Settings_CL\n| where TimeGenerated > ago(24h)\n| summarize Rows=count() by Table, bin(TimeGenerated, 1h)\n| order by TimeGenerated asc", + "size": 0, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "timechart", + "title": "Rows ingested per Tailscale table per hour (last 24h)", + "noDataMessage": "No Tailscale data ingested in the last 24h - check the connector card under Sentinel Data Connectors.", + "noDataMessageStyle": 5 + }, + "name": "q-c6fd0143" + }, + { + "type": 1, + "content": { + "json": "
Last poll time per table
" + }, + "name": "div-last-poll-time-per-t-49b051" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "union withsource=Table Tailscale_Audit_CL, Tailscale_Devices_CL, Tailscale_Users_CL, Tailscale_Keys_CL, Tailscale_Dns_CL, Tailscale_Settings_CL\n| summarize LastRow=max(TimeGenerated), TotalRows=count() by Table\n| extend MinutesAgo=toint((now() - LastRow) / 1m)\n| extend Status=case(MinutesAgo < 60, \"Fresh\", MinutesAgo < 360, \"Recent\", MinutesAgo < 1440, \"Stale\", \"Very Stale\")\n| project Table, LastRow, MinutesAgo, TotalRows, Status\n| order by MinutesAgo asc", + "size": 0, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "table", + "title": "Per-table freshness", + "gridSettings": { + "formatters": [ + { + "columnMatch": "LastRow", + "formatter": 6 + }, + { + "columnMatch": "MinutesAgo", + "formatter": 8, + "formatOptions": { + "palette": "redBright" + } + }, + { + "columnMatch": "TotalRows", + "formatter": 8, + "formatOptions": { + "palette": "blue" + } + }, + { + "columnMatch": "Status", + "formatter": 1 + } + ] + } + }, + "name": "q-a02e37a8" + }, + { + "type": 1, + "content": { + "json": "
Log Analytics operational events
" + }, + "name": "div-log-analytics-operat-630749" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "_LogOperation\n| where TimeGenerated > ago(24h)\n| where _ResourceId contains \"tailscale\" or Detail contains \"Tailscale_\"\n| project TimeGenerated, Operation, Level, Detail\n| order by TimeGenerated desc | take 100", + "size": 0, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "table", + "title": "Log Analytics operational events touching Tailscale tables", + "gridSettings": { + "formatters": [ + { + "columnMatch": "TimeGenerated", + "formatter": 6 + }, + { + "columnMatch": "Level", + "formatter": 1 + } + ] + }, + "noDataMessage": "No operational issues recorded in the last 24h.", + "noDataMessageStyle": 1 + }, + "name": "q-b492f150" + } + ] + }, + "conditionalVisibility": { + "parameterName": "selectedTab", + "comparison": "isEqualTo", + "value": "pipeline" + }, + "name": "group-pipeline" }, { "type": 1, "content": { - "json": "---\n\n**Tailscale Premium (CCF)** | All Standard telemetry plus network flow logs (`/logging/network`) and posture integrations (`/posture/integrations`) on Premium / Enterprise tier tailnets." + "json": "
Tailscale Operations (Premium) (CCF) - Microsoft Sentinel content from the Tailscale (CCF) solution, Premium-tier surface. Tables polled from the Tailscale REST API: audit, devices, users, keys, dns, settings, network flows (/logging/network), posture integrations. Filter every panel via the time range above; the Investigate tab adds Actor and Device pickers for drilldown. The Network Flows and Posture tabs use the 15 promoted columns added in 3.1.0 (SrcUser, SrcTags, DstUser, DstTags, HasVirtualTraffic, HasSubnetTraffic, HasExitTraffic, HasPhysicalTraffic, IsRelayed, etc.). Companion workbook: Tailscale Operations (Standard) for Personal / Starter tailnets.
" }, "name": "footer" } ], - "fallbackResourceIds": [], - "fromTemplateId": "sentinel-TailscalePremiumOperations", + "fallbackResourceIds": [ + "Azure Monitor" + ], + "fromTemplateId": "sentinel-TailscalePremiumWorkbook", "$schema": "https://github.com/Microsoft/Application-Insights-Workbooks/blob/master/schema/workbook.json" } \ No newline at end of file diff --git a/Solutions/Tailscale (CCF)/Workbooks/TailscaleStandardOperations.json b/Solutions/Tailscale (CCF)/Workbooks/TailscaleStandardOperations.json index 906a5b57f50..9aed3bfe696 100644 --- a/Solutions/Tailscale (CCF)/Workbooks/TailscaleStandardOperations.json +++ b/Solutions/Tailscale (CCF)/Workbooks/TailscaleStandardOperations.json @@ -55,39 +55,6 @@ }, "name": "header" }, - { - "type": 1, - "content": { - "json": "
Tailnet at a glance
" - }, - "name": "div-tailnet-at-a-glance-04e62b" - }, - { - "type": 3, - "content": { - "version": "KqlItem/1.0", - "query": "let DEV = Tailscale_Devices_CL | summarize arg_max(TimeGenerated, *) by DeviceId;\nlet USR = Tailscale_Users_CL | summarize arg_max(TimeGenerated, *) by UserId;\nlet KEY = Tailscale_Keys_CL | summarize arg_max(TimeGenerated, *) by KeyId;\nunion\n (DEV | summarize V=toreal(count()) | extend Metric=\"Devices\", Order=1),\n (DEV | where Authorized == true | summarize V=toreal(count()) | extend Metric=\"Authorized\", Order=2),\n (DEV | where UpdateAvailable == true | summarize V=toreal(count()) | extend Metric=\"Updates Available\", Order=3),\n (DEV | where SshEnabled == true | summarize V=toreal(count()) | extend Metric=\"SSH-Enabled\", Order=4),\n (USR | summarize V=toreal(count()) | extend Metric=\"Users\", Order=5),\n (USR | where Role =~ \"admin\" or Role =~ \"owner\" or Role =~ \"network-admin\" | summarize V=toreal(count()) | extend Metric=\"Admins\", Order=6),\n (KEY | where isnull(Revoked) and (isnull(Expires) or Expires > now()) | summarize V=toreal(count()) | extend Metric=\"Active Keys\", Order=7),\n (Tailscale_Audit_CL | where TimeGenerated {TimeRange} | summarize V=toreal(count()) | extend Metric=\"Audit Events ({TimeRange:label})\", Order=8)\n| order by Order asc | project Metric, Value=V", - "size": 3, - "queryType": 0, - "resourceType": "microsoft.operationalinsights/workspaces", - "visualization": "tiles", - "tileSettings": { - "titleContent": { - "columnMatch": "Metric", - "formatter": 1 - }, - "leftContent": { - "columnMatch": "Value", - "formatter": 12, - "formatOptions": { - "palette": "auto" - } - }, - "showBorder": false - } - }, - "name": "q-4e9d7cff" - }, { "type": 11, "content": { @@ -176,6 +143,39 @@ "version": "NotebookGroup/1.0", "groupType": "editable", "items": [ + { + "type": 1, + "content": { + "json": "
Tailnet at a glance
" + }, + "name": "div-tailnet-at-a-glance-04e62b" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "let DEV = Tailscale_Devices_CL | summarize arg_max(TimeGenerated, *) by DeviceId;\nlet USR = Tailscale_Users_CL | summarize arg_max(TimeGenerated, *) by UserId;\nlet KEY = Tailscale_Keys_CL | summarize arg_max(TimeGenerated, *) by KeyId;\nunion\n (DEV | summarize V=toreal(count()) | extend Metric=\"Devices\", Order=1),\n (DEV | where Authorized == true | summarize V=toreal(count()) | extend Metric=\"Authorized\", Order=2),\n (DEV | where UpdateAvailable == true | summarize V=toreal(count()) | extend Metric=\"Updates Available\", Order=3),\n (DEV | where SshEnabled == true | summarize V=toreal(count()) | extend Metric=\"SSH-Enabled\", Order=4),\n (USR | summarize V=toreal(count()) | extend Metric=\"Users\", Order=5),\n (USR | where Role =~ \"admin\" or Role =~ \"owner\" or Role =~ \"network-admin\" | summarize V=toreal(count()) | extend Metric=\"Admins\", Order=6),\n (KEY | where isnull(Revoked) and (isnull(Expires) or Expires > now()) | summarize V=toreal(count()) | extend Metric=\"Active Keys\", Order=7),\n (Tailscale_Audit_CL | where TimeGenerated {TimeRange} | summarize V=toreal(count()) | extend Metric=\"Audit Events ({TimeRange:label})\", Order=8)\n| order by Order asc | project Metric, Value=V", + "size": 3, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "tiles", + "tileSettings": { + "titleContent": { + "columnMatch": "Metric", + "formatter": 1 + }, + "leftContent": { + "columnMatch": "Value", + "formatter": 12, + "formatOptions": { + "palette": "auto" + } + }, + "showBorder": false + } + }, + "name": "q-4e9d7cff" + }, { "type": 1, "content": { @@ -303,41 +303,20 @@ "label": "Actor", "type": 2, "isRequired": false, - "query": "Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\n| where isnotempty(ActorLogin)\n| summarize Events=count() by ActorLogin\n| order by Events desc | take 50\n| project value=ActorLogin, label=strcat(ActorLogin, \" (\", Events, \")\")", - "typeSettings": { - "additionalResourceOptions": [ - "value::all" - ], - "showDefault": false - }, - "queryType": 0, - "resourceType": "microsoft.operationalinsights/workspaces", - "value": "value::all" - }, - { - "id": "a9cf7907-f201-4725-a072-a8bd34bef74e", - "version": "KqlParameterItem/1.0", - "name": "SelectedDevice", - "label": "Device", - "type": 2, - "isRequired": false, - "query": "Tailscale_Devices_CL\n| summarize arg_max(TimeGenerated, *) by DeviceId\n| order by LastSeen desc | take 100\n| project value=DeviceName, label=strcat(coalesce(DeviceName, Hostname), \" (\", User, \")\")", + "query": "let opts = Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\n| where isnotempty(ActorLogin)\n| summarize Events=count() by ActorLogin\n| project value=ActorLogin, label=strcat(ActorLogin, \" (\", tostring(Events), \" events)\");\n(print value=\"__ALL__\", label=\"(All actors)\")\n| union opts", "typeSettings": { - "additionalResourceOptions": [ - "value::all" - ], "showDefault": false }, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", - "value": "value::all" + "value": "__ALL__" } ], "style": "pills", "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces" }, - "name": "investigate-pickers" + "name": "investigate-picker-actor" }, { "type": 1, @@ -350,12 +329,12 @@ "type": 3, "content": { "version": "KqlItem/1.0", - "query": "Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\n| where \"{SelectedActor}\" == \"value::all\" or ActorLogin == \"{SelectedActor}\"\n| summarize Events=count() by bin(TimeGenerated, 1h), Action\n| order by TimeGenerated asc", + "query": "Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\n| where \"{SelectedActor}\" == \"__ALL__\" or ActorLogin == \"{SelectedActor}\"\n| summarize Events=count() by bin(TimeGenerated, 1h), Action\n| order by TimeGenerated asc", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", "visualization": "timechart", - "title": "Actions over time for selected actor (or all actors)", + "title": "Actions over time -- actor: {SelectedActor:label}", "noDataMessage": "Select an actor from the Actor dropdown above, or leave on 'All' to see total activity.", "noDataMessageStyle": 5 }, @@ -365,12 +344,12 @@ "type": 3, "content": { "version": "KqlItem/1.0", - "query": "Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\n| where \"{SelectedActor}\" == \"value::all\" or ActorLogin == \"{SelectedActor}\"\n| extend TargetType=tostring(Target.type), TargetName=tostring(coalesce(Target.name, Target.id))\n| project TimeGenerated, ActorLogin, Action, TargetType, TargetName, Origin\n| order by TimeGenerated desc | take 100", + "query": "Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\n| where \"{SelectedActor}\" == \"__ALL__\" or ActorLogin == \"{SelectedActor}\"\n| extend TargetType=tostring(Target.type), TargetName=tostring(coalesce(Target.name, Target.id))\n| project TimeGenerated, ActorLogin, Action, TargetType, TargetName, Origin\n| order by TimeGenerated desc | take 100", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", "visualization": "table", - "title": "Events from selected actor", + "title": "Recent events for actor: {SelectedActor:label}", "gridSettings": { "formatters": [ { @@ -384,6 +363,33 @@ }, "name": "q-a742f6fd" }, + { + "type": 9, + "content": { + "version": "KqlParameterItem/1.0", + "parameters": [ + { + "id": "a9cf7907-f201-4725-a072-a8bd34bef74e", + "version": "KqlParameterItem/1.0", + "name": "SelectedDevice", + "label": "Device", + "type": 2, + "isRequired": false, + "query": "let opts = Tailscale_Devices_CL\n| summarize arg_max(TimeGenerated, *) by DeviceId\n| order by LastSeen desc | take 100\n| project value=DeviceName, label=strcat(coalesce(DeviceName, Hostname), \" (\", User, \")\");\n(print value=\"__ALL__\", label=\"(All devices)\")\n| union opts", + "typeSettings": { + "showDefault": false + }, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "value": "__ALL__" + } + ], + "style": "pills", + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces" + }, + "name": "investigate-picker-device" + }, { "type": 1, "content": { @@ -395,12 +401,12 @@ "type": 3, "content": { "version": "KqlItem/1.0", - "query": "Tailscale_Devices_CL\n| where \"{SelectedDevice}\" == \"value::all\" or DeviceName == \"{SelectedDevice}\"\n| summarize arg_max(TimeGenerated, *) by DeviceId\n| extend OnlineNow = ClientConnectivity.endpoints != \"\" or ConnectedToControl == true\n| project DeviceName, Hostname, User, Os, ClientVersion, UpdateAvailable, Authorized, IsExternal, SshEnabled, LastSeen, Expires, KeyExpiryDisabled, OnlineNow, Addresses, Tags, AdvertisedRoutes", + "query": "Tailscale_Devices_CL\n| where \"{SelectedDevice}\" == \"__ALL__\" or DeviceName == \"{SelectedDevice}\"\n| summarize arg_max(TimeGenerated, *) by DeviceId\n| extend OnlineNow = ClientConnectivity.endpoints != \"\" or ConnectedToControl == true\n| project DeviceName, Hostname, User, Os, ClientVersion, UpdateAvailable, Authorized, IsExternal, SshEnabled, LastSeen, Expires, KeyExpiryDisabled, OnlineNow, Addresses, Tags, AdvertisedRoutes", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", "visualization": "table", - "title": "Device summary", + "title": "Summary for device: {SelectedDevice:label}", "noDataMessage": "Select a device from the Device dropdown above. Defaults to 'All'.", "noDataMessageStyle": 5 }, @@ -410,12 +416,12 @@ "type": 3, "content": { "version": "KqlItem/1.0", - "query": "Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| extend TargetType=tostring(Target.type), TargetName=tostring(Target.name), TargetId=tostring(Target.id)\n| where (\"{SelectedDevice}\" == \"value::all\" and TargetType == \"NODE\") or TargetName == \"{SelectedDevice}\"\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\n| project TimeGenerated, Action, ActorLogin, TargetName, TargetId, Origin\n| order by TimeGenerated desc | take 100", + "query": "Tailscale_Audit_CL\n| where TimeGenerated {TimeRange}\n| extend TargetType=tostring(Target.type), TargetName=tostring(Target.name), TargetId=tostring(Target.id)\n| where (\"{SelectedDevice}\" == \"__ALL__\" and TargetType == \"NODE\") or TargetName == \"{SelectedDevice}\"\n| extend ActorLogin=tostring(coalesce(Actor.loginName, Actor.displayName))\n| project TimeGenerated, Action, ActorLogin, TargetName, TargetId, Origin\n| order by TimeGenerated desc | take 100", "size": 0, "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", "visualization": "table", - "title": "Audit events touching this device", + "title": "Audit events touching device: {SelectedDevice:label}", "gridSettings": { "formatters": [ { @@ -477,7 +483,7 @@ ] }, "noDataMessage": "Every actor seen in the last 24h has appeared at least once in the prior 30d. Healthy state.", - "noDataMessageStyle": 5 + "noDataMessageStyle": 1 }, "name": "q-9ba7b85e" }, @@ -507,7 +513,7 @@ ] }, "noDataMessage": "No off-hours admin changes recorded - healthy state for an organisation working business hours.", - "noDataMessageStyle": 5 + "noDataMessageStyle": 1 }, "name": "q-721ee490" }, @@ -537,7 +543,7 @@ ] }, "noDataMessage": "No devices have key expiry disabled - good. Disabling key expiry creates devices that never re-auth, drifting from policy.", - "noDataMessageStyle": 5 + "noDataMessageStyle": 1 }, "name": "q-8a8e2fb5" }, @@ -567,7 +573,7 @@ ] }, "noDataMessage": "No never-expiring auth keys - rotation hygiene is good.", - "noDataMessageStyle": 5 + "noDataMessageStyle": 1 }, "name": "q-1372740c" }, @@ -597,7 +603,7 @@ ] }, "noDataMessage": "All devices on current client - nothing to patch.", - "noDataMessageStyle": 5 + "noDataMessageStyle": 1 }, "name": "q-ecebf1f7" }, @@ -634,7 +640,7 @@ ] }, "noDataMessage": "No devices idle 30+ days - inventory is fresh.", - "noDataMessageStyle": 5 + "noDataMessageStyle": 1 }, "name": "q-fb6c1fcc" }, @@ -664,7 +670,7 @@ ] }, "noDataMessage": "No devices advertising subnet routes. Pure mesh topology.", - "noDataMessageStyle": 5 + "noDataMessageStyle": 1 }, "name": "q-9533b081" }, @@ -694,7 +700,7 @@ ] }, "noDataMessage": "No devices have Tailscale SSH enabled - no SSH-via-Tailscale risk surface.", - "noDataMessageStyle": 5 + "noDataMessageStyle": 1 }, "name": "q-735368fb" } @@ -844,11 +850,11 @@ }, { "columnMatch": "Role", - "formatter": 11 + "formatter": 1 }, { "columnMatch": "Status", - "formatter": 11 + "formatter": 1 }, { "columnMatch": "DeviceCount", @@ -892,7 +898,7 @@ ] }, "noDataMessage": "Every active account has at least one device - good hygiene.", - "noDataMessageStyle": 5 + "noDataMessageStyle": 1 }, "name": "q-83fcd942" }, @@ -921,12 +927,12 @@ }, { "columnMatch": "ToRole", - "formatter": 11 + "formatter": 1 } ] }, "noDataMessage": "No role changes in this window.", - "noDataMessageStyle": 5 + "noDataMessageStyle": 1 }, "name": "q-f6c8358a" } @@ -1052,12 +1058,12 @@ }, { "columnMatch": "Issues", - "formatter": 11 + "formatter": 1 } ] }, "noDataMessage": "No devices need attention - all updated, fresh, authorized, and key-rotating.", - "noDataMessageStyle": 5 + "noDataMessageStyle": 1 }, "name": "q-dc5db84c" }, @@ -1086,11 +1092,11 @@ }, { "columnMatch": "Os", - "formatter": 11 + "formatter": 1 }, { "columnMatch": "ClientVersion", - "formatter": 11 + "formatter": 1 } ] } @@ -1123,7 +1129,7 @@ ] }, "noDataMessage": "No subnet routers in this tailnet - pure mesh topology.", - "noDataMessageStyle": 5 + "noDataMessageStyle": 1 }, "name": "q-5004df25" } @@ -1239,11 +1245,11 @@ }, { "columnMatch": "ExpiryStatus", - "formatter": 11 + "formatter": 1 }, { "columnMatch": "KeyType", - "formatter": 11 + "formatter": 1 } ] } @@ -1276,7 +1282,7 @@ ] }, "noDataMessage": "No credential CRUD activity in this window.", - "noDataMessageStyle": 5 + "noDataMessageStyle": 1 }, "name": "q-777693bd" } @@ -1395,7 +1401,7 @@ }, { "columnMatch": "Action", - "formatter": 11 + "formatter": 1 } ] }, @@ -1444,7 +1450,7 @@ }, { "columnMatch": "ConfigType", - "formatter": 11 + "formatter": 1 } ] }, @@ -1501,7 +1507,7 @@ ] }, "noDataMessage": "No DNS changes in this window.", - "noDataMessageStyle": 5 + "noDataMessageStyle": 1 }, "name": "q-a132ff6a" }, @@ -1531,7 +1537,7 @@ ] }, "noDataMessage": "No ACL changes in this window.", - "noDataMessageStyle": 5 + "noDataMessageStyle": 1 }, "name": "q-94818c53" }, @@ -1561,7 +1567,7 @@ ] }, "noDataMessage": "No subnet routers active in this tailnet.", - "noDataMessageStyle": 5 + "noDataMessageStyle": 1 }, "name": "q-61a792cd" } @@ -1641,7 +1647,7 @@ }, { "columnMatch": "Status", - "formatter": 11 + "formatter": 1 } ] } @@ -1673,12 +1679,12 @@ }, { "columnMatch": "Level", - "formatter": 11 + "formatter": 1 } ] }, "noDataMessage": "No operational issues recorded in the last 24h.", - "noDataMessageStyle": 5 + "noDataMessageStyle": 1 }, "name": "q-b492f150" } From 40c18b8d4b9030ee6e0e200342b32d46404d90cd Mon Sep 17 00:00:00 2001 From: noodlemctwoodle Date: Tue, 19 May 2026 12:45:02 +0100 Subject: [PATCH 17/27] docs(tailscale): credit Tailscale for tailnet upgrade enabling Premium build Adds an Acknowledgements section (and table-of-contents entry) thanking Tailscale for upgrading the maintainer's tailnet to Premium. That access was the prerequisite for building and validating the Premium-tier features against live API responses: network flow log ingestion, posture integration inventory, the seven Premium analytic rules, the five Premium hunting queries, and the Premium Operations workbook. Without it the Premium connector would have shipped speculatively rather than end-to-end verified. --- Solutions/Tailscale (CCF)/README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Solutions/Tailscale (CCF)/README.md b/Solutions/Tailscale (CCF)/README.md index 4907d86e4ac..e4adaa5fb1c 100644 --- a/Solutions/Tailscale (CCF)/README.md +++ b/Solutions/Tailscale (CCF)/README.md @@ -24,6 +24,7 @@ Microsoft Sentinel solution that ingests Tailscale identity, device, configurati 10. [Limitations](#10-limitations) 11. [Troubleshooting](#11-troubleshooting) 12. [Support](#12-support) +13. [Acknowledgements](#13-acknowledgements) --- @@ -340,3 +341,9 @@ This is a Community-tier solution. Bugs, feature requests, and PRs: - **Maintainer**: noodlemctwoodle No SLA - the maintainer responds when convenient. PRs that include tests + a reproducer trip the response time considerably. + +--- + +## 13. Acknowledgements + +Thanks to [Tailscale](https://tailscale.com/) for upgrading the maintainer's tailnet to Premium so the Premium-tier features of this solution (network flow log ingestion, posture integration inventory, the seven Premium analytic rules, the five Premium hunting queries, and the Premium Operations workbook) could be built, validated against live API responses, and shipped. Without that access, the Premium connector would have been speculative - the published version is end-to-end verified. From caabafc25ea5e496d44a889791a80475d0c2b3ba Mon Sep 17 00:00:00 2001 From: noodlemctwoodle Date: Tue, 19 May 2026 12:46:04 +0100 Subject: [PATCH 18/27] docs(tailscale): refine Tailscale acknowledgement - flow logs, no trial limit Updates the Acknowledgements section to credit Tailscale more accurately: they enabled Network Flow Logs on the maintainer's tailnet WITHOUT the usual 30-60 day trial restriction, allowing open-ended development and validation against live API data instead of working against a feature timeout. That open-ended access (not just a generic Premium upgrade) is what made the Premium connector end-to-end verifiable. --- Solutions/Tailscale (CCF)/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Solutions/Tailscale (CCF)/README.md b/Solutions/Tailscale (CCF)/README.md index e4adaa5fb1c..266838c5e0f 100644 --- a/Solutions/Tailscale (CCF)/README.md +++ b/Solutions/Tailscale (CCF)/README.md @@ -346,4 +346,4 @@ No SLA - the maintainer responds when convenient. PRs that include tests + a rep ## 13. Acknowledgements -Thanks to [Tailscale](https://tailscale.com/) for upgrading the maintainer's tailnet to Premium so the Premium-tier features of this solution (network flow log ingestion, posture integration inventory, the seven Premium analytic rules, the five Premium hunting queries, and the Premium Operations workbook) could be built, validated against live API responses, and shipped. Without that access, the Premium connector would have been speculative - the published version is end-to-end verified. +Thanks to [Tailscale](https://tailscale.com/) for enabling Network Flow Logs on the maintainer's tailnet - and crucially, without the usual 30-60 day trial restriction - so the Premium-tier features of this solution could be built, validated against live API responses, and shipped without development being interrupted by a feature timeout. That open-ended access is what made it possible to develop and verify the network flow log ingestion (`Tailscale_Network_CL`), posture integration inventory, the seven Premium analytic rules, the five Premium hunting queries, and the Premium Operations workbook end-to-end against real data. Without that support the Premium connector would have shipped speculatively rather than verified. From 7edc476d91ca1ecf7594b14e2b310b7ce0905caa Mon Sep 17 00:00:00 2001 From: noodlemctwoodle Date: Tue, 19 May 2026 12:47:43 +0100 Subject: [PATCH 19/27] docs(tailscale): tighten Tailscale acknowledgement to generic thanks Replaces specifics about what Tailscale did (Network Flow Logs enablement, no trial restriction) with a generic thank-you. The earlier text could inadvertently encourage other developers to request the same arrangement; not the maintainer's intent. The thanks remains, the mechanism does not. --- Solutions/Tailscale (CCF)/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Solutions/Tailscale (CCF)/README.md b/Solutions/Tailscale (CCF)/README.md index 266838c5e0f..623098b6abf 100644 --- a/Solutions/Tailscale (CCF)/README.md +++ b/Solutions/Tailscale (CCF)/README.md @@ -346,4 +346,4 @@ No SLA - the maintainer responds when convenient. PRs that include tests + a rep ## 13. Acknowledgements -Thanks to [Tailscale](https://tailscale.com/) for enabling Network Flow Logs on the maintainer's tailnet - and crucially, without the usual 30-60 day trial restriction - so the Premium-tier features of this solution could be built, validated against live API responses, and shipped without development being interrupted by a feature timeout. That open-ended access is what made it possible to develop and verify the network flow log ingestion (`Tailscale_Network_CL`), posture integration inventory, the seven Premium analytic rules, the five Premium hunting queries, and the Premium Operations workbook end-to-end against real data. Without that support the Premium connector would have shipped speculatively rather than verified. +Thanks to [Tailscale](https://tailscale.com/) for the support that made the Premium-tier features of this solution (network flow log ingestion, posture integration inventory, the seven Premium analytic rules, the five Premium hunting queries, and the Premium Operations workbook) buildable and verifiable against live data. From dff0769568a78e4cbb9570836a19dfde832e412d Mon Sep 17 00:00:00 2001 From: noodlemctwoodle Date: Tue, 19 May 2026 12:52:03 +0100 Subject: [PATCH 20/27] docs(tailscale): sync README with current content counts and schema The README had drifted behind several rounds of content additions and schema work. Now matches the shipped 3.0.0 solution exactly. Header / tier-comparison counts: - 22 -> 24 analytic rules (Standard 15 -> 16, Premium 7 -> 8). - 17 -> 22 hunting queries (Standard 12 unchanged, Premium 5 -> 10). Table column counts (section 5): - Tailscale_Audit_CL: 9 -> 11 cols (EventType + ActionDetails were added to the streamDecl + transform in earlier dev iterations, README never followed). - Tailscale_Network_CL: 10 -> 27 cols (15 cols promoted from srcNode/dstNodes plus 5 traffic-shape/relay flags). - Added a new prose paragraph in section 5 highlighting Network_CL as the richest event table alongside Devices_CL, with the promoted-column list so hunting authors know what is available without JSON traversal. Analytic rules section (section 6): - Standard tier Identity & access: added "OAuth client or API key created with write scopes" (High, Persistence + PrivilegeEscalation) - fires on any granted :write scope. - Premium tier: added "DERP relay traffic surge" (Low, CommandAndControl, T1572) - fires when >75% of a source node's recent flows fall back to DERP relay. Hunting queries section (section 7): - Premium tier added 5 new entries: Cross-tag flow matrix, Persistent DERP relay usage, Off-hours flows, Tagged service fan-in, Multi-device users (each with tactics + use case). Architecture (section 9): - "How three DNS endpoints become one table" rewritten to explain the two different mechanisms the Standard and Premium connectors use to land into Tailscale_Dns_CL. Standard: 3-streams-in, 3 dataFlows. Premium: 1 unified DnsConfig stream, 1 dataFlow (Premium would otherwise be at 11 dataFlows, Azure caps at 10). Troubleshooting (section 11): - Removed obsolete "current version (3.0.2+) polls with ?fields=all" reference. With 3.0.0 as the upstream first release that historical caveat no longer makes sense; rephrased as a generic transform-projection check. --- Solutions/Tailscale (CCF)/README.md | 45 ++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/Solutions/Tailscale (CCF)/README.md b/Solutions/Tailscale (CCF)/README.md index 623098b6abf..15a4b9014ce 100644 --- a/Solutions/Tailscale (CCF)/README.md +++ b/Solutions/Tailscale (CCF)/README.md @@ -3,8 +3,8 @@ Microsoft Sentinel solution that ingests Tailscale identity, device, configuration, audit and (Premium) network-flow telemetry via the OAuth2-secured Tailscale API. Built on the Codeless Connector Framework (CCF) - no Function App or container required. - **2 data connectors** (Standard, Premium) - install whichever matches your Tailscale plan -- **22 analytic rules** (15 Standard + 7 Premium-only) -- **17 hunting queries** (12 Standard + 5 Premium-only) +- **24 analytic rules** (16 Standard + 8 Premium-only) +- **22 hunting queries** (12 Standard + 10 Premium-only) - **2 workbooks** (Standard Operations, Premium Operations) - **9 custom tables** ingested via 9-11 polling rules behind a single Connect button @@ -37,8 +37,8 @@ Install **one** of the two connectors based on your Tailscale plan. The split mi | **Tailscale plan** | Personal (Free), Starter, Premium\* | Premium, Enterprise | | **Pollers behind one Connect** | 9 | 11 | | **Custom tables created** | 7 | 9 | -| **Analytic rules wired** | 15 | 22 (Standard 15 + Premium 7) | -| **Hunting queries wired** | 12 | 17 (Standard 12 + Premium 5) | +| **Analytic rules wired** | 16 | 24 (Standard 16 + Premium 8) | +| **Hunting queries wired** | 12 | 22 (Standard 12 + Premium 10) | | **Workbook** | Standard Operations | Premium Operations | | **Network flow logs** | not exposed by API | `Tailscale_Network_CL` | | **Posture integrations** | not exposed by API | `Tailscale_PostureIntegrations_CL` | @@ -135,19 +135,19 @@ All tables are Log Analytics custom tables (`_CL`) populated via Sentinel CCF po | Table | Cols | Cadence | Source endpoint | Tier | |---|---|---|---|---| -| `Tailscale_Audit_CL` | 9 | 5 min | `/logging/configuration` | Standard + Premium | +| `Tailscale_Audit_CL` | 11 | 5 min | `/logging/configuration` | Standard + Premium | | `Tailscale_Devices_CL` | 27 | 60 min | `/devices?fields=all` | Standard + Premium | | `Tailscale_Users_CL` | 13 | 60 min | `/users` | Standard + Premium | | `Tailscale_Keys_CL` | 10 | 60 min | `/keys?all=true` | Standard + Premium | | `Tailscale_Webhooks_CL` | 8 | 60 min | `/webhooks` | Standard + Premium | | `Tailscale_Settings_CL` | 9 | 60 min | `/settings` | Standard + Premium | | `Tailscale_Dns_CL` | 5 | 60 min | merged from `/dns/nameservers`, `/dns/preferences`, `/dns/searchpaths` | Standard + Premium | -| `Tailscale_Network_CL` | 10 | 5 min | `/logging/network` | Premium only | +| `Tailscale_Network_CL` | 27 | 5 min | `/logging/network` | Premium only | | `Tailscale_PostureIntegrations_CL` | 8 | 60 min | `/posture/integrations` | Premium only | **Snapshot semantics.** All `_CL` tables except `Audit` and `Network` are snapshot tables - each poll writes the full current state of the endpoint. Use `summarize arg_max(TimeGenerated, *) by ` to get the latest snapshot. The 5-min audit and network tables are append-only event streams. -**`Tailscale_Devices_CL` is the richest table** at 27 columns. The 5 most interesting columns for detection are surfaced via `?fields=all`: +**`Tailscale_Devices_CL` is the richest snapshot table** at 27 columns. The 5 most interesting columns for detection are surfaced via `?fields=all`: - `AdvertisedRoutes` / `EnabledRoutes` - dynamic arrays of CIDRs the device offers/has approved - `SshEnabled` - bool, is Tailscale SSH active on this device @@ -155,17 +155,25 @@ All tables are Log Analytics custom tables (`_CL`) populated via Sentinel CCF po - `TailnetLockKey` / `TailnetLockError` - cryptographic node-key validation state - `UpdateAvailable` - bool, client behind latest release +**`Tailscale_Network_CL` is the richest event table** (Premium only) at 27 columns. The DCR transform promotes the most useful inner-object fields out of the source `srcNode` / `dstNodes` dynamics so hunting queries don't have to traverse dynamic JSON: + +- `SrcUser` / `SrcNodeName` / `SrcOs` / `SrcTags` / `SrcAddresses` - src device identity + tagging +- `DstCount` / `DstNodeId` / `DstNodeName` / `DstUser` / `DstOs` / `DstTags` / `DstAddresses` - dst device identity + tagging +- `HasVirtualTraffic` / `HasSubnetTraffic` / `HasExitTraffic` / `HasPhysicalTraffic` - traffic-shape flags +- `IsRelayed` - bool, true when the flow used a DERP relay (detected via `127.3.3.40` in `physicalTraffic`) + --- ## 6. Analytic rules -### Standard tier (15 rules) +### Standard tier (16 rules) -**Identity & access (4)** +**Identity & access (5)** | Rule | Severity | Tactics | What it watches | |---|---|---|---| | New API access token or OAuth client created | Medium | Persistence, CredentialAccess | New `API_ACCESS_TOKEN_CREATE` / `OAUTH_CLIENT_CREATE` audit events | +| OAuth client or API key created with write scopes | High | Persistence, PrivilegeEscalation | New OAuth client or API key whose granted scopes include any `:write` permission | | Auth key created | Low | Persistence | Any new auth key (incl. ephemeral / reusable / preauthorized) | | User role elevated to admin or owner | High | PrivilegeEscalation, Persistence | `USER_ROLE_UPDATE` audit events targeting `admin` or `owner` | | Unauthorized device connected to control plane | High | InitialAccess, Persistence | `Authorized=false AND ConnectedToControl=true` in devices snapshot | @@ -201,13 +209,14 @@ All tables are Log Analytics custom tables (`_CL`) populated via Sentinel CCF po | MagicDNS disabled | Medium | DefenseEvasion | `MAGICDNS_DISABLE` audit event | | Split-DNS configuration modified | High | DefenseEvasion, CommandAndControl | `SPLIT_DNS_UPDATE` audit events (per-domain DNS override) | -### Premium tier (additional 7 rules) +### Premium tier (additional 8 rules) These require `Tailscale_Network_CL` (flow logs) or `Tailscale_PostureIntegrations_CL`. | Rule | Severity | Tactics | What it watches | |---|---|---|---| | Network flow beaconing detected | Medium | CommandAndControl, Exfiltration | Regular periodic flows from a single source over 24h (jitter-tolerant) | +| DERP relay traffic surge | Low | CommandAndControl | More than 75% of a source node's recent flows fell back to DERP relay (`IsRelayed=true`); signals NAT/firewall failure, UDP-blocking middlebox, or attempted evasion | | Large outbound transfer over tailnet | Medium | Exfiltration, Collection | Single flow tx-bytes > 1GB in any 5-min window | | Mass fan-out from single node | High | Discovery, LateralMovement | Source node initiated flows to N+ distinct destinations within 5 min | | Subnet router throughput anomaly | Low | Exfiltration, CommandAndControl | Subnet-router src->dst throughput exceeds 3-sigma of its 7-day baseline | @@ -236,14 +245,19 @@ These require `Tailscale_Network_CL` (flow logs) or `Tailscale_PostureIntegratio | External (shared-in) device inventory | InitialAccess | Devices admitted via Tailscale sharing | | Subnet router CIDR exposure inventory | LateralMovement | Every CIDR currently bridged into the tailnet | -### Premium (5) +### Premium (10) | Query | Tactics | Use case | |---|---|---| | Beaconing candidates (regular periodic flows) | CommandAndControl, Exfiltration | Looser threshold than the analytic rule - investigation aid | +| Cross-tag flow matrix | LateralMovement, Discovery | Flows pivoted by src-tag x dst-tag over 7 days; surfaces tag-to-same-tag loops as worm/service-mesh signal | +| Devices with persistent DERP relay usage | CommandAndControl | Devices that consistently fall back to DERP relay over 24h; long-window companion to the surge rule | | Exit-node usage patterns | CommandAndControl, Exfiltration | Who uses which exit node, how often, how much data | | New src->dst node pairs (lateral movement candidates) | LateralMovement, Discovery | First-time observed flow pair vs 7-day baseline | +| Network flows outside business hours | Exfiltration, CommandAndControl | Off-hours flows with `TaggedSource` discriminator to separate service-account from human activity | +| Tagged services with broad inbound exposure | LateralMovement, InitialAccess | Tagged services ranked by inbound `DistinctSrcDevices` / `DistinctSrcUsers` / `DistinctSrcOs` - surfaces ACL drift | | Top talkers by bytes (virtual traffic) | Exfiltration, Collection | Highest tx/rx nodes over time window | +| Users generating traffic from multiple devices | InitialAccess, Persistence | Multi-device users joined against `Tailscale_Devices_CL.Created` to flag newly-added devices | | Current posture integration inventory | DefenseEvasion | Snapshot of every posture integration and its enabled state | --- @@ -278,7 +292,12 @@ Tailscale's `/logging/configuration`, `/logging/network`, posture, and DNS endpo ### How three DNS endpoints become one table -Tailscale exposes DNS configuration across three endpoints (`/dns/nameservers`, `/dns/preferences`, `/dns/searchpaths`). Rather than create three tables, the DCR uses a **multi-stream-in / single-stream-out** pattern: each poller writes to its own input stream (`Custom-Tailscale_DnsNameservers`, `Custom-Tailscale_DnsPreferences`, `Custom-Tailscale_DnsSearchPaths`), three `dataFlows` transform each with a `ConfigType` discriminator column into one output stream (`Custom-Tailscale_Dns_CL`). Net result: one table to query with a `ConfigType` filter. +Tailscale exposes DNS configuration across three endpoints (`/dns/nameservers`, `/dns/preferences`, `/dns/searchpaths`) but the Sentinel UX is cleaner with one logical table. Both connectors land all three into a single `Tailscale_Dns_CL` table with a `ConfigType` discriminator column - the mechanism differs by connector because Azure DCRs are capped at 10 `dataFlows` and the Premium connector is already at the limit: + +- **Standard connector** (7 tables, 9 dataFlows): each DNS poller writes to its own input stream (`Custom-Tailscale_DnsNameservers_CL`, `Custom-Tailscale_DnsPreferences_CL`, `Custom-Tailscale_DnsSearchPaths_CL`) and three separate `dataFlows` apply per-source transforms before merging into the shared `Tailscale_Dns_CL` output. +- **Premium connector** (9 tables, 9 dataFlows): all three DNS pollers write to a single unified input stream (`Custom-Tailscale_DnsConfig_CL`) so the DCR uses just one `dataFlow` for DNS. Without this consolidation the Premium DCR would be at 11 dataFlows and Azure would reject the deployment. + +Net effect for the operator is identical: one table, filter by `ConfigType`. ### Cadence rationale @@ -321,7 +340,7 @@ Almost always a missing OAuth scope. Tailscale returns HTTP 200 with empty data ### `Tailscale_Devices_CL` is missing `AdvertisedRoutes`, `SshEnabled`, etc. -Older versions of this connector polled `/devices` without `?fields=all`. The current version (3.0.2+) polls with `?fields=all`. If you're on an older version, reinstall from Content Hub then click Connect again. +The connector polls `/devices?fields=all`. If those columns are empty in your workspace, the most likely cause is that the DCR transform isn't projecting them - confirm the deployed `dcr-tailscale*` DCR `transformKql` matches the version in this solution and re-Connect. ### OAuth deployment fails with "Invalid Token Endpoint query parameters" From a1221a941217f8e57b2d2f3386fd6caba0d8c68e Mon Sep 17 00:00:00 2001 From: noodlemctwoodle Date: Tue, 19 May 2026 12:54:05 +0100 Subject: [PATCH 21/27] docs(tailscale): drop VPN-tunnel-events bullet, sync Premium counts Removes the "No VPN tunnel events" limitation bullet - it framed an upstream Tailscale platform decision as a missing feature with a "file a feature request" suggestion, which reads as backhanded in a README that now publicly thanks Tailscale for enabling the Premium build. If the absence ever matters operationally, ops can investigate at that point. Also fixes the stale Premium counts in the adjacent "Network flow logs are Premium-only" bullet: seven rules / five hunts -> eight rules / ten hunts, matching the rest of the README. --- Solutions/Tailscale (CCF)/README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Solutions/Tailscale (CCF)/README.md b/Solutions/Tailscale (CCF)/README.md index 15a4b9014ce..454ab280103 100644 --- a/Solutions/Tailscale (CCF)/README.md +++ b/Solutions/Tailscale (CCF)/README.md @@ -308,8 +308,7 @@ Net effect for the operator is identical: one table, filter by `ConfigType`. ## 10. Limitations -- **No VPN tunnel events.** Tailscale doesn't expose per-tunnel connect/disconnect events via API. This is a Tailscale platform limitation, not a solution one - if it matters, file a feature request with Tailscale. -- **Network flow logs are Premium-only.** The seven Premium rules and five Premium hunts depend on `Tailscale_Network_CL` and won't run on a Standard install. +- **Network flow logs are Premium-only.** The eight Premium rules and ten Premium hunts depend on `Tailscale_Network_CL` and won't run on a Standard install. - **Microsoft pre-built "Network Session Essentials" rules don't auto-cover this data.** Workspace-level ASIM functions (`_Im_NetworkSession` and similar) are sealed and can't be extended from Solutions. To use Microsoft's pre-built network-session detections on Tailscale data, clone the relevant rules and point them at `imNetworkSession` (a non-underscore workspace function that unions Microsoft built-ins with our `vimNetworkSessionTailscale` parser) - included in this solution. - **Snapshot tables overwrite, they don't diff.** Each 60-min snapshot is a complete current-state poll, not a delta. Rules that need transition detection (e.g. "SSH newly enabled") compare the latest snapshot against a 24-hour baseline. From 8c334078cd569e3771c17439ab329428d61871f4 Mon Sep 17 00:00:00 2001 From: noodlemctwoodle Date: Tue, 19 May 2026 12:55:31 +0100 Subject: [PATCH 22/27] docs(tailscale): replace fictional ASIM-parser claim with a planned-work note The README claimed Tailscale (CCF) shipped a vimNetworkSessionTailscale parser and an imNetworkSession workspace function. Neither exists - there is no Parsers/ directory, no parser file under Data Connectors/, and Solution_Tailscale.json has no Parsers section. Only the README itself referenced those names. Replaces the bullet with an accurate "planned follow-up release" note so users aren't led to look for files that aren't there. Building the real parser (mapping the 17 promoted Tailscale_Network_CL columns to ASIM NetworkSession fields + wiring imNetworkSession) is meaningful follow-up work outside 3.0.0 scope. --- Solutions/Tailscale (CCF)/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Solutions/Tailscale (CCF)/README.md b/Solutions/Tailscale (CCF)/README.md index 454ab280103..6ad0011d157 100644 --- a/Solutions/Tailscale (CCF)/README.md +++ b/Solutions/Tailscale (CCF)/README.md @@ -309,7 +309,7 @@ Net effect for the operator is identical: one table, filter by `ConfigType`. ## 10. Limitations - **Network flow logs are Premium-only.** The eight Premium rules and ten Premium hunts depend on `Tailscale_Network_CL` and won't run on a Standard install. -- **Microsoft pre-built "Network Session Essentials" rules don't auto-cover this data.** Workspace-level ASIM functions (`_Im_NetworkSession` and similar) are sealed and can't be extended from Solutions. To use Microsoft's pre-built network-session detections on Tailscale data, clone the relevant rules and point them at `imNetworkSession` (a non-underscore workspace function that unions Microsoft built-ins with our `vimNetworkSessionTailscale` parser) - included in this solution. +- **No ASIM NetworkSession parser yet.** Microsoft's pre-built "Network Session Essentials" detections won't auto-cover Tailscale data in 3.0.0 - a `vimNetworkSessionTailscale` parser mapping `Tailscale_Network_CL` to the ASIM NetworkSession schema is planned for a follow-up release. Until then use the analytic rules and hunting queries shipped in this solution. - **Snapshot tables overwrite, they don't diff.** Each 60-min snapshot is a complete current-state poll, not a delta. Rules that need transition detection (e.g. "SSH newly enabled") compare the latest snapshot against a 24-hour baseline. --- From 0beedf0e099d748f9fab31179dc1d0147aa1dfc9 Mon Sep 17 00:00:00 2001 From: noodlemctwoodle Date: Tue, 19 May 2026 13:07:47 +0100 Subject: [PATCH 23/27] feat(tailscale): add vimNetworkSessionTailscale ASIM NetworkSession parser Implements the ASIM NetworkSession parser the README previously promised but didn't ship. Maps Tailscale_Network_CL (Premium-only flow logs) to the ASIM NetworkSession schema (v0.2.6) so Microsoft's Network Session Essentials pre-built detections can be cloned and re-pointed at Tailscale data with a one-line query change. Files: - Parsers/vimNetworkSessionTailscale.yaml - full parser. Unions flows from VirtualTraffic (NetworkDirection=Local), SubnetTraffic and ExitTraffic (NetworkDirection=Outbound), parsing the "host:port" / "[ipv6]:port" / bare-IP forms into SrcIpAddr/SrcPortNumber/DstIpAddr/ DstPortNumber. Maps every promoted column from the 3.1.0 schema expansion onto its ASIM equivalent: SrcNodeName -> SrcHostname, SrcUser -> SrcUsername, SrcOs -> SrcDvcOs, SrcTags retained via Dvc, DstNodeName -> DstHostname, DstUser -> DstUsername, DstOs -> DstDvcOs. Synthesises NetworkProtocol from proto (1/6/17/58 -> ICMP/TCP/UDP/ ICMPv6), NetworkBytes/NetworkPackets, EventStartTime/EventEndTime from FlowStart/FlowEnd, and the standard EventProduct/EventVendor/ EventSchema constants. PhysicalTraffic deliberately excluded - it is transport-layer (WireGuard/DERP endpoints) rather than logical user session. - Parsers/ASimNetworkSessionTailscale.yaml - thin wrapper aliasing vimNetworkSessionTailscale. Present so ASIM-convention call sites using ASimNetworkSession naming work without modification. Schema discovery / why this format: - V3 packaging tool reads Function: schema (FunctionName / FunctionAlias / FunctionQuery), not the upstream ASIM Parser: schema (ParserName / ParserParams / ParserQuery) which is reserved for Parsers/ASimNetworkSession/ central repo. Authored both parsers in the Function-schema form so the V3 build correctly populates parserObject{1,2}._parserName{1,2} with the actual function names. Without this, the workspace savedSearch resources end up with empty names (verified pre-fix). - Function-schema savedSearch functions don't expose signature parameters, so the standard ASIM filter-pushdown params (starttime, srcipaddr_has_any_prefix, etc.) cannot be honoured. Users apply filters as post-call where clauses: vimNetworkSessionTailscale | where EventStartTime > ago(1d) | where DstPortNumber == 443. Wiring: - Solution_Tailscale.json: added Parsers: [...] array with both files. - README.md header bullet list: added "2 ASIM NetworkSession parsers" alongside data connectors / rules / hunts / workbooks / tables. - README.md section 10 Limitations: prior text already rewritten in the previous commit to describe the parser; no further update needed. Package regenerated; ARM-TTK 48/48 verified after .local-helpers/ finalize-tailscale-package.py strip + 3.0.0 pin. --- .../Data/Solution_Tailscale.json | 4 + Solutions/Tailscale (CCF)/Package/3.0.0.zip | Bin 77845 -> 79745 bytes .../Package/createUiDefinition.json | 2 +- .../Tailscale (CCF)/Package/mainTemplate.json | 578 +++++++++++++----- .../Parsers/ASimNetworkSessionTailscale.yaml | 10 + .../Parsers/vimNetworkSessionTailscale.yaml | 96 +++ Solutions/Tailscale (CCF)/README.md | 3 +- 7 files changed, 546 insertions(+), 147 deletions(-) create mode 100644 Solutions/Tailscale (CCF)/Parsers/ASimNetworkSessionTailscale.yaml create mode 100644 Solutions/Tailscale (CCF)/Parsers/vimNetworkSessionTailscale.yaml diff --git a/Solutions/Tailscale (CCF)/Data/Solution_Tailscale.json b/Solutions/Tailscale (CCF)/Data/Solution_Tailscale.json index edd83d9d060..52ad7fdb74f 100644 --- a/Solutions/Tailscale (CCF)/Data/Solution_Tailscale.json +++ b/Solutions/Tailscale (CCF)/Data/Solution_Tailscale.json @@ -65,5 +65,9 @@ "Workbooks/TailscaleStandardOperations.json", "Workbooks/TailscalePremiumOperations.json" ], + "Parsers": [ + "Parsers/vimNetworkSessionTailscale.yaml", + "Parsers/ASimNetworkSessionTailscale.yaml" + ], "Metadata": "SolutionMetadata.json" } diff --git a/Solutions/Tailscale (CCF)/Package/3.0.0.zip b/Solutions/Tailscale (CCF)/Package/3.0.0.zip index 08b165c1c3bdf9033f74245b9aeb3375ec612007..0beed8785fe931a46c844a6d7d5e0867ff9eaaf7 100644 GIT binary patch delta 79039 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 literal 77845 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

hoX_&n}Kg+zjjx1wGDIB73CTfFQN`vcPMH! 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 T!T*;b%*P%6@%Cef{%HRP^E6Ls diff --git a/Solutions/Tailscale (CCF)/Package/createUiDefinition.json b/Solutions/Tailscale (CCF)/Package/createUiDefinition.json index 7a365b1194c..e3a7abb909f 100644 --- a/Solutions/Tailscale (CCF)/Package/createUiDefinition.json +++ b/Solutions/Tailscale (CCF)/Package/createUiDefinition.json @@ -6,7 +6,7 @@ "config": { "isWizard": false, "basics": { - "description": "\n\n**Note:** Please refer to the following before installing the solution: \n\n• Review the solution [Release Notes](https://github.com/Azure/Azure-Sentinel/tree/master/Solutions/Tailscale%20%28CCF%29/ReleaseNotes.md)\n\n • There may be [known issues](https://aka.ms/sentinelsolutionsknownissues) pertaining to this Solution, please refer to them before installing.\n\nThe [Tailscale](https://tailscale.com/) solution for Microsoft Sentinel ingests Tailscale identity, device, configuration, audit and (Premium) network-flow telemetry via OAuth2-secured APIs. Built on the Codeless Connector Framework (CCF) - no Function App or container required.\n\n**Data connectors in this solution (install the one matching your Tailscale plan):**\n- **Tailscale Standard (CCF)** - Configuration audit, devices, users, keys, webhooks, DNS, settings. Use on **Personal (Free), Starter and Premium** tailnets.\n- **Tailscale Premium (CCF)** - Everything in Standard plus network flow logs and posture integrations. Use on **Premium and Enterprise** tailnets for full coverage.\n\n**Pre-requisites:**\n1. Sign in to [Tailscale OAuth settings](https://login.tailscale.com/admin/settings/oauth)\n2. Create an OAuth client with the scopes for your tier (see the README in this solution).\n3. Copy the client ID and client secret (secret shown once).\n4. Note your tailnet name (e.g. `tailb094d7.ts.net`) from the [Keys page](https://login.tailscale.com/admin/settings/keys).\n\n**Data Connectors:** 2, **Workbooks:** 2, **Analytic Rules:** 24, **Hunting Queries:** 22\n\n[Learn more about Microsoft Sentinel](https://aka.ms/azuresentinel) | [Learn more about Solutions](https://aka.ms/azuresentinelsolutionsdoc)", + "description": "\n\n**Note:** Please refer to the following before installing the solution: \n\n• Review the solution [Release Notes](https://github.com/Azure/Azure-Sentinel/tree/master/Solutions/Tailscale%20%28CCF%29/ReleaseNotes.md)\n\n • There may be [known issues](https://aka.ms/sentinelsolutionsknownissues) pertaining to this Solution, please refer to them before installing.\n\nThe [Tailscale](https://tailscale.com/) solution for Microsoft Sentinel ingests Tailscale identity, device, configuration, audit and (Premium) network-flow telemetry via OAuth2-secured APIs. Built on the Codeless Connector Framework (CCF) - no Function App or container required.\n\n**Data connectors in this solution (install the one matching your Tailscale plan):**\n- **Tailscale Standard (CCF)** - Configuration audit, devices, users, keys, webhooks, DNS, settings. Use on **Personal (Free), Starter and Premium** tailnets.\n- **Tailscale Premium (CCF)** - Everything in Standard plus network flow logs and posture integrations. Use on **Premium and Enterprise** tailnets for full coverage.\n\n**Pre-requisites:**\n1. Sign in to [Tailscale OAuth settings](https://login.tailscale.com/admin/settings/oauth)\n2. Create an OAuth client with the scopes for your tier (see the README in this solution).\n3. Copy the client ID and client secret (secret shown once).\n4. Note your tailnet name (e.g. `tailb094d7.ts.net`) from the [Keys page](https://login.tailscale.com/admin/settings/keys).\n\n**Data Connectors:** 2, **Parsers:** 2, **Workbooks:** 2, **Analytic Rules:** 24, **Hunting Queries:** 22\n\n[Learn more about Microsoft Sentinel](https://aka.ms/azuresentinel) | [Learn more about Solutions](https://aka.ms/azuresentinelsolutionsdoc)", "subscription": { "resourceProviders": [ "Microsoft.OperationsManagement/solutions", diff --git a/Solutions/Tailscale (CCF)/Package/mainTemplate.json b/Solutions/Tailscale (CCF)/Package/mainTemplate.json index c653f22c2b6..7f968c9498c 100644 --- a/Solutions/Tailscale (CCF)/Package/mainTemplate.json +++ b/Solutions/Tailscale (CCF)/Package/mainTemplate.json @@ -347,6 +347,20 @@ "workbookTemplateSpecName2": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-wb-',uniquestring(variables('_workbookContentId2'))))]", "_workbookContentId2": "[variables('workbookContentId2')]", "_workbookcontentProductId2": "[concat(take(variables('_solutionId'),50),'-','wb','-', uniqueString(concat(variables('_solutionId'),'-','Workbook','-',variables('_workbookContentId2'),'-', variables('workbookVersion2'))))]", + "parserObject1": { + "_parserName1": "[concat(parameters('workspace'),'/','vimNetworkSessionTailscale')]", + "_parserId1": "[resourceId('Microsoft.OperationalInsights/workspaces/savedSearches', parameters('workspace'), 'vimNetworkSessionTailscale')]", + "parserTemplateSpecName1": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-pr-',uniquestring('vimNetworkSessionTailscale-Parser')))]", + "parserVersion1": "1.0.0", + "parserContentId1": "vimNetworkSessionTailscale-Parser" + }, + "parserObject2": { + "_parserName2": "[concat(parameters('workspace'),'/','ASimNetworkSessionTailscale')]", + "_parserId2": "[resourceId('Microsoft.OperationalInsights/workspaces/savedSearches', parameters('workspace'), 'ASimNetworkSessionTailscale')]", + "parserTemplateSpecName2": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-pr-',uniquestring('ASimNetworkSessionTailscale-Parser')))]", + "parserVersion2": "1.0.0", + "parserContentId2": "ASimNetworkSessionTailscale-Parser" + }, "_solutioncontentProductId": "[concat(take(variables('_solutionId'),50),'-','sl','-', uniqueString(concat(variables('_solutionId'),'-','Solution','-',variables('_solutionId'),'-', variables('_solutionVersion'))))]" }, "resources": [ @@ -4588,14 +4602,14 @@ } ], "incidentConfiguration": { - "createIncident": true, "groupingConfiguration": { - "groupByEntities": [], - "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "lookbackDuration": "5h", - "enabled": true - } + "reopenClosedIncident": false, + "groupByEntities": [], + "enabled": true, + "lookbackDuration": "5h" + }, + "createIncident": true } } }, @@ -4710,14 +4724,14 @@ } ], "incidentConfiguration": { - "createIncident": true, "groupingConfiguration": { - "groupByEntities": [], - "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "lookbackDuration": "PT5H", - "enabled": true - } + "reopenClosedIncident": false, + "groupByEntities": [], + "enabled": true, + "lookbackDuration": "PT5H" + }, + "createIncident": true } } }, @@ -4825,14 +4839,14 @@ } ], "incidentConfiguration": { - "createIncident": true, "groupingConfiguration": { - "groupByEntities": [], - "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "lookbackDuration": "5h", - "enabled": true - } + "reopenClosedIncident": false, + "groupByEntities": [], + "enabled": true, + "lookbackDuration": "5h" + }, + "createIncident": true } } }, @@ -4939,14 +4953,14 @@ } ], "incidentConfiguration": { - "createIncident": true, "groupingConfiguration": { - "groupByEntities": [], - "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "lookbackDuration": "5h", - "enabled": true - } + "reopenClosedIncident": false, + "groupByEntities": [], + "enabled": true, + "lookbackDuration": "5h" + }, + "createIncident": true } } }, @@ -5063,14 +5077,14 @@ } ], "incidentConfiguration": { - "createIncident": true, "groupingConfiguration": { - "groupByEntities": [], - "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "lookbackDuration": "5h", - "enabled": true - } + "reopenClosedIncident": false, + "groupByEntities": [], + "enabled": true, + "lookbackDuration": "5h" + }, + "createIncident": true } } }, @@ -5178,14 +5192,14 @@ } ], "incidentConfiguration": { - "createIncident": true, "groupingConfiguration": { - "groupByEntities": [], - "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "lookbackDuration": "5h", - "enabled": true - } + "reopenClosedIncident": false, + "groupByEntities": [], + "enabled": true, + "lookbackDuration": "5h" + }, + "createIncident": true } } }, @@ -5301,14 +5315,14 @@ } ], "incidentConfiguration": { - "createIncident": true, "groupingConfiguration": { - "groupByEntities": [], - "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "lookbackDuration": "1d", - "enabled": true - } + "reopenClosedIncident": false, + "groupByEntities": [], + "enabled": true, + "lookbackDuration": "1d" + }, + "createIncident": true } } }, @@ -5426,14 +5440,14 @@ } ], "incidentConfiguration": { - "createIncident": true, "groupingConfiguration": { - "groupByEntities": [], - "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "lookbackDuration": "1d", - "enabled": true - } + "reopenClosedIncident": false, + "groupByEntities": [], + "enabled": true, + "lookbackDuration": "1d" + }, + "createIncident": true } } }, @@ -5542,14 +5556,14 @@ } ], "incidentConfiguration": { - "createIncident": true, "groupingConfiguration": { - "groupByEntities": [], - "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "lookbackDuration": "1d", - "enabled": true - } + "reopenClosedIncident": false, + "groupByEntities": [], + "enabled": true, + "lookbackDuration": "1d" + }, + "createIncident": true } } }, @@ -5658,14 +5672,14 @@ } ], "incidentConfiguration": { - "createIncident": true, "groupingConfiguration": { - "groupByEntities": [], - "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "lookbackDuration": "1d", - "enabled": true - } + "reopenClosedIncident": false, + "groupByEntities": [], + "enabled": true, + "lookbackDuration": "1d" + }, + "createIncident": true } } }, @@ -5774,14 +5788,14 @@ } ], "incidentConfiguration": { - "createIncident": true, "groupingConfiguration": { - "groupByEntities": [], - "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "lookbackDuration": "1d", - "enabled": true - } + "reopenClosedIncident": false, + "groupByEntities": [], + "enabled": true, + "lookbackDuration": "1d" + }, + "createIncident": true } } }, @@ -5888,14 +5902,14 @@ } ], "incidentConfiguration": { - "createIncident": true, "groupingConfiguration": { - "groupByEntities": [], - "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "lookbackDuration": "1d", - "enabled": true - } + "reopenClosedIncident": false, + "groupByEntities": [], + "enabled": true, + "lookbackDuration": "1d" + }, + "createIncident": true } } }, @@ -6013,14 +6027,14 @@ } ], "incidentConfiguration": { - "createIncident": true, "groupingConfiguration": { - "groupByEntities": [], - "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "lookbackDuration": "1d", - "enabled": true - } + "reopenClosedIncident": false, + "groupByEntities": [], + "enabled": true, + "lookbackDuration": "1d" + }, + "createIncident": true } } }, @@ -6138,14 +6152,14 @@ } ], "incidentConfiguration": { - "createIncident": true, "groupingConfiguration": { - "groupByEntities": [], - "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "lookbackDuration": "1d", - "enabled": true - } + "reopenClosedIncident": false, + "groupByEntities": [], + "enabled": true, + "lookbackDuration": "1d" + }, + "createIncident": true } } }, @@ -6263,14 +6277,14 @@ } ], "incidentConfiguration": { - "createIncident": true, "groupingConfiguration": { - "groupByEntities": [], - "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "lookbackDuration": "1d", - "enabled": true - } + "reopenClosedIncident": false, + "groupByEntities": [], + "enabled": true, + "lookbackDuration": "1d" + }, + "createIncident": true } } }, @@ -6386,14 +6400,14 @@ } ], "incidentConfiguration": { - "createIncident": true, "groupingConfiguration": { - "groupByEntities": [], - "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "lookbackDuration": "1d", - "enabled": true - } + "reopenClosedIncident": false, + "groupByEntities": [], + "enabled": true, + "lookbackDuration": "1d" + }, + "createIncident": true } } }, @@ -6520,14 +6534,14 @@ } ], "incidentConfiguration": { - "createIncident": true, "groupingConfiguration": { - "groupByEntities": [], - "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "lookbackDuration": "5h", - "enabled": true - } + "reopenClosedIncident": false, + "groupByEntities": [], + "enabled": true, + "lookbackDuration": "5h" + }, + "createIncident": true } } }, @@ -6663,14 +6677,14 @@ } ], "incidentConfiguration": { - "createIncident": true, "groupingConfiguration": { - "groupByEntities": [], - "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "lookbackDuration": "5h", - "enabled": true - } + "reopenClosedIncident": false, + "groupByEntities": [], + "enabled": true, + "lookbackDuration": "5h" + }, + "createIncident": true } } }, @@ -6807,14 +6821,14 @@ } ], "incidentConfiguration": { - "createIncident": true, "groupingConfiguration": { - "groupByEntities": [], - "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "lookbackDuration": "1d", - "enabled": true - } + "reopenClosedIncident": false, + "groupByEntities": [], + "enabled": true, + "lookbackDuration": "1d" + }, + "createIncident": true } } }, @@ -6942,14 +6956,14 @@ } ], "incidentConfiguration": { - "createIncident": true, "groupingConfiguration": { - "groupByEntities": [], - "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "lookbackDuration": "5h", - "enabled": true - } + "reopenClosedIncident": false, + "groupByEntities": [], + "enabled": true, + "lookbackDuration": "5h" + }, + "createIncident": true } } }, @@ -7067,14 +7081,14 @@ } ], "incidentConfiguration": { - "createIncident": true, "groupingConfiguration": { - "groupByEntities": [], - "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "lookbackDuration": "1d", - "enabled": true - } + "reopenClosedIncident": false, + "groupByEntities": [], + "enabled": true, + "lookbackDuration": "1d" + }, + "createIncident": true } } }, @@ -7183,14 +7197,14 @@ } ], "incidentConfiguration": { - "createIncident": true, "groupingConfiguration": { - "groupByEntities": [], - "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "lookbackDuration": "1d", - "enabled": true - } + "reopenClosedIncident": false, + "groupByEntities": [], + "enabled": true, + "lookbackDuration": "1d" + }, + "createIncident": true } } }, @@ -7297,14 +7311,14 @@ } ], "incidentConfiguration": { - "createIncident": true, "groupingConfiguration": { - "groupByEntities": [], - "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "lookbackDuration": "1d", - "enabled": true - } + "reopenClosedIncident": false, + "groupByEntities": [], + "enabled": true, + "lookbackDuration": "1d" + }, + "createIncident": true } } }, @@ -7420,14 +7434,14 @@ } ], "incidentConfiguration": { - "createIncident": true, "groupingConfiguration": { - "groupByEntities": [], - "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "lookbackDuration": "PT6H", - "enabled": true - } + "reopenClosedIncident": false, + "groupByEntities": [], + "enabled": true, + "lookbackDuration": "PT6H" + }, + "createIncident": true } } }, @@ -9575,6 +9589,270 @@ "version": "[variables('workbookVersion2')]" } }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('parserObject1').parserTemplateSpecName1]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "vimNetworkSessionTailscale Data Parser with template version 3.0.1", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('parserObject1').parserVersion1]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "name": "[variables('parserObject1')._parserName1]", + "apiVersion": "2025-07-01", + "type": "Microsoft.OperationalInsights/workspaces/savedSearches", + "location": "[parameters('workspace-location')]", + "properties": { + "eTag": "*", + "displayName": "ASIM Network Session parser for Tailscale", + "category": "Microsoft Sentinel Parser", + "functionAlias": "vimNetworkSessionTailscale", + "query": "let NetworkProtocolLookup = datatable(proto_lookup: int, NetworkProtocol_lookup: string)\n[\n 1, \"ICMP\",\n 6, \"TCP\",\n 17, \"UDP\",\n 58, \"ICMPv6\"\n];\nlet baseEvents = Tailscale_Network_CL;\nlet virtualFlows = baseEvents\n | where HasVirtualTraffic\n | mv-expand t = VirtualTraffic\n | extend NetworkSessionType = \"Virtual\", NetworkDirection = \"Local\";\nlet subnetFlows = baseEvents\n | where HasSubnetTraffic\n | mv-expand t = SubnetTraffic\n | extend NetworkSessionType = \"Subnet\", NetworkDirection = \"Outbound\";\nlet exitFlows = baseEvents\n | where HasExitTraffic\n | mv-expand t = ExitTraffic\n | extend NetworkSessionType = \"Exit\", NetworkDirection = \"Outbound\";\nunion virtualFlows, subnetFlows, exitFlows\n| extend\n SrcIpAddrAndPort = tostring(t.src),\n DstIpAddrAndPort = tostring(t.dst),\n proto_lookup = toint(t.proto),\n SrcBytes = tolong(t.txBytes),\n DstBytes = tolong(t.rxBytes),\n SrcPackets = tolong(t.txPkts),\n DstPackets = tolong(t.rxPkts)\n| extend\n SrcRawHost = extract(@\"^(.+):([0-9]+)$\", 1, SrcIpAddrAndPort),\n SrcPortNumber = toint(extract(@\"^(.+):([0-9]+)$\", 2, SrcIpAddrAndPort)),\n DstRawHost = extract(@\"^(.+):([0-9]+)$\", 1, DstIpAddrAndPort),\n DstPortNumber = toint(extract(@\"^(.+):([0-9]+)$\", 2, DstIpAddrAndPort))\n| extend\n SrcIpAddr = case(\n isempty(SrcRawHost), SrcIpAddrAndPort,\n startswith(SrcRawHost, \"[\"), substring(SrcRawHost, 1, strlen(SrcRawHost) - 2),\n SrcRawHost\n ),\n DstIpAddr = case(\n isempty(DstRawHost), DstIpAddrAndPort,\n startswith(DstRawHost, \"[\"), substring(DstRawHost, 1, strlen(DstRawHost) - 2),\n DstRawHost\n )\n| lookup NetworkProtocolLookup on proto_lookup\n| extend\n NetworkProtocol = coalesce(NetworkProtocol_lookup, tostring(proto_lookup)),\n NetworkBytes = SrcBytes + DstBytes,\n NetworkPackets = SrcPackets + DstPackets\n| extend\n EventStartTime = todatetime(FlowStart),\n EventEndTime = todatetime(FlowEnd),\n EventProduct = \"Tailscale\",\n EventVendor = \"Tailscale\",\n EventSchema = \"NetworkSession\",\n EventSchemaVersion = \"0.2.6\",\n EventType = \"NetworkSession\",\n EventResult = \"Success\",\n EventCount = int(1),\n EventSeverity = \"Informational\",\n DvcAction = \"Allow\"\n| extend\n SrcHostname = SrcNodeName,\n SrcUsername = SrcUser,\n SrcUsernameType = iff(isnotempty(SrcUser), \"Simple\", \"\"),\n SrcDvcOs = SrcOs,\n DstHostname = DstNodeName,\n DstUsername = DstUser,\n DstUsernameType = iff(isnotempty(DstUser), \"Simple\", \"\"),\n DstDvcOs = DstOs,\n Dvc = SrcNodeName,\n Src = SrcIpAddr,\n Dst = DstIpAddr,\n IpAddr = SrcIpAddr,\n User = SrcUser,\n Hostname = SrcNodeName\n| project-away\n t, NetworkProtocol_lookup, proto_lookup,\n VirtualTraffic, SubnetTraffic, ExitTraffic, PhysicalTraffic,\n HasVirtualTraffic, HasSubnetTraffic, HasExitTraffic, HasPhysicalTraffic,\n SrcAddresses, DstAddresses, SrcTags, DstTags,\n SrcOs, DstOs, SrcUser, DstUser, SrcNodeName, DstNodeName,\n DstCount, DstNodeId, NodeId, IsRelayed,\n SrcIpAddrAndPort, DstIpAddrAndPort, SrcRawHost, DstRawHost,\n FlowStart, FlowEnd,\n TenantId, _ResourceId, Type\n", + "functionParameters": "", + "version": 2, + "tags": [ + { + "name": "description", + "value": "" + } + ] + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('Parser-', last(split(variables('parserObject1')._parserId1,'/'))))]", + "dependsOn": [ + "[variables('parserObject1')._parserId1]" + ], + "properties": { + "parentId": "[resourceId('Microsoft.OperationalInsights/workspaces/savedSearches', parameters('workspace'), 'vimNetworkSessionTailscale')]", + "contentId": "[variables('parserObject1').parserContentId1]", + "kind": "Parser", + "version": "[variables('parserObject1').parserVersion1]", + "source": { + "name": "Tailscale (CCF)", + "kind": "Solution", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "noodlemctwoodle", + "email": "[variables('_email')]" + }, + "support": { + "name": "Tailscale (CCF)", + "tier": "Community", + "email": "ccfconnectors.county118@passmail.com", + "link": "https://github.com/Azure/Azure-Sentinel/issues" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('parserObject1').parserContentId1]", + "contentKind": "Parser", + "displayName": "ASIM Network Session parser for Tailscale", + "contentProductId": "[concat(take(variables('_solutionId'),50),'-','pr','-', uniqueString(concat(variables('_solutionId'),'-','Parser','-',variables('parserObject1').parserContentId1,'-', '1.0.0')))]", + "id": "[concat(take(variables('_solutionId'),50),'-','pr','-', uniqueString(concat(variables('_solutionId'),'-','Parser','-',variables('parserObject1').parserContentId1,'-', '1.0.0')))]", + "version": "[variables('parserObject1').parserVersion1]" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/savedSearches", + "apiVersion": "2025-07-01", + "name": "[variables('parserObject1')._parserName1]", + "location": "[parameters('workspace-location')]", + "properties": { + "eTag": "*", + "displayName": "ASIM Network Session parser for Tailscale", + "category": "Microsoft Sentinel Parser", + "functionAlias": "vimNetworkSessionTailscale", + "query": "let NetworkProtocolLookup = datatable(proto_lookup: int, NetworkProtocol_lookup: string)\n[\n 1, \"ICMP\",\n 6, \"TCP\",\n 17, \"UDP\",\n 58, \"ICMPv6\"\n];\nlet baseEvents = Tailscale_Network_CL;\nlet virtualFlows = baseEvents\n | where HasVirtualTraffic\n | mv-expand t = VirtualTraffic\n | extend NetworkSessionType = \"Virtual\", NetworkDirection = \"Local\";\nlet subnetFlows = baseEvents\n | where HasSubnetTraffic\n | mv-expand t = SubnetTraffic\n | extend NetworkSessionType = \"Subnet\", NetworkDirection = \"Outbound\";\nlet exitFlows = baseEvents\n | where HasExitTraffic\n | mv-expand t = ExitTraffic\n | extend NetworkSessionType = \"Exit\", NetworkDirection = \"Outbound\";\nunion virtualFlows, subnetFlows, exitFlows\n| extend\n SrcIpAddrAndPort = tostring(t.src),\n DstIpAddrAndPort = tostring(t.dst),\n proto_lookup = toint(t.proto),\n SrcBytes = tolong(t.txBytes),\n DstBytes = tolong(t.rxBytes),\n SrcPackets = tolong(t.txPkts),\n DstPackets = tolong(t.rxPkts)\n| extend\n SrcRawHost = extract(@\"^(.+):([0-9]+)$\", 1, SrcIpAddrAndPort),\n SrcPortNumber = toint(extract(@\"^(.+):([0-9]+)$\", 2, SrcIpAddrAndPort)),\n DstRawHost = extract(@\"^(.+):([0-9]+)$\", 1, DstIpAddrAndPort),\n DstPortNumber = toint(extract(@\"^(.+):([0-9]+)$\", 2, DstIpAddrAndPort))\n| extend\n SrcIpAddr = case(\n isempty(SrcRawHost), SrcIpAddrAndPort,\n startswith(SrcRawHost, \"[\"), substring(SrcRawHost, 1, strlen(SrcRawHost) - 2),\n SrcRawHost\n ),\n DstIpAddr = case(\n isempty(DstRawHost), DstIpAddrAndPort,\n startswith(DstRawHost, \"[\"), substring(DstRawHost, 1, strlen(DstRawHost) - 2),\n DstRawHost\n )\n| lookup NetworkProtocolLookup on proto_lookup\n| extend\n NetworkProtocol = coalesce(NetworkProtocol_lookup, tostring(proto_lookup)),\n NetworkBytes = SrcBytes + DstBytes,\n NetworkPackets = SrcPackets + DstPackets\n| extend\n EventStartTime = todatetime(FlowStart),\n EventEndTime = todatetime(FlowEnd),\n EventProduct = \"Tailscale\",\n EventVendor = \"Tailscale\",\n EventSchema = \"NetworkSession\",\n EventSchemaVersion = \"0.2.6\",\n EventType = \"NetworkSession\",\n EventResult = \"Success\",\n EventCount = int(1),\n EventSeverity = \"Informational\",\n DvcAction = \"Allow\"\n| extend\n SrcHostname = SrcNodeName,\n SrcUsername = SrcUser,\n SrcUsernameType = iff(isnotempty(SrcUser), \"Simple\", \"\"),\n SrcDvcOs = SrcOs,\n DstHostname = DstNodeName,\n DstUsername = DstUser,\n DstUsernameType = iff(isnotempty(DstUser), \"Simple\", \"\"),\n DstDvcOs = DstOs,\n Dvc = SrcNodeName,\n Src = SrcIpAddr,\n Dst = DstIpAddr,\n IpAddr = SrcIpAddr,\n User = SrcUser,\n Hostname = SrcNodeName\n| project-away\n t, NetworkProtocol_lookup, proto_lookup,\n VirtualTraffic, SubnetTraffic, ExitTraffic, PhysicalTraffic,\n HasVirtualTraffic, HasSubnetTraffic, HasExitTraffic, HasPhysicalTraffic,\n SrcAddresses, DstAddresses, SrcTags, DstTags,\n SrcOs, DstOs, SrcUser, DstUser, SrcNodeName, DstNodeName,\n DstCount, DstNodeId, NodeId, IsRelayed,\n SrcIpAddrAndPort, DstIpAddrAndPort, SrcRawHost, DstRawHost,\n FlowStart, FlowEnd,\n TenantId, _ResourceId, Type\n", + "functionParameters": "", + "version": 2, + "tags": [ + { + "name": "description", + "value": "" + } + ] + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "location": "[parameters('workspace-location')]", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('Parser-', last(split(variables('parserObject1')._parserId1,'/'))))]", + "dependsOn": [ + "[variables('parserObject1')._parserId1]" + ], + "properties": { + "parentId": "[resourceId('Microsoft.OperationalInsights/workspaces/savedSearches', parameters('workspace'), 'vimNetworkSessionTailscale')]", + "contentId": "[variables('parserObject1').parserContentId1]", + "kind": "Parser", + "version": "[variables('parserObject1').parserVersion1]", + "source": { + "kind": "Solution", + "name": "Tailscale (CCF)", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "noodlemctwoodle", + "email": "[variables('_email')]" + }, + "support": { + "name": "Tailscale (CCF)", + "tier": "Community", + "email": "ccfconnectors.county118@passmail.com", + "link": "https://github.com/Azure/Azure-Sentinel/issues" + } + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('parserObject2').parserTemplateSpecName2]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "ASimNetworkSessionTailscale Data Parser with template version 3.0.1", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('parserObject2').parserVersion2]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "name": "[variables('parserObject2')._parserName2]", + "apiVersion": "2025-07-01", + "type": "Microsoft.OperationalInsights/workspaces/savedSearches", + "location": "[parameters('workspace-location')]", + "properties": { + "eTag": "*", + "displayName": "ASIM Network Session parser for Tailscale (no-prefix wrapper)", + "category": "Microsoft Sentinel Parser", + "functionAlias": "ASimNetworkSessionTailscale", + "query": "vimNetworkSessionTailscale\n", + "functionParameters": "", + "version": 2, + "tags": [ + { + "name": "description", + "value": "" + } + ] + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('Parser-', last(split(variables('parserObject2')._parserId2,'/'))))]", + "dependsOn": [ + "[variables('parserObject2')._parserId2]" + ], + "properties": { + "parentId": "[resourceId('Microsoft.OperationalInsights/workspaces/savedSearches', parameters('workspace'), 'ASimNetworkSessionTailscale')]", + "contentId": "[variables('parserObject2').parserContentId2]", + "kind": "Parser", + "version": "[variables('parserObject2').parserVersion2]", + "source": { + "name": "Tailscale (CCF)", + "kind": "Solution", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "noodlemctwoodle", + "email": "[variables('_email')]" + }, + "support": { + "name": "Tailscale (CCF)", + "tier": "Community", + "email": "ccfconnectors.county118@passmail.com", + "link": "https://github.com/Azure/Azure-Sentinel/issues" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('parserObject2').parserContentId2]", + "contentKind": "Parser", + "displayName": "ASIM Network Session parser for Tailscale (no-prefix wrapper)", + "contentProductId": "[concat(take(variables('_solutionId'),50),'-','pr','-', uniqueString(concat(variables('_solutionId'),'-','Parser','-',variables('parserObject2').parserContentId2,'-', '1.0.0')))]", + "id": "[concat(take(variables('_solutionId'),50),'-','pr','-', uniqueString(concat(variables('_solutionId'),'-','Parser','-',variables('parserObject2').parserContentId2,'-', '1.0.0')))]", + "version": "[variables('parserObject2').parserVersion2]" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/savedSearches", + "apiVersion": "2025-07-01", + "name": "[variables('parserObject2')._parserName2]", + "location": "[parameters('workspace-location')]", + "properties": { + "eTag": "*", + "displayName": "ASIM Network Session parser for Tailscale (no-prefix wrapper)", + "category": "Microsoft Sentinel Parser", + "functionAlias": "ASimNetworkSessionTailscale", + "query": "vimNetworkSessionTailscale\n", + "functionParameters": "", + "version": 2, + "tags": [ + { + "name": "description", + "value": "" + } + ] + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "location": "[parameters('workspace-location')]", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('Parser-', last(split(variables('parserObject2')._parserId2,'/'))))]", + "dependsOn": [ + "[variables('parserObject2')._parserId2]" + ], + "properties": { + "parentId": "[resourceId('Microsoft.OperationalInsights/workspaces/savedSearches', parameters('workspace'), 'ASimNetworkSessionTailscale')]", + "contentId": "[variables('parserObject2').parserContentId2]", + "kind": "Parser", + "version": "[variables('parserObject2').parserVersion2]", + "source": { + "kind": "Solution", + "name": "Tailscale (CCF)", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "noodlemctwoodle", + "email": "[variables('_email')]" + }, + "support": { + "name": "Tailscale (CCF)", + "tier": "Community", + "email": "ccfconnectors.county118@passmail.com", + "link": "https://github.com/Azure/Azure-Sentinel/issues" + } + } + }, { "type": "Microsoft.OperationalInsights/workspaces/providers/contentPackages", "apiVersion": "2023-04-01-preview", @@ -9585,7 +9863,7 @@ "contentSchemaVersion": "3.0.0", "displayName": "Tailscale (CCF)", "publisherDisplayName": "Tailscale (CCF)", - "descriptionHtml": "

Note: Please refer to the following before installing the solution:

\n
\n

\u2022 There may be known issues pertaining to this Solution, please refer to them before installing.

\n

The Tailscale solution for Microsoft Sentinel ingests Tailscale identity, device, configuration, audit and (Premium) network-flow telemetry via OAuth2-secured APIs. Built on the Codeless Connector Framework (CCF) - no Function App or container required.

\n

Data connectors in this solution (install the one matching your Tailscale plan):

\n
    \n
  • Tailscale Standard (CCF) - Configuration audit, devices, users, keys, webhooks, DNS, settings. Use on Personal (Free), Starter and Premium tailnets.
  • \n
  • Tailscale Premium (CCF) - Everything in Standard plus network flow logs and posture integrations. Use on Premium and Enterprise tailnets for full coverage.
  • \n
\n

Pre-requisites:

\n
    \n
  1. Sign in to Tailscale OAuth settings
  2. \n
  3. Create an OAuth client with the scopes for your tier (see the README in this solution).
  4. \n
  5. Copy the client ID and client secret (secret shown once).
  6. \n
  7. Note your tailnet name (e.g. tailb094d7.ts.net) from the Keys page.
  8. \n
\n

Data Connectors: 2, Workbooks: 2, Analytic Rules: 24, Hunting Queries: 22

\n

Learn more about Microsoft Sentinel | Learn more about Solutions

\n", + "descriptionHtml": "

Note: Please refer to the following before installing the solution:

\n

\u2022 Review the solution Release Notes

\n

\u2022 There may be known issues pertaining to this Solution, please refer to them before installing.

\n

The Tailscale solution for Microsoft Sentinel ingests Tailscale identity, device, configuration, audit and (Premium) network-flow telemetry via OAuth2-secured APIs. Built on the Codeless Connector Framework (CCF) - no Function App or container required.

\n

Data connectors in this solution (install the one matching your Tailscale plan):

\n
    \n
  • Tailscale Standard (CCF) - Configuration audit, devices, users, keys, webhooks, DNS, settings. Use on Personal (Free), Starter and Premium tailnets.
  • \n
  • Tailscale Premium (CCF) - Everything in Standard plus network flow logs and posture integrations. Use on Premium and Enterprise tailnets for full coverage.
  • \n
\n

Pre-requisites:

\n
    \n
  1. Sign in to Tailscale OAuth settings
  2. \n
  3. Create an OAuth client with the scopes for your tier (see the README in this solution).
  4. \n
  5. Copy the client ID and client secret (secret shown once).
  6. \n
  7. Note your tailnet name (e.g. tailb094d7.ts.net) from the Keys page.
  8. \n
\n

Data Connectors: 2, Parsers: 2, Workbooks: 2, Analytic Rules: 24, Hunting Queries: 22

\n

Learn more about Microsoft Sentinel | Learn more about Solutions

\n", "contentKind": "Solution", "contentProductId": "[variables('_solutioncontentProductId')]", "id": "[variables('_solutioncontentProductId')]", @@ -9859,6 +10137,16 @@ "kind": "Workbook", "contentId": "[variables('_workbookContentId2')]", "version": "[variables('workbookVersion2')]" + }, + { + "kind": "Parser", + "contentId": "[variables('parserObject1').parserContentId1]", + "version": "[variables('parserObject1').parserVersion1]" + }, + { + "kind": "Parser", + "contentId": "[variables('parserObject2').parserContentId2]", + "version": "[variables('parserObject2').parserVersion2]" } ] }, diff --git a/Solutions/Tailscale (CCF)/Parsers/ASimNetworkSessionTailscale.yaml b/Solutions/Tailscale (CCF)/Parsers/ASimNetworkSessionTailscale.yaml new file mode 100644 index 00000000000..ac12904b67e --- /dev/null +++ b/Solutions/Tailscale (CCF)/Parsers/ASimNetworkSessionTailscale.yaml @@ -0,0 +1,10 @@ +id: 1e9af7d2-4b5a-5067-ad9e-2b3c4d5e6f70 +Function: + Title: ASIM Network Session parser for Tailscale (no-prefix wrapper) + Version: '1.0.0' + LastUpdated: '2026-05-19' +Category: Microsoft Sentinel Parser +FunctionName: ASimNetworkSessionTailscale +FunctionAlias: ASimNetworkSessionTailscale +FunctionQuery: | + vimNetworkSessionTailscale diff --git a/Solutions/Tailscale (CCF)/Parsers/vimNetworkSessionTailscale.yaml b/Solutions/Tailscale (CCF)/Parsers/vimNetworkSessionTailscale.yaml new file mode 100644 index 00000000000..31a739d01ae --- /dev/null +++ b/Solutions/Tailscale (CCF)/Parsers/vimNetworkSessionTailscale.yaml @@ -0,0 +1,96 @@ +id: 0d8fe6c1-3a4f-4f5b-9c8d-1a2b3c4d5e6f +Function: + Title: ASIM Network Session parser for Tailscale + Version: '1.0.0' + LastUpdated: '2026-05-19' +Category: Microsoft Sentinel Parser +FunctionName: vimNetworkSessionTailscale +FunctionAlias: vimNetworkSessionTailscale +FunctionQuery: | + let NetworkProtocolLookup = datatable(proto_lookup: int, NetworkProtocol_lookup: string) + [ + 1, "ICMP", + 6, "TCP", + 17, "UDP", + 58, "ICMPv6" + ]; + let baseEvents = Tailscale_Network_CL; + let virtualFlows = baseEvents + | where HasVirtualTraffic + | mv-expand t = VirtualTraffic + | extend NetworkSessionType = "Virtual", NetworkDirection = "Local"; + let subnetFlows = baseEvents + | where HasSubnetTraffic + | mv-expand t = SubnetTraffic + | extend NetworkSessionType = "Subnet", NetworkDirection = "Outbound"; + let exitFlows = baseEvents + | where HasExitTraffic + | mv-expand t = ExitTraffic + | extend NetworkSessionType = "Exit", NetworkDirection = "Outbound"; + union virtualFlows, subnetFlows, exitFlows + | extend + SrcIpAddrAndPort = tostring(t.src), + DstIpAddrAndPort = tostring(t.dst), + proto_lookup = toint(t.proto), + SrcBytes = tolong(t.txBytes), + DstBytes = tolong(t.rxBytes), + SrcPackets = tolong(t.txPkts), + DstPackets = tolong(t.rxPkts) + | extend + SrcRawHost = extract(@"^(.+):([0-9]+)$", 1, SrcIpAddrAndPort), + SrcPortNumber = toint(extract(@"^(.+):([0-9]+)$", 2, SrcIpAddrAndPort)), + DstRawHost = extract(@"^(.+):([0-9]+)$", 1, DstIpAddrAndPort), + DstPortNumber = toint(extract(@"^(.+):([0-9]+)$", 2, DstIpAddrAndPort)) + | extend + SrcIpAddr = case( + isempty(SrcRawHost), SrcIpAddrAndPort, + startswith(SrcRawHost, "["), substring(SrcRawHost, 1, strlen(SrcRawHost) - 2), + SrcRawHost + ), + DstIpAddr = case( + isempty(DstRawHost), DstIpAddrAndPort, + startswith(DstRawHost, "["), substring(DstRawHost, 1, strlen(DstRawHost) - 2), + DstRawHost + ) + | lookup NetworkProtocolLookup on proto_lookup + | extend + NetworkProtocol = coalesce(NetworkProtocol_lookup, tostring(proto_lookup)), + NetworkBytes = SrcBytes + DstBytes, + NetworkPackets = SrcPackets + DstPackets + | extend + EventStartTime = todatetime(FlowStart), + EventEndTime = todatetime(FlowEnd), + EventProduct = "Tailscale", + EventVendor = "Tailscale", + EventSchema = "NetworkSession", + EventSchemaVersion = "0.2.6", + EventType = "NetworkSession", + EventResult = "Success", + EventCount = int(1), + EventSeverity = "Informational", + DvcAction = "Allow" + | extend + SrcHostname = SrcNodeName, + SrcUsername = SrcUser, + SrcUsernameType = iff(isnotempty(SrcUser), "Simple", ""), + SrcDvcOs = SrcOs, + DstHostname = DstNodeName, + DstUsername = DstUser, + DstUsernameType = iff(isnotempty(DstUser), "Simple", ""), + DstDvcOs = DstOs, + Dvc = SrcNodeName, + Src = SrcIpAddr, + Dst = DstIpAddr, + IpAddr = SrcIpAddr, + User = SrcUser, + Hostname = SrcNodeName + | project-away + t, NetworkProtocol_lookup, proto_lookup, + VirtualTraffic, SubnetTraffic, ExitTraffic, PhysicalTraffic, + HasVirtualTraffic, HasSubnetTraffic, HasExitTraffic, HasPhysicalTraffic, + SrcAddresses, DstAddresses, SrcTags, DstTags, + SrcOs, DstOs, SrcUser, DstUser, SrcNodeName, DstNodeName, + DstCount, DstNodeId, NodeId, IsRelayed, + SrcIpAddrAndPort, DstIpAddrAndPort, SrcRawHost, DstRawHost, + FlowStart, FlowEnd, + TenantId, _ResourceId, Type diff --git a/Solutions/Tailscale (CCF)/README.md b/Solutions/Tailscale (CCF)/README.md index 6ad0011d157..38512f55518 100644 --- a/Solutions/Tailscale (CCF)/README.md +++ b/Solutions/Tailscale (CCF)/README.md @@ -6,6 +6,7 @@ Microsoft Sentinel solution that ingests Tailscale identity, device, configurati - **24 analytic rules** (16 Standard + 8 Premium-only) - **22 hunting queries** (12 Standard + 10 Premium-only) - **2 workbooks** (Standard Operations, Premium Operations) +- **2 ASIM NetworkSession parsers** (`vimNetworkSessionTailscale` + `ASimNetworkSessionTailscale` wrapper) - Premium only - **9 custom tables** ingested via 9-11 polling rules behind a single Connect button --- @@ -309,7 +310,7 @@ Net effect for the operator is identical: one table, filter by `ConfigType`. ## 10. Limitations - **Network flow logs are Premium-only.** The eight Premium rules and ten Premium hunts depend on `Tailscale_Network_CL` and won't run on a Standard install. -- **No ASIM NetworkSession parser yet.** Microsoft's pre-built "Network Session Essentials" detections won't auto-cover Tailscale data in 3.0.0 - a `vimNetworkSessionTailscale` parser mapping `Tailscale_Network_CL` to the ASIM NetworkSession schema is planned for a follow-up release. Until then use the analytic rules and hunting queries shipped in this solution. +- **Microsoft pre-built "Network Session Essentials" detections require a clone-and-rewire.** This solution ships a `vimNetworkSessionTailscale` ASIM NetworkSession parser (and the param-less `ASimNetworkSessionTailscale` wrapper) that maps `Tailscale_Network_CL` to the ASIM NetworkSession schema. Microsoft's pre-built detections call the sealed workspace function `_Im_NetworkSession`, which can't be extended from a Solution - so to apply those detections to Tailscale data, clone the rule and replace `_Im_NetworkSession(...)` with `vimNetworkSessionTailscale(...)` (or `union vimNetworkSessionTailscale(...), _Im_NetworkSession(...)` if you want both sources in one query). - **Snapshot tables overwrite, they don't diff.** Each 60-min snapshot is a complete current-state poll, not a delta. Rules that need transition detection (e.g. "SSH newly enabled") compare the latest snapshot against a 24-hour baseline. --- From 35c26ced9e1a128ccc807eef7e7f4ea2d7334341 Mon Sep 17 00:00:00 2001 From: noodlemctwoodle Date: Mon, 15 Jun 2026 12:58:41 +0100 Subject: [PATCH 24/27] fix(tailscale): align CustomTables schemas + finish hunt description sweep CustomTables schemas had drifted behind the DCR streamDeclarations: - Tailscale_Audit_CL.json: added EventType + ActionDetails (introduced in earlier dev iteration but never reflected in the validation schema; queries referencing them would have failed CI KqlValidation). - Tailscale_Network_CL.json: added the 17 columns the DCR transform promotes out of srcNode/dstNodes/traffic arrays (SrcUser, SrcNodeName, SrcOs, SrcTags, SrcAddresses, DstCount, DstNodeId, DstNodeName, DstUser, DstOs, DstTags, DstAddresses, HasVirtualTraffic, HasSubnetTraffic, HasExitTraffic, HasPhysicalTraffic, IsRelayed). The hunting queries and Premium analytic rules reference these columns; without the schema update KqlValidation would fail "name does not refer to any known table column". Hunting query description sweep finish-up: two files were missed in the parallel-edit batch earlier (the Read-before-Edit guard blocked them and they didn't make it into the follow-up batch): - TailscaleSplitDnsPerDomainChanges.yaml: Reconstructs -> Identifies - TailscaleSubnetRouteExposure.yaml: Lists -> Identifies --- .../CustomTables/Tailscale_Audit_CL.json | 2 ++ .../CustomTables/Tailscale_Network_CL.json | 19 ++++++++++++++++++- .../TailscaleSplitDnsPerDomainChanges.yaml | 2 +- .../TailscaleSubnetRouteExposure.yaml | 2 +- 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/.script/tests/KqlvalidationsTests/CustomTables/Tailscale_Audit_CL.json b/.script/tests/KqlvalidationsTests/CustomTables/Tailscale_Audit_CL.json index 7c497a5b893..87e3511ef41 100644 --- a/.script/tests/KqlvalidationsTests/CustomTables/Tailscale_Audit_CL.json +++ b/.script/tests/KqlvalidationsTests/CustomTables/Tailscale_Audit_CL.json @@ -5,6 +5,8 @@ { "Name": "TimeGenerated", "Type": "datetime" }, { "Name": "EventTime", "Type": "datetime" }, { "Name": "EventGroupID", "Type": "string" }, + { "Name": "EventType", "Type": "string" }, + { "Name": "ActionDetails", "Type": "string" }, { "Name": "Actor", "Type": "dynamic" }, { "Name": "Action", "Type": "string" }, { "Name": "Target", "Type": "dynamic" }, diff --git a/.script/tests/KqlvalidationsTests/CustomTables/Tailscale_Network_CL.json b/.script/tests/KqlvalidationsTests/CustomTables/Tailscale_Network_CL.json index 2a49931e28a..9f076e31472 100644 --- a/.script/tests/KqlvalidationsTests/CustomTables/Tailscale_Network_CL.json +++ b/.script/tests/KqlvalidationsTests/CustomTables/Tailscale_Network_CL.json @@ -7,9 +7,26 @@ { "Name": "FlowStart", "Type": "datetime" }, { "Name": "FlowEnd", "Type": "datetime" }, { "Name": "SrcNode", "Type": "dynamic" }, + { "Name": "SrcUser", "Type": "string" }, + { "Name": "SrcNodeName", "Type": "string" }, + { "Name": "SrcOs", "Type": "string" }, + { "Name": "SrcTags", "Type": "dynamic" }, + { "Name": "SrcAddresses", "Type": "dynamic" }, { "Name": "DstNodes", "Type": "dynamic" }, + { "Name": "DstCount", "Type": "int" }, + { "Name": "DstNodeId", "Type": "string" }, + { "Name": "DstNodeName", "Type": "string" }, + { "Name": "DstUser", "Type": "string" }, + { "Name": "DstOs", "Type": "string" }, + { "Name": "DstTags", "Type": "dynamic" }, + { "Name": "DstAddresses", "Type": "dynamic" }, { "Name": "VirtualTraffic", "Type": "dynamic" }, { "Name": "SubnetTraffic", "Type": "dynamic" }, { "Name": "ExitTraffic", "Type": "dynamic" }, - { "Name": "PhysicalTraffic", "Type": "dynamic" } + { "Name": "PhysicalTraffic", "Type": "dynamic" }, + { "Name": "HasVirtualTraffic", "Type": "bool" }, + { "Name": "HasSubnetTraffic", "Type": "bool" }, + { "Name": "HasExitTraffic", "Type": "bool" }, + { "Name": "HasPhysicalTraffic", "Type": "bool" }, + { "Name": "IsRelayed", "Type": "bool" } ]} diff --git a/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleSplitDnsPerDomainChanges.yaml b/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleSplitDnsPerDomainChanges.yaml index ff9112296c9..54afc73051b 100644 --- a/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleSplitDnsPerDomainChanges.yaml +++ b/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleSplitDnsPerDomainChanges.yaml @@ -1,7 +1,7 @@ id: e7f8a9b0-3456-7890-12ab-cdef12345012 name: "Tailscale: Split-DNS per-domain change history" description: | - Reconstructs the per-domain Split-DNS change history from the audit log. For each modification event, expands the Old and New documents and surfaces which domains were added, removed, or had their resolver IPs changed. Useful for forensic review after a suspected DNS hijack and for confirming that a planned DNS migration deployed as intended. + Identifies the per-domain Split-DNS change history from the audit log. For each modification event, expands the Old and New documents and surfaces which domains were added, removed, or had their resolver IPs changed. Useful for forensic review after a suspected DNS hijack and for confirming that a planned DNS migration deployed as intended. requiredDataConnectors: - connectorId: TailscaleCCF dataTypes: diff --git a/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleSubnetRouteExposure.yaml b/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleSubnetRouteExposure.yaml index 63366999109..62bd5b7a989 100644 --- a/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleSubnetRouteExposure.yaml +++ b/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleSubnetRouteExposure.yaml @@ -1,7 +1,7 @@ id: f6a7b8c9-0123-4567-8901-234567890047 name: "Tailscale: Subnet router CIDR exposure inventory" description: | - Lists every device currently advertising or enabling subnet routes (i.e., bridging non-tailnet networks into the tailnet). Excludes pure exit-node advertisements (0.0.0.0/0 and ::/0) so only true subnet exposure is surfaced. Use this for change-control - confirm every CIDR listed is intended to be reachable from the tailnet, and that the device's ACL restricts who can route through it. + Identifies every device currently advertising or enabling subnet routes (i.e., bridging non-tailnet networks into the tailnet). Excludes pure exit-node advertisements (0.0.0.0/0 and ::/0) so only true subnet exposure is surfaced. Use this for change-control - confirm every CIDR listed is intended to be reachable from the tailnet, and that the device's ACL restricts who can route through it. requiredDataConnectors: - connectorId: TailscaleCCF dataTypes: From 63221a6b83bce4f1f1b2a81f15aa0cb142755ffc Mon Sep 17 00:00:00 2001 From: noodlemctwoodle Date: Mon, 15 Jun 2026 13:04:04 +0100 Subject: [PATCH 25/27] chore(tailscale): regenerate Package/ against current upstream V3 tooling After merging upstream/master (1.3k+ commits) the V3 packaging tool behaviour changed: variables and parameters the older tool emitted without consumers (_solutionVersion, dataConnectorTemplateName*, dataCollectionEndpointId{1,2}, resourceGroupName, subscription, dataCollectionEndpoint) are now actively referenced by the generated mainTemplate.json (54 refs to _solutionVersion alone). The .local-helpers/finalize-tailscale-package.py script's blind-strip would corrupt the template; the script is updated to only strip entries that test zero-reference at runtime. Net effect on Package/: - mainTemplate.json: regenerated by upstream V3, version-pinned to 3.0.0, 79 variables and 7 parameters (vs 70 / 5 previously) - all referenced. - createUiDefinition.json: regenerated by upstream V3. - 3.0.0.zip: repackaged from the above. Verification: - ARM-TTK: 48/48 PASS on first build (no manual fixup needed). - KQL: PASS (16s, 56 files - schema files for Audit_CL + Network_CL picking up the 19 added columns). - Hyperlink validation: PASS (4s). - Field Types, Classic App Insights: PASS. - TruffleHog + .NET-3.1 detection-schema/non-ASCII: SKIP (tools absent on macOS, substitution checks pass). --- Solutions/Tailscale (CCF)/Package/3.0.0.zip | Bin 79745 -> 79926 bytes .../Package/createUiDefinition.json | 4 +- .../Tailscale (CCF)/Package/mainTemplate.json | 489 +++++++++--------- 3 files changed, 257 insertions(+), 236 deletions(-) diff --git a/Solutions/Tailscale (CCF)/Package/3.0.0.zip b/Solutions/Tailscale (CCF)/Package/3.0.0.zip index 0beed8785fe931a46c844a6d7d5e0867ff9eaaf7..e3742df4a100047c23401ee8e0f755d76e55a982 100644 GIT binary patch delta 71296 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

sV@t+O?X`X5R7M5Y`4G-;%=^iWE-l_ZCQp=8xJ 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

;A3maO0JwLEp+eY`TaSDd$9^2s)ejwfe6WiTj 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 JDU9;C_+R;r@?QV| delta 71200 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 Date: Tue, 16 Jun 2026 10:43:40 +0100 Subject: [PATCH 26/27] fix(tailscale): address PR #14482 Copilot review feedback Cross-referenced against PR #14253 (UniFi) Copilot findings and applied matching fixes where Tailscale had drifted from the same conventions. Description length (26 files, all <=255 chars): - Shortened the `description:` block on all 8 Premium analytic rules, the OAuth-write-scopes Standard rule, plus 17 hunting queries (every one whose original description exceeded the 255-char schema limit). - Original wording preserved verbatim in a new `description-detailed:` block on each file (pattern matches AWS Security Hub solution). - The shortened text on TailscaleOAuthClientCreatedWithWriteScopes now opens with "Identifies" instead of "Detects" (Copilot's #5 finding). Duration format (2 rules): - TailscalePremiumDerpRelaySurge.yaml: PT6H -> 6h - TailscaleOAuthClientCreatedWithWriteScopes.yaml: PT5H -> 5h Both nested under incidentConfiguration.groupingConfiguration where the V3 converter does not auto-convert. WorkbooksMetadata.json: - Added `author: { "name": "noodlemctwoodle" }` block to both Tailscale workbook entries (46/48 Community workbooks have this block; was a Copilot drive-by suggestion). Parsers: - Parsers/ASimNetworkSessionTailscale.yaml: regenerated `id` as a proper v4 UUID (`2b9c1f5a-7d3e-4f8b-a456-c1d2e3f4a5b6`). The previous value had `5` as the 3rd-group first char, violating v4 spec. Cleanup: - Removed duplicate `Solutions/Tailscale (CCF)/Tailscale.svg` (canonical copy lives at `Logos/Tailscale.svg`; resolver looks there). Package: - Regenerated mainTemplate.json + createUiDefinition.json + 3.0.0.zip against current upstream V3 tooling. Copilot's other findings on PR #14482 do not require code changes: - "VaikoraSecurityCenter dropped" - it's present at line 313. - "ReleaseNotes uses ||" - it uses single | (Copilot may have read rendered output). - "publisherId noodlemctwoodle invalid" - approved Community publisherId, used by the previously-accepted PR #14253. - "BasePath should be repo-relative" - kept Windows-style path to match the v-shukore reviewer note on the analogous PR #14253. - "PREMIUM-ENDPOINTS.md uses ||" - it uses single | (same root cause as the ReleaseNotes false positive). Verification: - ARM-TTK 48/48 PASS. - KQL validation PASS (56 files). - Hyperlink + Field Types + Classic App Insights PASS. - All 46 rule/hunt YAMLs parse, all have required keys. --- ...caleOAuthClientCreatedWithWriteScopes.yaml | 5 +- .../TailscalePremiumDerpRelaySurge.yaml | 5 +- ...TailscalePremiumNewPostureIntegration.yaml | 3 +- ...calePremiumPostureIntegrationDisabled.yaml | 3 +- ...lscalePremiumUnexpectedExitNodeEgress.yaml | 3 +- .../TailscaleTailnetLockValidationFailed.yaml | 3 +- .../TailscaleUnauthorizedDeviceConnected.yaml | 3 +- .../TailscaleACLPolicyChurn.yaml | 3 +- .../TailscaleAuthKeySprawl.yaml | 3 +- .../TailscaleDevicesWithSshEnabled.yaml | 3 +- .../TailscaleDormantDevices.yaml | 3 +- .../TailscaleExternalDeviceInventory.yaml | 3 +- .../TailscaleOffHoursConfigChanges.yaml | 3 +- .../TailscaleOrphanedUsers.yaml | 3 +- .../TailscaleOutdatedClients.yaml | 3 +- .../TailscalePremiumBeaconingCandidates.yaml | 3 +- .../TailscalePremiumCrossTagFlowMatrix.yaml | 3 +- .../TailscalePremiumDerpRelayPersistence.yaml | 3 +- .../TailscalePremiumExitNodeUsage.yaml | 3 +- .../TailscalePremiumNewNodePairs.yaml | 3 +- .../TailscalePremiumOffHoursFlows.yaml | 3 +- .../TailscalePremiumPostureInventory.yaml | 3 +- .../TailscalePremiumTaggedServiceFanIn.yaml | 3 +- .../TailscalePremiumUserMultiDevice.yaml | 3 +- .../TailscaleSplitDnsPerDomainChanges.yaml | 3 +- .../TailscaleSubnetRouteExposure.yaml | 3 +- Solutions/Tailscale (CCF)/Package/3.0.0.zip | Bin 79926 -> 77207 bytes .../Package/createUiDefinition.json | 52 +- .../Tailscale (CCF)/Package/mainTemplate.json | 568 +++++++++--------- .../Parsers/ASimNetworkSessionTailscale.yaml | 2 +- Solutions/Tailscale (CCF)/Tailscale.svg | 1 - Workbooks/WorkbooksMetadata.json | 6 + 32 files changed, 371 insertions(+), 340 deletions(-) delete mode 100644 Solutions/Tailscale (CCF)/Tailscale.svg diff --git a/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleOAuthClientCreatedWithWriteScopes.yaml b/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleOAuthClientCreatedWithWriteScopes.yaml index 1acfe79d43c..66b8165a34f 100644 --- a/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleOAuthClientCreatedWithWriteScopes.yaml +++ b/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleOAuthClientCreatedWithWriteScopes.yaml @@ -1,6 +1,7 @@ id: 7237a848-30f2-499b-9ad5-024aea1288bd name: "Tailscale: OAuth client or API key created with write scopes" -description: | +description: Identifies creation of a Tailscale OAuth client or API access key whose granted scopes include WRITE permissions (anything matching :write). Tokens with write scopes are high-value adversary targets. +description-detailed: | Detects creation of a Tailscale OAuth client or API access key whose granted scopes include WRITE permissions (anything matching ":write"). Tokens with write scopes can modify tailnet configuration, manage devices, write ACLs, and revoke keys - high-value adversary targets. Compare against the recent actor history; revoke immediately if unexpected. severity: High status: Available @@ -45,7 +46,7 @@ incidentConfiguration: groupingConfiguration: enabled: true reopenClosedIncident: false - lookbackDuration: PT5H + lookbackDuration: 5h matchingMethod: AllEntities groupByEntities: [] kind: Scheduled diff --git a/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumDerpRelaySurge.yaml b/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumDerpRelaySurge.yaml index 0a67b705d5a..2b7194b412f 100644 --- a/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumDerpRelaySurge.yaml +++ b/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumDerpRelaySurge.yaml @@ -1,6 +1,7 @@ id: 0a1c8d12-e7d3-4890-8b89-8d6dbc1be2f0 name: "Tailscale Premium: DERP relay traffic surge" -description: | +description: Identifies when a source node has more than 75 percent of its recent flows falling back to a DERP relay (Tailscale IsRelayed flag, traffic via 127.3.3.40). Operational signal useful for spotting policy drift. +description-detailed: | Identifies when a source node has more than 75 percent of its recent flows falling back to a DERP relay (Tailscale's IsRelayed flag, traffic via 127.3.3.40). Sustained high relay rate indicates direct WireGuard peer-to-peer is failing - causes include NAT/firewall changes, a network blocking UDP 41641, or potential evasion attempts. Operational signal but useful for spotting policy drift. Requires Tailscale Premium or Enterprise. severity: Low status: Available @@ -44,7 +45,7 @@ incidentConfiguration: groupingConfiguration: enabled: true reopenClosedIncident: false - lookbackDuration: PT6H + lookbackDuration: 6h matchingMethod: AllEntities groupByEntities: [] kind: Scheduled diff --git a/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumNewPostureIntegration.yaml b/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumNewPostureIntegration.yaml index 56777da2221..0d705f006f0 100644 --- a/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumNewPostureIntegration.yaml +++ b/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumNewPostureIntegration.yaml @@ -1,6 +1,7 @@ id: b2c3d4e5-6789-0123-45ab-cdef12345031 name: "Tailscale Premium: New posture integration added" -description: | +description: Identifies when a new device-posture integration is added to the tailnet (Jamf, Kandji, Intune, Kolide, Defender for Endpoint, CrowdStrike, SentinelOne). Verify the addition was sanctioned. +description-detailed: | Identifies when a new device-posture integration is added to the tailnet (Jamf, Kandji, Intune, Kolide, Defender for Endpoint, CrowdStrike, SentinelOne, etc.). Unexpected additions may indicate an attacker establishing a control plane or bypassing compliance gates. Requires Tailscale Premium or Enterprise. severity: Medium status: Available diff --git a/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumPostureIntegrationDisabled.yaml b/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumPostureIntegrationDisabled.yaml index 712ca67876d..1361958eeec 100644 --- a/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumPostureIntegrationDisabled.yaml +++ b/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumPostureIntegrationDisabled.yaml @@ -1,6 +1,7 @@ id: a1b2c3d4-5678-9012-34ab-cdef12345030 name: "Tailscale Premium: Posture integration disabled or removed" -description: | +description: Identifies when a device-posture integration is disabled or removed from the tailnet. Posture integrations enforce device compliance - removal weakens fleet posture and is a possible defense-evasion step. +description-detailed: | Identifies when a device-posture integration is disabled or removed from the tailnet. Posture integrations enforce device compliance; removing one disables that enforcement and increases blast radius for compromised endpoints. Requires Tailscale Premium or Enterprise. severity: High status: Available diff --git a/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumUnexpectedExitNodeEgress.yaml b/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumUnexpectedExitNodeEgress.yaml index 4826adbaef9..67f92176534 100644 --- a/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumUnexpectedExitNodeEgress.yaml +++ b/Solutions/Tailscale (CCF)/Analytic Rules/TailscalePremiumUnexpectedExitNodeEgress.yaml @@ -1,6 +1,7 @@ id: c1d2e3f4-1a2b-3c4d-5e6f-7a8b9c0d1e2f name: "Tailscale Premium: Unexpected exit-node egress" -description: | +description: Identifies when a node sends traffic via an exit node not used in the prior 7-day baseline. First-seen exit destinations from a node may indicate routing-policy drift, data exfiltration, or compromise. +description-detailed: | Identifies when a node sends traffic via an exit node not used in the prior 7-day baseline. First-seen exit destinations from a specific source may indicate unsanctioned egress, a compromised node pivoting, or a policy misconfiguration. Requires Tailscale Premium or Enterprise. severity: Medium status: Available diff --git a/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleTailnetLockValidationFailed.yaml b/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleTailnetLockValidationFailed.yaml index cd0e551f20a..9fd2801cea0 100644 --- a/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleTailnetLockValidationFailed.yaml +++ b/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleTailnetLockValidationFailed.yaml @@ -1,6 +1,7 @@ id: e9f0a1b2-3456-7890-12cd-ef1234560040 name: "Tailscale: Tailnet lock validation failed" -description: | +description: Identifies tailnet devices with a non-empty TailnetLockError, indicating the device failed tailnet-lock cryptographic validation. Suspicious - likely an unsigned node attempting to join. +description-detailed: | Identifies tailnet devices with a non-empty TailnetLockError, indicating the device failed tailnet-lock cryptographic validation. Tailnet lock requires new node-keys be co-signed by trusted signers; errors here are the only direct signal of a node-key injection attempt. severity: High status: Available diff --git a/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleUnauthorizedDeviceConnected.yaml b/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleUnauthorizedDeviceConnected.yaml index e9cc0b56206..9df4f541e56 100644 --- a/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleUnauthorizedDeviceConnected.yaml +++ b/Solutions/Tailscale (CCF)/Analytic Rules/TailscaleUnauthorizedDeviceConnected.yaml @@ -1,6 +1,7 @@ id: a1b2c3d4-5678-9012-3456-789012340042 name: "Tailscale: Unauthorized device connected to control plane" -description: | +description: Identifies devices actively connected to the Tailscale control plane (ConnectedToControl=true) but not yet authorized by an admin (Authorized=false). Often benign onboarding but can indicate rogue joins. +description-detailed: | Identifies devices actively connected to the Tailscale control plane (ConnectedToControl=true) but not yet authorized by an admin (Authorized=false). With device approval enabled, persistent entries warrant review; without approval, persistence may indicate a node-key injection attempt. severity: High status: Available diff --git a/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleACLPolicyChurn.yaml b/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleACLPolicyChurn.yaml index ea7e7885a30..1084aabf5fb 100644 --- a/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleACLPolicyChurn.yaml +++ b/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleACLPolicyChurn.yaml @@ -1,6 +1,7 @@ id: b82e5d4d-2c8f-5e3b-ad2e-1f4c6e8d9eab name: "Tailscale: ACL policy churn" -description: | +description: Identifies short windows where the tailnet ACL policy file was rewritten multiple times. Iterative policy edits during an incident can indicate a defender adjusting rules or an attacker probing. +description-detailed: | Identifies short windows where the tailnet ACL policy file was rewritten multiple times. Iterative policy edits during an incident can indicate rule-hunting by an attacker or a misconfiguration spiral; legitimate ACL refactors usually land as a single change. requiredDataConnectors: - connectorId: TailscaleCCF diff --git a/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleAuthKeySprawl.yaml b/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleAuthKeySprawl.yaml index b9b3e83fc9b..9ecdae14d99 100644 --- a/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleAuthKeySprawl.yaml +++ b/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleAuthKeySprawl.yaml @@ -1,6 +1,7 @@ id: d64e7f6f-4eab-7a5d-cf4a-3b6e8aafbcad name: "Tailscale: Auth key sprawl" -description: | +description: Identifies actors creating multiple auth keys in a short window. A single admin creating many keys for unattended enrollment is normal during a rollout; same pattern can also indicate token-spraying. +description-detailed: | Identifies actors creating multiple auth keys in a short window. A single admin creating many keys for unattended enrollment is normal during a rollout; the same pattern can also indicate token-spraying for persistence. Cross-reference with the Tailscale: Auth key created alert to filter context. requiredDataConnectors: - connectorId: TailscaleCCF diff --git a/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleDevicesWithSshEnabled.yaml b/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleDevicesWithSshEnabled.yaml index fde464a26fd..affba148758 100644 --- a/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleDevicesWithSshEnabled.yaml +++ b/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleDevicesWithSshEnabled.yaml @@ -1,6 +1,7 @@ id: c3d4e5f6-7890-1234-5678-901234560044 name: "Tailscale: Devices with Tailscale SSH enabled" -description: | +description: Identifies devices that currently have Tailscale SSH enabled. Tailscale SSH delivers SSH access over the tailnet using Tailscale identity and is governed by the SSH ACL block in the policy file. +description-detailed: | Identifies devices that currently have Tailscale SSH enabled. Tailscale SSH delivers SSH access over the tailnet using Tailscale identity (no SSH keys needed) and is governed by the SSH ACL block in the policy file. Cross-reference this list with the SSH ACL to confirm only the intended devices are reachable as SSH targets. requiredDataConnectors: - connectorId: TailscaleCCF diff --git a/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleDormantDevices.yaml b/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleDormantDevices.yaml index 2ab88f565e6..80aa9a978e4 100644 --- a/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleDormantDevices.yaml +++ b/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleDormantDevices.yaml @@ -1,6 +1,7 @@ id: e4d5f6a7-4567-8901-23ab-cdef12345004 name: "Tailscale: Devices not seen in 30+ days" -description: | +description: Identifies tailnet devices that have not connected to the control plane for at least 30 days. Dormant devices accumulate risk - they may still have valid keys, advertised routes, or tags. +description-detailed: | Identifies tailnet devices that have not connected to the control plane for at least 30 days. Dormant devices accumulate risk - they may still have valid keys, advertised routes, or tags but no operational oversight. Candidate for removal or key rotation. requiredDataConnectors: - connectorId: TailscaleCCF diff --git a/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleExternalDeviceInventory.yaml b/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleExternalDeviceInventory.yaml index 6fce5a0e915..9ad35e7c3b8 100644 --- a/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleExternalDeviceInventory.yaml +++ b/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleExternalDeviceInventory.yaml @@ -1,6 +1,7 @@ id: d4e5f6a7-8901-2345-6789-012345670045 name: "Tailscale: External (shared-in) device inventory" -description: | +description: Identifies external (shared-in) devices currently active in the tailnet. These devices belong to another tailnet and have been admitted via a Tailscale sharing arrangement. +description-detailed: | Identifies external (shared-in) devices currently active in the tailnet. These devices belong to another tailnet and have been admitted via a Tailscale sharing arrangement. Confirm each entry maps to a documented collaboration and that the corresponding ACL restricts the device to the intended resources. requiredDataConnectors: - connectorId: TailscaleCCF diff --git a/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleOffHoursConfigChanges.yaml b/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleOffHoursConfigChanges.yaml index 60e0b91791c..a5180186d29 100644 --- a/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleOffHoursConfigChanges.yaml +++ b/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleOffHoursConfigChanges.yaml @@ -1,6 +1,7 @@ id: c73f6e5e-3d9a-6f4c-be3f-2a5d7f9eafbc name: "Tailscale: Off-hours configuration changes" -description: | +description: Identifies configuration audit events that occurred outside typical business hours (Monday-Friday 08:00-18:00 UTC). Useful for spotting impromptu maintenance, account compromise, or insider activity. +description-detailed: | Identifies configuration audit events that occurred outside typical business hours (defined as Monday-Friday 08:00-18:00 UTC). Adjust the time window to match your operating region. Useful for spotting impromptu maintenance, account compromise, or insider activity. requiredDataConnectors: - connectorId: TailscaleCCF diff --git a/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleOrphanedUsers.yaml b/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleOrphanedUsers.yaml index e58252d2216..ba5b0e293d5 100644 --- a/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleOrphanedUsers.yaml +++ b/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleOrphanedUsers.yaml @@ -1,6 +1,7 @@ id: a6f7b8c9-6789-0123-45ab-cdef12345006 name: "Tailscale: Users with zero devices" -description: | +description: Identifies tailnet users who have no devices currently registered. Orphaned identities are candidates for off-boarding - they retain whatever role/permissions they were granted. +description-detailed: | Identifies tailnet users who have no devices currently registered. Orphaned identities are candidates for off-boarding - they retain whatever role/permissions they were granted and can still create auth keys or invite others. Review against your HR / identity-provider join-leaver records. requiredDataConnectors: - connectorId: TailscaleCCF diff --git a/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleOutdatedClients.yaml b/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleOutdatedClients.yaml index 06026b74b41..a5e7bfa7bda 100644 --- a/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleOutdatedClients.yaml +++ b/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleOutdatedClients.yaml @@ -1,6 +1,7 @@ id: e5f6a7b8-9012-3456-7890-123456780046 name: "Tailscale: Devices with outdated client version" -description: | +description: Identifies tailnet devices that report UpdateAvailable=true on the latest snapshot. Tailscale releases security updates regularly; outdated clients lack the most recent improvements. +description-detailed: | Identifies tailnet devices that report UpdateAvailable=true on the latest snapshot. Tailscale releases security and feature updates regularly; a device showing UpdateAvailable lacks the most recent improvements. Use this list to plan a fleet update, especially before enabling new ACL features that require minimum client versions. requiredDataConnectors: - connectorId: TailscaleCCF diff --git a/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumBeaconingCandidates.yaml b/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumBeaconingCandidates.yaml index 5f6716d7121..48cddb9b063 100644 --- a/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumBeaconingCandidates.yaml +++ b/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumBeaconingCandidates.yaml @@ -1,6 +1,7 @@ id: b28cbdd2-8cef-be9b-a38e-7faceedfdbec name: "Tailscale Premium: Beaconing candidates (regular periodic flows)" -description: | +description: Identifies flows that recur at a highly regular interval, which is the signature of C2 beaconing or scheduled exfiltration jobs. Looser threshold than the analytic rule - investigation aid. +description-detailed: | Identifies flows that recur at a highly regular interval, which is the signature of C2 beaconing or scheduled exfiltration jobs. Looks for src->dst pairs where 80%+ of inter-flow gaps cluster around the same delta. Requires Tailscale Premium or Enterprise. requiredDataConnectors: - connectorId: TailscalePremiumCCF diff --git a/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumCrossTagFlowMatrix.yaml b/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumCrossTagFlowMatrix.yaml index 03c18902fde..9b424a7221a 100644 --- a/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumCrossTagFlowMatrix.yaml +++ b/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumCrossTagFlowMatrix.yaml @@ -1,6 +1,7 @@ id: a8978f27-3c85-4c29-a45a-c4a5e43fef2d name: "Tailscale Premium: Cross-tag flow matrix" -description: | +description: Identifies network flows pivoted by source-tag x destination-tag over 7 days. Highlights tag-to-tag traffic, useful for ACL validation. Same-tag loops can signal worm-style propagation. +description-detailed: | Identifies network flows pivoted by source-tag x destination-tag over the past 7 days, treating untagged user devices as ``. Highlights which tagged services interact - useful for ACL validation, detecting unexpected tag-to-tag traffic, and spotting tag-to-same-tag loops that can indicate worm-style propagation or service-mesh anomalies. Order by Flows to find the heaviest tag pairs; sort by DistinctSrcDevices to find broadly-used services. Requires Tailscale Premium or Enterprise. requiredDataConnectors: - connectorId: TailscalePremiumCCF diff --git a/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumDerpRelayPersistence.yaml b/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumDerpRelayPersistence.yaml index 30e94f9bbfe..25e55a16556 100644 --- a/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumDerpRelayPersistence.yaml +++ b/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumDerpRelayPersistence.yaml @@ -1,6 +1,7 @@ id: 20457fba-08e2-42d7-b972-fbe9acf583c8 name: "Tailscale Premium: Devices with persistent DERP relay usage" -description: | +description: Identifies devices that have consistently fallen back to DERP relay (IsRelayed=true) over the past 24 hours. Sustained relay usage points to NAT/firewall misconfiguration or deliberate evasion. +description-detailed: | Identifies devices that have consistently fallen back to DERP relay (IsRelayed = true) over the past 24 hours. Sustained relay usage points to NAT/firewall misconfiguration on the device's network, a tunnel-blocking middlebox, or in rare cases deliberate evasion attempting to obscure direct peer-to-peer paths. Useful for proactive network-hygiene investigation and capacity planning. Requires Tailscale Premium or Enterprise. requiredDataConnectors: - connectorId: TailscalePremiumCCF diff --git a/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumExitNodeUsage.yaml b/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumExitNodeUsage.yaml index f1709e06371..799b953ac78 100644 --- a/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumExitNodeUsage.yaml +++ b/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumExitNodeUsage.yaml @@ -1,6 +1,7 @@ id: a37bacc1-7bde-ad8a-f27d-6e9bcdcecadb name: "Tailscale Premium: Exit-node usage patterns" -description: | +description: Identifies traffic leaving the tailnet via exit nodes. Exit-node use is typically intentional (regional egress, privacy routing) but unexpected egress from a node warrants investigation. +description-detailed: | Identifies traffic leaving the tailnet via exit nodes. Exit node use is typically intentional (regional egress, privacy routing) but unexpected exit-node traffic from a node should be investigated as a potential pivot point or unsanctioned egress. Requires Tailscale Premium or Enterprise. requiredDataConnectors: - connectorId: TailscalePremiumCCF diff --git a/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumNewNodePairs.yaml b/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumNewNodePairs.yaml index e4a8500e422..cc6263a845b 100644 --- a/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumNewNodePairs.yaml +++ b/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumNewNodePairs.yaml @@ -1,6 +1,7 @@ id: e55f8aaf-5fbc-8b6e-d05b-4c7faabcadbe name: "Tailscale Premium: New src->dst node pairs (lateral movement candidates)" -description: | +description: Identifies tailnet src->dst pairs observed in the last 24h that were NOT observed in the prior 7-day baseline. Useful for spotting lateral movement to nodes that don't usually talk. +description-detailed: | Identifies tailnet src->dst pairs observed in the last 24h that were NOT observed in the prior 7-day baseline. Useful for spotting lateral movement to nodes that don't usually talk to each other. Requires Tailscale Premium or Enterprise (network flow logs). requiredDataConnectors: - connectorId: TailscalePremiumCCF diff --git a/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumOffHoursFlows.yaml b/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumOffHoursFlows.yaml index 9419376a05e..4990f80cdec 100644 --- a/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumOffHoursFlows.yaml +++ b/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumOffHoursFlows.yaml @@ -1,6 +1,7 @@ id: 622ce88a-0838-4bbe-8a00-ab8ac8377f41 name: "Tailscale Premium: Network flows outside business hours" -description: | +description: Identifies network flows occurring outside 07:00-19:00 UTC on weekdays plus all weekend, over 7 days. Filters to virtual/subnet/exit traffic. Useful for spotting unattended automation gone wrong. +description-detailed: | Identifies network flows occurring outside 07:00-19:00 UTC on weekdays, plus all of weekend, over the past 7 days. Filters to virtual/subnet/exit traffic (drops DERP-only keepalive noise). Useful for spotting unattended automation gone wrong, scheduled exfiltration, or unsanctioned after-hours access by humans. The TaggedSource column makes it easy to separate cron-like service traffic (tag:backup, tag:cron) from human user activity. Requires Tailscale Premium or Enterprise. requiredDataConnectors: - connectorId: TailscalePremiumCCF diff --git a/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumPostureInventory.yaml b/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumPostureInventory.yaml index 15b14198492..5a245511699 100644 --- a/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumPostureInventory.yaml +++ b/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumPostureInventory.yaml @@ -1,6 +1,7 @@ id: c3d4e5f6-7890-1234-56ab-cdef12345032 name: "Tailscale Premium: Current posture integration inventory" -description: | +description: Identifies the current set of device-posture integrations configured on the tailnet (latest snapshot per integration). Useful for compliance attestation and detecting drift from the expected baseline. +description-detailed: | Identifies the current set of device-posture integrations configured on the tailnet (latest snapshot per integration). Useful for compliance attestation - confirms what MDM/EDR systems are connected and what their status is. Compare against the expected baseline to detect drift. Requires Tailscale Premium or Enterprise. requiredDataConnectors: - connectorId: TailscalePremiumCCF diff --git a/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumTaggedServiceFanIn.yaml b/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumTaggedServiceFanIn.yaml index ec1e49bf559..23fc4a069ee 100644 --- a/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumTaggedServiceFanIn.yaml +++ b/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumTaggedServiceFanIn.yaml @@ -1,6 +1,7 @@ id: f8d4e7bc-3450-4c55-84ac-90e6e9c6b8fe name: "Tailscale Premium: Tagged services with broad inbound exposure" -description: | +description: Identifies tagged services (devices with non-empty DstTags) ranked by inbound diversity over 7 days. Surfaces services with the broadest blast-radius; ACL drift candidates. +description-detailed: | Identifies tagged services (devices with non-empty DstTags) ranked by inbound diversity over 7 days. Baseline: a tag:plex serving the household typically sees connections from 1-3 user devices; a tag:db or tag:internal that receives connections from 10+ distinct users or 3+ OS families may indicate ACL drift, credential sharing, or unauthorised access. Sort by DistinctSrcDevices to surface services with the broadest blast-radius. Requires Tailscale Premium or Enterprise. requiredDataConnectors: - connectorId: TailscalePremiumCCF diff --git a/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumUserMultiDevice.yaml b/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumUserMultiDevice.yaml index ee35cccb8ba..0a2df129d51 100644 --- a/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumUserMultiDevice.yaml +++ b/Solutions/Tailscale (CCF)/Hunting Queries/TailscalePremiumUserMultiDevice.yaml @@ -1,6 +1,7 @@ id: daac10bd-d842-4122-90cc-9957256f04e3 name: "Tailscale Premium: Users generating traffic from multiple devices" -description: | +description: Identifies users (SrcUser) generating tailnet flows from more than one distinct device in the past 24 hours. Useful for spotting account compromise (sudden new device) or unauthorised device enrollment. +description-detailed: | Identifies users (SrcUser) generating tailnet flows from more than one distinct device in the past 24 hours. Normal for a user with phone + laptop. Useful for spotting account compromise (sudden new device for a user), unauthorised device enrollment, or device sharing across users. Cross-reference NewDevicesToday against Tailscale_Devices_CL.Created to confirm whether each device is genuinely new vs long-known. Requires Tailscale Premium or Enterprise. requiredDataConnectors: - connectorId: TailscalePremiumCCF diff --git a/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleSplitDnsPerDomainChanges.yaml b/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleSplitDnsPerDomainChanges.yaml index 54afc73051b..e871bc93bfa 100644 --- a/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleSplitDnsPerDomainChanges.yaml +++ b/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleSplitDnsPerDomainChanges.yaml @@ -1,6 +1,7 @@ id: e7f8a9b0-3456-7890-12ab-cdef12345012 name: "Tailscale: Split-DNS per-domain change history" -description: | +description: Identifies the per-domain Split-DNS change history from the audit log. For each modification event, expands Old and New documents and surfaces which domains were added, removed, or changed. +description-detailed: | Identifies the per-domain Split-DNS change history from the audit log. For each modification event, expands the Old and New documents and surfaces which domains were added, removed, or had their resolver IPs changed. Useful for forensic review after a suspected DNS hijack and for confirming that a planned DNS migration deployed as intended. requiredDataConnectors: - connectorId: TailscaleCCF diff --git a/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleSubnetRouteExposure.yaml b/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleSubnetRouteExposure.yaml index 62bd5b7a989..fc31c96f864 100644 --- a/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleSubnetRouteExposure.yaml +++ b/Solutions/Tailscale (CCF)/Hunting Queries/TailscaleSubnetRouteExposure.yaml @@ -1,6 +1,7 @@ id: f6a7b8c9-0123-4567-8901-234567890047 name: "Tailscale: Subnet router CIDR exposure inventory" -description: | +description: Identifies every device currently advertising or enabling subnet routes (bridging non-tailnet networks into the tailnet). Excludes pure exit-node advertisements so only true subnet exposure is surfaced. +description-detailed: | Identifies every device currently advertising or enabling subnet routes (i.e., bridging non-tailnet networks into the tailnet). Excludes pure exit-node advertisements (0.0.0.0/0 and ::/0) so only true subnet exposure is surfaced. Use this for change-control - confirm every CIDR listed is intended to be reachable from the tailnet, and that the device's ACL restricts who can route through it. requiredDataConnectors: - connectorId: TailscaleCCF diff --git a/Solutions/Tailscale (CCF)/Package/3.0.0.zip b/Solutions/Tailscale (CCF)/Package/3.0.0.zip index e3742df4a100047c23401ee8e0f755d76e55a982..65656be69d01843d5d83344da05424f28e83b3db 100644 GIT binary patch delta 52139 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 delta 54895 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 diff --git a/Solutions/Tailscale (CCF)/Package/createUiDefinition.json b/Solutions/Tailscale (CCF)/Package/createUiDefinition.json index e2a77b70b7c..0084ccf6de7 100644 --- a/Solutions/Tailscale (CCF)/Package/createUiDefinition.json +++ b/Solutions/Tailscale (CCF)/Package/createUiDefinition.json @@ -197,7 +197,7 @@ "name": "analytic2-text", "type": "Microsoft.Common.TextBlock", "options": { - "text": "Detects creation of a Tailscale OAuth client or API access key whose granted scopes include WRITE permissions (anything matching \":write\"). Tokens with write scopes can modify tailnet configuration, manage devices, write ACLs, and revoke keys - high-value adversary targets. Compare against the recent actor history; revoke immediately if unexpected." + "text": "Identifies creation of a Tailscale OAuth client or API access key whose granted scopes include WRITE permissions (anything matching :write). Tokens with write scopes are high-value adversary targets." } } ] @@ -351,7 +351,7 @@ "name": "analytic13-text", "type": "Microsoft.Common.TextBlock", "options": { - "text": "Identifies tailnet devices with a non-empty TailnetLockError, indicating the device failed tailnet-lock cryptographic validation. Tailnet lock requires new node-keys be co-signed by trusted signers; errors here are the only direct signal of a node-key injection attempt." + "text": "Identifies tailnet devices with a non-empty TailnetLockError, indicating the device failed tailnet-lock cryptographic validation. Suspicious - likely an unsigned node attempting to join." } } ] @@ -379,7 +379,7 @@ "name": "analytic15-text", "type": "Microsoft.Common.TextBlock", "options": { - "text": "Identifies devices actively connected to the Tailscale control plane (ConnectedToControl=true) but not yet authorized by an admin (Authorized=false). With device approval enabled, persistent entries warrant review; without approval, persistence may indicate a node-key injection attempt." + "text": "Identifies devices actively connected to the Tailscale control plane (ConnectedToControl=true) but not yet authorized by an admin (Authorized=false). Often benign onboarding but can indicate rogue joins." } } ] @@ -407,7 +407,7 @@ "name": "analytic17-text", "type": "Microsoft.Common.TextBlock", "options": { - "text": "Identifies when a node sends traffic via an exit node not used in the prior 7-day baseline. First-seen exit destinations from a specific source may indicate unsanctioned egress, a compromised node pivoting, or a policy misconfiguration. Requires Tailscale Premium or Enterprise." + "text": "Identifies when a node sends traffic via an exit node not used in the prior 7-day baseline. First-seen exit destinations from a node may indicate routing-policy drift, data exfiltration, or compromise." } } ] @@ -477,7 +477,7 @@ "name": "analytic22-text", "type": "Microsoft.Common.TextBlock", "options": { - "text": "Identifies when a device-posture integration is disabled or removed from the tailnet. Posture integrations enforce device compliance; removing one disables that enforcement and increases blast radius for compromised endpoints. Requires Tailscale Premium or Enterprise." + "text": "Identifies when a device-posture integration is disabled or removed from the tailnet. Posture integrations enforce device compliance - removal weakens fleet posture and is a possible defense-evasion step." } } ] @@ -491,7 +491,7 @@ "name": "analytic23-text", "type": "Microsoft.Common.TextBlock", "options": { - "text": "Identifies when a new device-posture integration is added to the tailnet (Jamf, Kandji, Intune, Kolide, Defender for Endpoint, CrowdStrike, SentinelOne, etc.). Unexpected additions may indicate an attacker establishing a control plane or bypassing compliance gates. Requires Tailscale Premium or Enterprise." + "text": "Identifies when a new device-posture integration is added to the tailnet (Jamf, Kandji, Intune, Kolide, Defender for Endpoint, CrowdStrike, SentinelOne). Verify the addition was sanctioned." } } ] @@ -505,7 +505,7 @@ "name": "analytic24-text", "type": "Microsoft.Common.TextBlock", "options": { - "text": "Identifies when a source node has more than 75 percent of its recent flows falling back to a DERP relay (Tailscale's IsRelayed flag, traffic via 127.3.3.40). Sustained high relay rate indicates direct WireGuard peer-to-peer is failing - causes include NAT/firewall changes, a network blocking UDP 41641, or potential evasion attempts. Operational signal but useful for spotting policy drift. Requires Tailscale Premium or Enterprise." + "text": "Identifies when a source node has more than 75 percent of its recent flows falling back to a DERP relay (Tailscale IsRelayed flag, traffic via 127.3.3.40). Operational signal useful for spotting policy drift." } } ] @@ -557,7 +557,7 @@ "name": "huntingquery2-text", "type": "Microsoft.Common.TextBlock", "options": { - "text": "Identifies short windows where the tailnet ACL policy file was rewritten multiple times. Iterative policy edits during an incident can indicate rule-hunting by an attacker or a misconfiguration spiral; legitimate ACL refactors usually land as a single change. This hunting query depends on TailscaleCCF data connector (Tailscale_Audit_CL Parser or Table)" + "text": "Identifies short windows where the tailnet ACL policy file was rewritten multiple times. Iterative policy edits during an incident can indicate a defender adjusting rules or an attacker probing. This hunting query depends on TailscaleCCF data connector (Tailscale_Audit_CL Parser or Table)" } } ] @@ -571,7 +571,7 @@ "name": "huntingquery3-text", "type": "Microsoft.Common.TextBlock", "options": { - "text": "Identifies configuration audit events that occurred outside typical business hours (defined as Monday-Friday 08:00-18:00 UTC). Adjust the time window to match your operating region. Useful for spotting impromptu maintenance, account compromise, or insider activity. This hunting query depends on TailscaleCCF data connector (Tailscale_Audit_CL Parser or Table)" + "text": "Identifies configuration audit events that occurred outside typical business hours (Monday-Friday 08:00-18:00 UTC). Useful for spotting impromptu maintenance, account compromise, or insider activity. This hunting query depends on TailscaleCCF data connector (Tailscale_Audit_CL Parser or Table)" } } ] @@ -585,7 +585,7 @@ "name": "huntingquery4-text", "type": "Microsoft.Common.TextBlock", "options": { - "text": "Identifies actors creating multiple auth keys in a short window. A single admin creating many keys for unattended enrollment is normal during a rollout; the same pattern can also indicate token-spraying for persistence. Cross-reference with the Tailscale: Auth key created alert to filter context. This hunting query depends on TailscaleCCF data connector (Tailscale_Audit_CL Parser or Table)" + "text": "Identifies actors creating multiple auth keys in a short window. A single admin creating many keys for unattended enrollment is normal during a rollout; same pattern can also indicate token-spraying. This hunting query depends on TailscaleCCF data connector (Tailscale_Audit_CL Parser or Table)" } } ] @@ -599,7 +599,7 @@ "name": "huntingquery5-text", "type": "Microsoft.Common.TextBlock", "options": { - "text": "Identifies tailnet devices that have not connected to the control plane for at least 30 days. Dormant devices accumulate risk - they may still have valid keys, advertised routes, or tags but no operational oversight. Candidate for removal or key rotation. This hunting query depends on TailscaleCCF data connector (Tailscale_Devices_CL Parser or Table)" + "text": "Identifies tailnet devices that have not connected to the control plane for at least 30 days. Dormant devices accumulate risk - they may still have valid keys, advertised routes, or tags. This hunting query depends on TailscaleCCF data connector (Tailscale_Devices_CL Parser or Table)" } } ] @@ -627,7 +627,7 @@ "name": "huntingquery7-text", "type": "Microsoft.Common.TextBlock", "options": { - "text": "Identifies tailnet users who have no devices currently registered. Orphaned identities are candidates for off-boarding - they retain whatever role/permissions they were granted and can still create auth keys or invite others. Review against your HR / identity-provider join-leaver records. This hunting query depends on TailscaleCCF data connector (Tailscale_Users_CL Parser or Table)" + "text": "Identifies tailnet users who have no devices currently registered. Orphaned identities are candidates for off-boarding - they retain whatever role/permissions they were granted. This hunting query depends on TailscaleCCF data connector (Tailscale_Users_CL Parser or Table)" } } ] @@ -641,7 +641,7 @@ "name": "huntingquery8-text", "type": "Microsoft.Common.TextBlock", "options": { - "text": "Identifies the per-domain Split-DNS change history from the audit log. For each modification event, expands the Old and New documents and surfaces which domains were added, removed, or had their resolver IPs changed. Useful for forensic review after a suspected DNS hijack and for confirming that a planned DNS migration deployed as intended. This hunting query depends on TailscaleCCF data connector (Tailscale_Audit_CL Parser or Table)" + "text": "Identifies the per-domain Split-DNS change history from the audit log. For each modification event, expands Old and New documents and surfaces which domains were added, removed, or changed. This hunting query depends on TailscaleCCF data connector (Tailscale_Audit_CL Parser or Table)" } } ] @@ -655,7 +655,7 @@ "name": "huntingquery9-text", "type": "Microsoft.Common.TextBlock", "options": { - "text": "Identifies devices that currently have Tailscale SSH enabled. Tailscale SSH delivers SSH access over the tailnet using Tailscale identity (no SSH keys needed) and is governed by the SSH ACL block in the policy file. Cross-reference this list with the SSH ACL to confirm only the intended devices are reachable as SSH targets. This hunting query depends on TailscaleCCF data connector (Tailscale_Devices_CL Parser or Table)" + "text": "Identifies devices that currently have Tailscale SSH enabled. Tailscale SSH delivers SSH access over the tailnet using Tailscale identity and is governed by the SSH ACL block in the policy file. This hunting query depends on TailscaleCCF data connector (Tailscale_Devices_CL Parser or Table)" } } ] @@ -669,7 +669,7 @@ "name": "huntingquery10-text", "type": "Microsoft.Common.TextBlock", "options": { - "text": "Identifies external (shared-in) devices currently active in the tailnet. These devices belong to another tailnet and have been admitted via a Tailscale sharing arrangement. Confirm each entry maps to a documented collaboration and that the corresponding ACL restricts the device to the intended resources. This hunting query depends on TailscaleCCF data connector (Tailscale_Devices_CL Parser or Table)" + "text": "Identifies external (shared-in) devices currently active in the tailnet. These devices belong to another tailnet and have been admitted via a Tailscale sharing arrangement. This hunting query depends on TailscaleCCF data connector (Tailscale_Devices_CL Parser or Table)" } } ] @@ -683,7 +683,7 @@ "name": "huntingquery11-text", "type": "Microsoft.Common.TextBlock", "options": { - "text": "Identifies tailnet devices that report UpdateAvailable=true on the latest snapshot. Tailscale releases security and feature updates regularly; a device showing UpdateAvailable lacks the most recent improvements. Use this list to plan a fleet update, especially before enabling new ACL features that require minimum client versions. This hunting query depends on TailscaleCCF data connector (Tailscale_Devices_CL Parser or Table)" + "text": "Identifies tailnet devices that report UpdateAvailable=true on the latest snapshot. Tailscale releases security updates regularly; outdated clients lack the most recent improvements. This hunting query depends on TailscaleCCF data connector (Tailscale_Devices_CL Parser or Table)" } } ] @@ -697,7 +697,7 @@ "name": "huntingquery12-text", "type": "Microsoft.Common.TextBlock", "options": { - "text": "Identifies every device currently advertising or enabling subnet routes (i.e., bridging non-tailnet networks into the tailnet). Excludes pure exit-node advertisements (0.0.0.0/0 and ::/0) so only true subnet exposure is surfaced. Use this for change-control - confirm every CIDR listed is intended to be reachable from the tailnet, and that the device's ACL restricts who can route through it. This hunting query depends on TailscaleCCF data connector (Tailscale_Devices_CL Parser or Table)" + "text": "Identifies every device currently advertising or enabling subnet routes (bridging non-tailnet networks into the tailnet). Excludes pure exit-node advertisements so only true subnet exposure is surfaced. This hunting query depends on TailscaleCCF data connector (Tailscale_Devices_CL Parser or Table)" } } ] @@ -711,7 +711,7 @@ "name": "huntingquery13-text", "type": "Microsoft.Common.TextBlock", "options": { - "text": "Identifies tailnet src->dst pairs observed in the last 24h that were NOT observed in the prior 7-day baseline. Useful for spotting lateral movement to nodes that don't usually talk to each other. Requires Tailscale Premium or Enterprise (network flow logs). This hunting query depends on TailscalePremiumCCF data connector (Tailscale_Network_CL Parser or Table)" + "text": "Identifies tailnet src->dst pairs observed in the last 24h that were NOT observed in the prior 7-day baseline. Useful for spotting lateral movement to nodes that don't usually talk. This hunting query depends on TailscalePremiumCCF data connector (Tailscale_Network_CL Parser or Table)" } } ] @@ -739,7 +739,7 @@ "name": "huntingquery15-text", "type": "Microsoft.Common.TextBlock", "options": { - "text": "Identifies traffic leaving the tailnet via exit nodes. Exit node use is typically intentional (regional egress, privacy routing) but unexpected exit-node traffic from a node should be investigated as a potential pivot point or unsanctioned egress. Requires Tailscale Premium or Enterprise. This hunting query depends on TailscalePremiumCCF data connector (Tailscale_Network_CL Parser or Table)" + "text": "Identifies traffic leaving the tailnet via exit nodes. Exit-node use is typically intentional (regional egress, privacy routing) but unexpected egress from a node warrants investigation. This hunting query depends on TailscalePremiumCCF data connector (Tailscale_Network_CL Parser or Table)" } } ] @@ -753,7 +753,7 @@ "name": "huntingquery16-text", "type": "Microsoft.Common.TextBlock", "options": { - "text": "Identifies flows that recur at a highly regular interval, which is the signature of C2 beaconing or scheduled exfiltration jobs. Looks for src->dst pairs where 80%+ of inter-flow gaps cluster around the same delta. Requires Tailscale Premium or Enterprise. This hunting query depends on TailscalePremiumCCF data connector (Tailscale_Network_CL Parser or Table)" + "text": "Identifies flows that recur at a highly regular interval, which is the signature of C2 beaconing or scheduled exfiltration jobs. Looser threshold than the analytic rule - investigation aid. This hunting query depends on TailscalePremiumCCF data connector (Tailscale_Network_CL Parser or Table)" } } ] @@ -767,7 +767,7 @@ "name": "huntingquery17-text", "type": "Microsoft.Common.TextBlock", "options": { - "text": "Identifies the current set of device-posture integrations configured on the tailnet (latest snapshot per integration). Useful for compliance attestation - confirms what MDM/EDR systems are connected and what their status is. Compare against the expected baseline to detect drift. Requires Tailscale Premium or Enterprise. This hunting query depends on TailscalePremiumCCF data connector (Tailscale_PostureIntegrations_CL Parser or Table)" + "text": "Identifies the current set of device-posture integrations configured on the tailnet (latest snapshot per integration). Useful for compliance attestation and detecting drift from the expected baseline. This hunting query depends on TailscalePremiumCCF data connector (Tailscale_PostureIntegrations_CL Parser or Table)" } } ] @@ -781,7 +781,7 @@ "name": "huntingquery18-text", "type": "Microsoft.Common.TextBlock", "options": { - "text": "Identifies devices that have consistently fallen back to DERP relay (IsRelayed = true) over the past 24 hours. Sustained relay usage points to NAT/firewall misconfiguration on the device's network, a tunnel-blocking middlebox, or in rare cases deliberate evasion attempting to obscure direct peer-to-peer paths. Useful for proactive network-hygiene investigation and capacity planning. Requires Tailscale Premium or Enterprise. This hunting query depends on TailscalePremiumCCF data connector (Tailscale_Network_CL Parser or Table)" + "text": "Identifies devices that have consistently fallen back to DERP relay (IsRelayed=true) over the past 24 hours. Sustained relay usage points to NAT/firewall misconfiguration or deliberate evasion. This hunting query depends on TailscalePremiumCCF data connector (Tailscale_Network_CL Parser or Table)" } } ] @@ -795,7 +795,7 @@ "name": "huntingquery19-text", "type": "Microsoft.Common.TextBlock", "options": { - "text": "Identifies tagged services (devices with non-empty DstTags) ranked by inbound diversity over 7 days. Baseline: a tag:plex serving the household typically sees connections from 1-3 user devices; a tag:db or tag:internal that receives connections from 10+ distinct users or 3+ OS families may indicate ACL drift, credential sharing, or unauthorised access. Sort by DistinctSrcDevices to surface services with the broadest blast-radius. Requires Tailscale Premium or Enterprise. This hunting query depends on TailscalePremiumCCF data connector (Tailscale_Network_CL Parser or Table)" + "text": "Identifies tagged services (devices with non-empty DstTags) ranked by inbound diversity over 7 days. Surfaces services with the broadest blast-radius; ACL drift candidates. This hunting query depends on TailscalePremiumCCF data connector (Tailscale_Network_CL Parser or Table)" } } ] @@ -809,7 +809,7 @@ "name": "huntingquery20-text", "type": "Microsoft.Common.TextBlock", "options": { - "text": "Identifies network flows pivoted by source-tag x destination-tag over the past 7 days, treating untagged user devices as ``. Highlights which tagged services interact - useful for ACL validation, detecting unexpected tag-to-tag traffic, and spotting tag-to-same-tag loops that can indicate worm-style propagation or service-mesh anomalies. Order by Flows to find the heaviest tag pairs; sort by DistinctSrcDevices to find broadly-used services. Requires Tailscale Premium or Enterprise. This hunting query depends on TailscalePremiumCCF data connector (Tailscale_Network_CL Parser or Table)" + "text": "Identifies network flows pivoted by source-tag x destination-tag over 7 days. Highlights tag-to-tag traffic, useful for ACL validation. Same-tag loops can signal worm-style propagation. This hunting query depends on TailscalePremiumCCF data connector (Tailscale_Network_CL Parser or Table)" } } ] @@ -823,7 +823,7 @@ "name": "huntingquery21-text", "type": "Microsoft.Common.TextBlock", "options": { - "text": "Identifies network flows occurring outside 07:00-19:00 UTC on weekdays, plus all of weekend, over the past 7 days. Filters to virtual/subnet/exit traffic (drops DERP-only keepalive noise). Useful for spotting unattended automation gone wrong, scheduled exfiltration, or unsanctioned after-hours access by humans. The TaggedSource column makes it easy to separate cron-like service traffic (tag:backup, tag:cron) from human user activity. Requires Tailscale Premium or Enterprise. This hunting query depends on TailscalePremiumCCF data connector (Tailscale_Network_CL Parser or Table)" + "text": "Identifies network flows occurring outside 07:00-19:00 UTC on weekdays plus all weekend, over 7 days. Filters to virtual/subnet/exit traffic. Useful for spotting unattended automation gone wrong. This hunting query depends on TailscalePremiumCCF data connector (Tailscale_Network_CL Parser or Table)" } } ] @@ -837,7 +837,7 @@ "name": "huntingquery22-text", "type": "Microsoft.Common.TextBlock", "options": { - "text": "Identifies users (SrcUser) generating tailnet flows from more than one distinct device in the past 24 hours. Normal for a user with phone + laptop. Useful for spotting account compromise (sudden new device for a user), unauthorised device enrollment, or device sharing across users. Cross-reference NewDevicesToday against Tailscale_Devices_CL.Created to confirm whether each device is genuinely new vs long-known. Requires Tailscale Premium or Enterprise. This hunting query depends on TailscalePremiumCCF data connector (Tailscale_Network_CL Tailscale_Devices_CL Parser or Table)" + "text": "Identifies users (SrcUser) generating tailnet flows from more than one distinct device in the past 24 hours. Useful for spotting account compromise (sudden new device) or unauthorised device enrollment. This hunting query depends on TailscalePremiumCCF data connector (Tailscale_Network_CL Tailscale_Devices_CL Parser or Table)" } } ] diff --git a/Solutions/Tailscale (CCF)/Package/mainTemplate.json b/Solutions/Tailscale (CCF)/Package/mainTemplate.json index 25afd872bd4..24050d808cc 100644 --- a/Solutions/Tailscale (CCF)/Package/mainTemplate.json +++ b/Solutions/Tailscale (CCF)/Package/mainTemplate.json @@ -4597,10 +4597,10 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscaleCCF", "dataTypes": [ "Tailscale_Audit_CL" - ] + ], + "connectorId": "TailscaleCCF" } ], "tactics": [ @@ -4613,24 +4613,24 @@ ], "entityMappings": [ { - "entityType": "Account", "fieldMappings": [ { "columnName": "ActorLogin", "identifier": "FullName" } - ] + ], + "entityType": "Account" } ], "incidentConfiguration": { + "createIncident": true, "groupingConfiguration": { - "lookbackDuration": "5h", "enabled": true, - "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "groupByEntities": [] - }, - "createIncident": true + "groupByEntities": [], + "lookbackDuration": "5h", + "reopenClosedIncident": false + } } } }, @@ -4699,7 +4699,7 @@ "kind": "Scheduled", "location": "[parameters('workspace-location')]", "properties": { - "description": "Detects creation of a Tailscale OAuth client or API access key whose granted scopes include WRITE permissions (anything matching \":write\"). Tokens with write scopes can modify tailnet configuration, manage devices, write ACLs, and revoke keys - high-value adversary targets. Compare against the recent actor history; revoke immediately if unexpected.", + "description": "Identifies creation of a Tailscale OAuth client or API access key whose granted scopes include WRITE permissions (anything matching :write). Tokens with write scopes are high-value adversary targets.", "displayName": "Tailscale: OAuth client or API key created with write scopes", "enabled": false, "query": "Tailscale_Audit_CL\n| where EventType == \"CONFIG\"\n| where Action == \"CREATE\"\n| where tostring(Target.type) in (\"API_KEY\", \"OAUTH_CLIENT\")\n| extend Scopes = extract(@\"scopes\\s*-\\s*(.+)$\", 1, ActionDetails)\n| where Scopes contains \":write\"\n| extend WriteScopes = extract_all(@\"([a-zA-Z_]+:write)\", Scopes)\n| extend ActorLogin = tostring(Actor.loginName)\n| extend ActorType = tostring(Actor.type)\n| extend TargetName = tostring(Target.name)\n| extend TargetId = tostring(Target.id)\n| extend TargetType = tostring(Target.type)\n| project TimeGenerated, ActorLogin, ActorType, Action, TargetType, TargetName, TargetId, WriteScopes, Scopes, Origin, ActionDetails\n", @@ -4713,16 +4713,16 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscaleCCF", "dataTypes": [ "Tailscale_Audit_CL" - ] + ], + "connectorId": "TailscaleCCF" }, { - "connectorId": "TailscalePremiumCCF", "dataTypes": [ "Tailscale_Audit_CL" - ] + ], + "connectorId": "TailscalePremiumCCF" } ], "tactics": [ @@ -4735,24 +4735,24 @@ ], "entityMappings": [ { - "entityType": "Account", "fieldMappings": [ { "columnName": "ActorLogin", "identifier": "FullName" } - ] + ], + "entityType": "Account" } ], "incidentConfiguration": { + "createIncident": true, "groupingConfiguration": { - "lookbackDuration": "PT5H", "enabled": true, - "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "groupByEntities": [] - }, - "createIncident": true + "groupByEntities": [], + "lookbackDuration": "5h", + "reopenClosedIncident": false + } } } }, @@ -4835,10 +4835,10 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscaleCCF", "dataTypes": [ "Tailscale_Audit_CL" - ] + ], + "connectorId": "TailscaleCCF" } ], "tactics": [ @@ -4850,24 +4850,24 @@ ], "entityMappings": [ { - "entityType": "Account", "fieldMappings": [ { "columnName": "ActorLogin", "identifier": "FullName" } - ] + ], + "entityType": "Account" } ], "incidentConfiguration": { + "createIncident": true, "groupingConfiguration": { - "lookbackDuration": "5h", "enabled": true, - "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "groupByEntities": [] - }, - "createIncident": true + "groupByEntities": [], + "lookbackDuration": "5h", + "reopenClosedIncident": false + } } } }, @@ -4950,10 +4950,10 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscaleCCF", "dataTypes": [ "Tailscale_Audit_CL" - ] + ], + "connectorId": "TailscaleCCF" } ], "tactics": [ @@ -4964,24 +4964,24 @@ ], "entityMappings": [ { - "entityType": "Account", "fieldMappings": [ { "columnName": "ActorLogin", "identifier": "FullName" } - ] + ], + "entityType": "Account" } ], "incidentConfiguration": { + "createIncident": true, "groupingConfiguration": { - "lookbackDuration": "5h", "enabled": true, - "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "groupByEntities": [] - }, - "createIncident": true + "groupByEntities": [], + "lookbackDuration": "5h", + "reopenClosedIncident": false + } } } }, @@ -5064,10 +5064,10 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscaleCCF", "dataTypes": [ "Tailscale_Audit_CL" - ] + ], + "connectorId": "TailscaleCCF" } ], "tactics": [ @@ -5079,33 +5079,33 @@ ], "entityMappings": [ { - "entityType": "Account", "fieldMappings": [ { "columnName": "ActorLogin", "identifier": "FullName" } - ] + ], + "entityType": "Account" }, { - "entityType": "Host", "fieldMappings": [ { "columnName": "NodeName", "identifier": "HostName" } - ] + ], + "entityType": "Host" } ], "incidentConfiguration": { + "createIncident": true, "groupingConfiguration": { - "lookbackDuration": "5h", "enabled": true, - "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "groupByEntities": [] - }, - "createIncident": true + "groupByEntities": [], + "lookbackDuration": "5h", + "reopenClosedIncident": false + } } } }, @@ -5188,10 +5188,10 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscaleCCF", "dataTypes": [ "Tailscale_Audit_CL" - ] + ], + "connectorId": "TailscaleCCF" } ], "tactics": [ @@ -5203,24 +5203,24 @@ ], "entityMappings": [ { - "entityType": "Account", "fieldMappings": [ { "columnName": "ActorLogin", "identifier": "FullName" } - ] + ], + "entityType": "Account" } ], "incidentConfiguration": { + "createIncident": true, "groupingConfiguration": { - "lookbackDuration": "5h", "enabled": true, - "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "groupByEntities": [] - }, - "createIncident": true + "groupByEntities": [], + "lookbackDuration": "5h", + "reopenClosedIncident": false + } } } }, @@ -5303,10 +5303,10 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscaleCCF", "dataTypes": [ "Tailscale_Devices_CL" - ] + ], + "connectorId": "TailscaleCCF" } ], "tactics": [ @@ -5317,33 +5317,33 @@ ], "entityMappings": [ { - "entityType": "Host", "fieldMappings": [ { "columnName": "Hostname", "identifier": "HostName" } - ] + ], + "entityType": "Host" }, { - "entityType": "Account", "fieldMappings": [ { "columnName": "User", "identifier": "FullName" } - ] + ], + "entityType": "Account" } ], "incidentConfiguration": { + "createIncident": true, "groupingConfiguration": { - "lookbackDuration": "1d", "enabled": true, - "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "groupByEntities": [] - }, - "createIncident": true + "groupByEntities": [], + "lookbackDuration": "1d", + "reopenClosedIncident": false + } } } }, @@ -5426,10 +5426,10 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscaleCCF", "dataTypes": [ "Tailscale_Devices_CL" - ] + ], + "connectorId": "TailscaleCCF" } ], "tactics": [ @@ -5442,33 +5442,33 @@ ], "entityMappings": [ { - "entityType": "Host", "fieldMappings": [ { "columnName": "Hostname", "identifier": "HostName" } - ] + ], + "entityType": "Host" }, { - "entityType": "Account", "fieldMappings": [ { "columnName": "User", "identifier": "FullName" } - ] + ], + "entityType": "Account" } ], "incidentConfiguration": { + "createIncident": true, "groupingConfiguration": { - "lookbackDuration": "1d", "enabled": true, - "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "groupByEntities": [] - }, - "createIncident": true + "groupByEntities": [], + "lookbackDuration": "1d", + "reopenClosedIncident": false + } } } }, @@ -5551,10 +5551,10 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscaleCCF", "dataTypes": [ "Tailscale_Users_CL" - ] + ], + "connectorId": "TailscaleCCF" } ], "tactics": [ @@ -5567,24 +5567,24 @@ ], "entityMappings": [ { - "entityType": "Account", "fieldMappings": [ { "columnName": "LoginName", "identifier": "FullName" } - ] + ], + "entityType": "Account" } ], "incidentConfiguration": { + "createIncident": true, "groupingConfiguration": { - "lookbackDuration": "1d", "enabled": true, - "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "groupByEntities": [] - }, - "createIncident": true + "groupByEntities": [], + "lookbackDuration": "1d", + "reopenClosedIncident": false + } } } }, @@ -5667,10 +5667,10 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscaleCCF", "dataTypes": [ "Tailscale_Audit_CL" - ] + ], + "connectorId": "TailscaleCCF" } ], "tactics": [ @@ -5683,24 +5683,24 @@ ], "entityMappings": [ { - "entityType": "Account", "fieldMappings": [ { "columnName": "ActorLogin", "identifier": "FullName" } - ] + ], + "entityType": "Account" } ], "incidentConfiguration": { + "createIncident": true, "groupingConfiguration": { - "lookbackDuration": "1d", "enabled": true, - "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "groupByEntities": [] - }, - "createIncident": true + "groupByEntities": [], + "lookbackDuration": "1d", + "reopenClosedIncident": false + } } } }, @@ -5783,10 +5783,10 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscaleCCF", "dataTypes": [ "Tailscale_Audit_CL" - ] + ], + "connectorId": "TailscaleCCF" } ], "tactics": [ @@ -5799,24 +5799,24 @@ ], "entityMappings": [ { - "entityType": "Account", "fieldMappings": [ { "columnName": "ActorLogin", "identifier": "FullName" } - ] + ], + "entityType": "Account" } ], "incidentConfiguration": { + "createIncident": true, "groupingConfiguration": { - "lookbackDuration": "1d", "enabled": true, - "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "groupByEntities": [] - }, - "createIncident": true + "groupByEntities": [], + "lookbackDuration": "1d", + "reopenClosedIncident": false + } } } }, @@ -5899,10 +5899,10 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscaleCCF", "dataTypes": [ "Tailscale_Audit_CL" - ] + ], + "connectorId": "TailscaleCCF" } ], "tactics": [ @@ -5913,24 +5913,24 @@ ], "entityMappings": [ { - "entityType": "Account", "fieldMappings": [ { "columnName": "ActorLogin", "identifier": "FullName" } - ] + ], + "entityType": "Account" } ], "incidentConfiguration": { + "createIncident": true, "groupingConfiguration": { - "lookbackDuration": "1d", "enabled": true, - "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "groupByEntities": [] - }, - "createIncident": true + "groupByEntities": [], + "lookbackDuration": "1d", + "reopenClosedIncident": false + } } } }, @@ -5999,7 +5999,7 @@ "kind": "Scheduled", "location": "[parameters('workspace-location')]", "properties": { - "description": "Identifies tailnet devices with a non-empty TailnetLockError, indicating the device failed tailnet-lock cryptographic validation. Tailnet lock requires new node-keys be co-signed by trusted signers; errors here are the only direct signal of a node-key injection attempt.", + "description": "Identifies tailnet devices with a non-empty TailnetLockError, indicating the device failed tailnet-lock cryptographic validation. Suspicious - likely an unsigned node attempting to join.", "displayName": "Tailscale: Tailnet lock validation failed", "enabled": false, "query": "Tailscale_Devices_CL\n| where TimeGenerated > ago(1h)\n| summarize arg_max(TimeGenerated, *) by DeviceId\n| where isnotempty(TailnetLockError)\n| project TimeGenerated, DeviceName, Hostname, User, Os, ClientVersion, TailnetLockError, TailnetLockKey, LastSeen, Authorized\n", @@ -6013,10 +6013,10 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscaleCCF", "dataTypes": [ "Tailscale_Devices_CL" - ] + ], + "connectorId": "TailscaleCCF" } ], "tactics": [ @@ -6029,33 +6029,33 @@ ], "entityMappings": [ { - "entityType": "Host", "fieldMappings": [ { "columnName": "Hostname", "identifier": "HostName" } - ] + ], + "entityType": "Host" }, { - "entityType": "Account", "fieldMappings": [ { "columnName": "User", "identifier": "FullName" } - ] + ], + "entityType": "Account" } ], "incidentConfiguration": { + "createIncident": true, "groupingConfiguration": { - "lookbackDuration": "1d", "enabled": true, - "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "groupByEntities": [] - }, - "createIncident": true + "groupByEntities": [], + "lookbackDuration": "1d", + "reopenClosedIncident": false + } } } }, @@ -6138,10 +6138,10 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscaleCCF", "dataTypes": [ "Tailscale_Devices_CL" - ] + ], + "connectorId": "TailscaleCCF" } ], "tactics": [ @@ -6154,33 +6154,33 @@ ], "entityMappings": [ { - "entityType": "Host", "fieldMappings": [ { "columnName": "Hostname", "identifier": "HostName" } - ] + ], + "entityType": "Host" }, { - "entityType": "Account", "fieldMappings": [ { "columnName": "User", "identifier": "FullName" } - ] + ], + "entityType": "Account" } ], "incidentConfiguration": { + "createIncident": true, "groupingConfiguration": { - "lookbackDuration": "1d", "enabled": true, - "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "groupByEntities": [] - }, - "createIncident": true + "groupByEntities": [], + "lookbackDuration": "1d", + "reopenClosedIncident": false + } } } }, @@ -6249,7 +6249,7 @@ "kind": "Scheduled", "location": "[parameters('workspace-location')]", "properties": { - "description": "Identifies devices actively connected to the Tailscale control plane (ConnectedToControl=true) but not yet authorized by an admin (Authorized=false). With device approval enabled, persistent entries warrant review; without approval, persistence may indicate a node-key injection attempt.", + "description": "Identifies devices actively connected to the Tailscale control plane (ConnectedToControl=true) but not yet authorized by an admin (Authorized=false). Often benign onboarding but can indicate rogue joins.", "displayName": "Tailscale: Unauthorized device connected to control plane", "enabled": false, "query": "Tailscale_Devices_CL\n| where TimeGenerated > ago(1h)\n| summarize arg_max(TimeGenerated, *) by DeviceId\n| where Authorized == false and ConnectedToControl == true\n| project TimeGenerated, DeviceName, Hostname, User, Os, ClientVersion, Created, LastSeen, MachineKey, NodeKey\n", @@ -6263,10 +6263,10 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscaleCCF", "dataTypes": [ "Tailscale_Devices_CL" - ] + ], + "connectorId": "TailscaleCCF" } ], "tactics": [ @@ -6279,33 +6279,33 @@ ], "entityMappings": [ { - "entityType": "Host", "fieldMappings": [ { "columnName": "Hostname", "identifier": "HostName" } - ] + ], + "entityType": "Host" }, { - "entityType": "Account", "fieldMappings": [ { "columnName": "User", "identifier": "FullName" } - ] + ], + "entityType": "Account" } ], "incidentConfiguration": { + "createIncident": true, "groupingConfiguration": { - "lookbackDuration": "1d", "enabled": true, - "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "groupByEntities": [] - }, - "createIncident": true + "groupByEntities": [], + "lookbackDuration": "1d", + "reopenClosedIncident": false + } } } }, @@ -6388,10 +6388,10 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscaleCCF", "dataTypes": [ "Tailscale_Devices_CL" - ] + ], + "connectorId": "TailscaleCCF" } ], "tactics": [ @@ -6402,33 +6402,33 @@ ], "entityMappings": [ { - "entityType": "Host", "fieldMappings": [ { "columnName": "Hostname", "identifier": "HostName" } - ] + ], + "entityType": "Host" }, { - "entityType": "Account", "fieldMappings": [ { "columnName": "User", "identifier": "FullName" } - ] + ], + "entityType": "Account" } ], "incidentConfiguration": { + "createIncident": true, "groupingConfiguration": { - "lookbackDuration": "1d", "enabled": true, - "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "groupByEntities": [] - }, - "createIncident": true + "groupByEntities": [], + "lookbackDuration": "1d", + "reopenClosedIncident": false + } } } }, @@ -6497,7 +6497,7 @@ "kind": "Scheduled", "location": "[parameters('workspace-location')]", "properties": { - "description": "Identifies when a node sends traffic via an exit node not used in the prior 7-day baseline. First-seen exit destinations from a specific source may indicate unsanctioned egress, a compromised node pivoting, or a policy misconfiguration. Requires Tailscale Premium or Enterprise.", + "description": "Identifies when a node sends traffic via an exit node not used in the prior 7-day baseline. First-seen exit destinations from a node may indicate routing-policy drift, data exfiltration, or compromise.", "displayName": "Tailscale Premium: Unexpected exit-node egress", "enabled": false, "query": "let baseline = 7d;\nlet recent = 1h;\nlet recentEgress =\n Tailscale_Network_CL\n | where TimeGenerated > ago(recent)\n | where HasExitTraffic\n | mv-expand t = ExitTraffic\n | extend Src = tostring(t.src), ExitDst = tostring(t.dst), Bytes = tolong(t.txBytes) + tolong(t.rxBytes)\n | summarize FirstSeen = min(TimeGenerated), TotalBytes = sum(Bytes) by NodeId, SrcNodeName, SrcUser, SrcOs, Src, ExitDst;\nlet baselineEgress =\n Tailscale_Network_CL\n | where TimeGenerated between (ago(baseline + recent) .. ago(recent))\n | where HasExitTraffic\n | mv-expand t = ExitTraffic\n | extend Src = tostring(t.src), ExitDst = tostring(t.dst)\n | distinct NodeId, Src, ExitDst;\nrecentEgress\n| join kind=leftanti baselineEgress on NodeId, Src, ExitDst\n| extend TotalMB = round(TotalBytes / 1024.0 / 1024.0, 2)\n", @@ -6511,10 +6511,10 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscalePremiumCCF", "dataTypes": [ "Tailscale_Network_CL" - ] + ], + "connectorId": "TailscalePremiumCCF" } ], "tactics": [ @@ -6527,42 +6527,42 @@ ], "entityMappings": [ { - "entityType": "Host", "fieldMappings": [ { "columnName": "SrcNodeName", "identifier": "HostName" } - ] + ], + "entityType": "Host" }, { - "entityType": "Account", "fieldMappings": [ { "columnName": "SrcUser", "identifier": "FullName" } - ] + ], + "entityType": "Account" }, { - "entityType": "IP", "fieldMappings": [ { "columnName": "Src", "identifier": "Address" } - ] + ], + "entityType": "IP" } ], "incidentConfiguration": { + "createIncident": true, "groupingConfiguration": { - "lookbackDuration": "5h", "enabled": true, - "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "groupByEntities": [] - }, - "createIncident": true + "groupByEntities": [], + "lookbackDuration": "5h", + "reopenClosedIncident": false + } } } }, @@ -6645,10 +6645,10 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscalePremiumCCF", "dataTypes": [ "Tailscale_Network_CL" - ] + ], + "connectorId": "TailscalePremiumCCF" } ], "tactics": [ @@ -6661,51 +6661,51 @@ ], "entityMappings": [ { - "entityType": "Host", "fieldMappings": [ { "columnName": "SrcNodeName", "identifier": "HostName" } - ] + ], + "entityType": "Host" }, { - "entityType": "Host", "fieldMappings": [ { "columnName": "DstNodeName", "identifier": "HostName" } - ] + ], + "entityType": "Host" }, { - "entityType": "Account", "fieldMappings": [ { "columnName": "SrcUser", "identifier": "FullName" } - ] + ], + "entityType": "Account" }, { - "entityType": "IP", "fieldMappings": [ { "columnName": "Src", "identifier": "Address" } - ] + ], + "entityType": "IP" } ], "incidentConfiguration": { + "createIncident": true, "groupingConfiguration": { - "lookbackDuration": "5h", "enabled": true, - "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "groupByEntities": [] - }, - "createIncident": true + "groupByEntities": [], + "lookbackDuration": "5h", + "reopenClosedIncident": false + } } } }, @@ -6788,10 +6788,10 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscalePremiumCCF", "dataTypes": [ "Tailscale_Network_CL" - ] + ], + "connectorId": "TailscalePremiumCCF" } ], "tactics": [ @@ -6805,51 +6805,51 @@ ], "entityMappings": [ { - "entityType": "Host", "fieldMappings": [ { "columnName": "SrcNodeName", "identifier": "HostName" } - ] + ], + "entityType": "Host" }, { - "entityType": "Host", "fieldMappings": [ { "columnName": "DstNodeName", "identifier": "HostName" } - ] + ], + "entityType": "Host" }, { - "entityType": "Account", "fieldMappings": [ { "columnName": "SrcUser", "identifier": "FullName" } - ] + ], + "entityType": "Account" }, { - "entityType": "IP", "fieldMappings": [ { "columnName": "Src", "identifier": "Address" } - ] + ], + "entityType": "IP" } ], "incidentConfiguration": { + "createIncident": true, "groupingConfiguration": { - "lookbackDuration": "1d", "enabled": true, - "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "groupByEntities": [] - }, - "createIncident": true + "groupByEntities": [], + "lookbackDuration": "1d", + "reopenClosedIncident": false + } } } }, @@ -6932,10 +6932,10 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscalePremiumCCF", "dataTypes": [ "Tailscale_Network_CL" - ] + ], + "connectorId": "TailscalePremiumCCF" } ], "tactics": [ @@ -6949,42 +6949,42 @@ ], "entityMappings": [ { - "entityType": "Host", "fieldMappings": [ { "columnName": "SrcNodeName", "identifier": "HostName" } - ] + ], + "entityType": "Host" }, { - "entityType": "Account", "fieldMappings": [ { "columnName": "SrcUser", "identifier": "FullName" } - ] + ], + "entityType": "Account" }, { - "entityType": "IP", "fieldMappings": [ { "columnName": "Src", "identifier": "Address" } - ] + ], + "entityType": "IP" } ], "incidentConfiguration": { + "createIncident": true, "groupingConfiguration": { - "lookbackDuration": "5h", "enabled": true, - "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "groupByEntities": [] - }, - "createIncident": true + "groupByEntities": [], + "lookbackDuration": "5h", + "reopenClosedIncident": false + } } } }, @@ -7067,10 +7067,10 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscalePremiumCCF", "dataTypes": [ "Tailscale_Network_CL" - ] + ], + "connectorId": "TailscalePremiumCCF" } ], "tactics": [ @@ -7083,33 +7083,33 @@ ], "entityMappings": [ { - "entityType": "Host", "fieldMappings": [ { "columnName": "SrcNodeName", "identifier": "HostName" } - ] + ], + "entityType": "Host" }, { - "entityType": "Account", "fieldMappings": [ { "columnName": "SrcUser", "identifier": "FullName" } - ] + ], + "entityType": "Account" } ], "incidentConfiguration": { + "createIncident": true, "groupingConfiguration": { - "lookbackDuration": "1d", "enabled": true, - "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "groupByEntities": [] - }, - "createIncident": true + "groupByEntities": [], + "lookbackDuration": "1d", + "reopenClosedIncident": false + } } } }, @@ -7178,7 +7178,7 @@ "kind": "Scheduled", "location": "[parameters('workspace-location')]", "properties": { - "description": "Identifies when a device-posture integration is disabled or removed from the tailnet. Posture integrations enforce device compliance; removing one disables that enforcement and increases blast radius for compromised endpoints. Requires Tailscale Premium or Enterprise.", + "description": "Identifies when a device-posture integration is disabled or removed from the tailnet. Posture integrations enforce device compliance - removal weakens fleet posture and is a possible defense-evasion step.", "displayName": "Tailscale Premium: Posture integration disabled or removed", "enabled": false, "query": "Tailscale_Audit_CL\n| where Action in (\"DELETE\", \"UPDATE\")\n| where tostring(Target.type) == \"POSTURE_INTEGRATION\"\n or tostring(Target.type) startswith \"POSTURE\"\n| extend ActorLogin = tostring(Actor.loginName)\n| extend Provider = tostring(Target.name)\n| project TimeGenerated, ActorLogin, Action, Provider, Target, Old, New, Origin\n", @@ -7192,10 +7192,10 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscalePremiumCCF", "dataTypes": [ "Tailscale_Audit_CL" - ] + ], + "connectorId": "TailscalePremiumCCF" } ], "tactics": [ @@ -7208,24 +7208,24 @@ ], "entityMappings": [ { - "entityType": "Account", "fieldMappings": [ { "columnName": "ActorLogin", "identifier": "FullName" } - ] + ], + "entityType": "Account" } ], "incidentConfiguration": { + "createIncident": true, "groupingConfiguration": { - "lookbackDuration": "1d", "enabled": true, - "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "groupByEntities": [] - }, - "createIncident": true + "groupByEntities": [], + "lookbackDuration": "1d", + "reopenClosedIncident": false + } } } }, @@ -7294,7 +7294,7 @@ "kind": "Scheduled", "location": "[parameters('workspace-location')]", "properties": { - "description": "Identifies when a new device-posture integration is added to the tailnet (Jamf, Kandji, Intune, Kolide, Defender for Endpoint, CrowdStrike, SentinelOne, etc.). Unexpected additions may indicate an attacker establishing a control plane or bypassing compliance gates. Requires Tailscale Premium or Enterprise.", + "description": "Identifies when a new device-posture integration is added to the tailnet (Jamf, Kandji, Intune, Kolide, Defender for Endpoint, CrowdStrike, SentinelOne). Verify the addition was sanctioned.", "displayName": "Tailscale Premium: New posture integration added", "enabled": false, "query": "Tailscale_Audit_CL\n| where Action == \"CREATE\"\n| where tostring(Target.type) == \"POSTURE_INTEGRATION\"\n or tostring(Target.type) startswith \"POSTURE\"\n| extend ActorLogin = tostring(Actor.loginName)\n| extend Provider = tostring(Target.name)\n| project TimeGenerated, ActorLogin, Provider, Target, New, Origin\n", @@ -7308,10 +7308,10 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscalePremiumCCF", "dataTypes": [ "Tailscale_Audit_CL" - ] + ], + "connectorId": "TailscalePremiumCCF" } ], "tactics": [ @@ -7322,24 +7322,24 @@ ], "entityMappings": [ { - "entityType": "Account", "fieldMappings": [ { "columnName": "ActorLogin", "identifier": "FullName" } - ] + ], + "entityType": "Account" } ], "incidentConfiguration": { + "createIncident": true, "groupingConfiguration": { - "lookbackDuration": "1d", "enabled": true, - "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "groupByEntities": [] - }, - "createIncident": true + "groupByEntities": [], + "lookbackDuration": "1d", + "reopenClosedIncident": false + } } } }, @@ -7408,7 +7408,7 @@ "kind": "Scheduled", "location": "[parameters('workspace-location')]", "properties": { - "description": "Identifies when a source node has more than 75 percent of its recent flows falling back to a DERP relay (Tailscale's IsRelayed flag, traffic via 127.3.3.40). Sustained high relay rate indicates direct WireGuard peer-to-peer is failing - causes include NAT/firewall changes, a network blocking UDP 41641, or potential evasion attempts. Operational signal but useful for spotting policy drift. Requires Tailscale Premium or Enterprise.", + "description": "Identifies when a source node has more than 75 percent of its recent flows falling back to a DERP relay (Tailscale IsRelayed flag, traffic via 127.3.3.40). Operational signal useful for spotting policy drift.", "displayName": "Tailscale Premium: DERP relay traffic surge", "enabled": false, "query": "let minFlows = 20;\nlet relayedPctThreshold = 75.0;\nTailscale_Network_CL\n| where TimeGenerated > ago(15m)\n| summarize\n TotalFlows = count(),\n RelayedFlows = countif(IsRelayed)\n by SrcNodeName, SrcUser, SrcOs, SrcTags=tostring(SrcTags)\n| where TotalFlows >= minFlows\n| extend RelayedPct = round(100.0 * RelayedFlows / TotalFlows, 1)\n| where RelayedPct > relayedPctThreshold\n| project SrcNodeName, SrcUser, SrcOs, SrcTags, TotalFlows, RelayedFlows, RelayedPct\n| order by RelayedPct desc\n", @@ -7422,10 +7422,10 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "TailscalePremiumCCF", "dataTypes": [ "Tailscale_Network_CL" - ] + ], + "connectorId": "TailscalePremiumCCF" } ], "tactics": [ @@ -7436,33 +7436,33 @@ ], "entityMappings": [ { - "entityType": "Host", "fieldMappings": [ { "columnName": "SrcNodeName", "identifier": "HostName" } - ] + ], + "entityType": "Host" }, { - "entityType": "Account", "fieldMappings": [ { "columnName": "SrcUser", "identifier": "FullName" } - ] + ], + "entityType": "Account" } ], "incidentConfiguration": { + "createIncident": true, "groupingConfiguration": { - "lookbackDuration": "PT6H", "enabled": true, - "reopenClosedIncident": false, "matchingMethod": "AllEntities", - "groupByEntities": [] - }, - "createIncident": true + "groupByEntities": [], + "lookbackDuration": "6h", + "reopenClosedIncident": false + } } } }, @@ -7623,7 +7623,7 @@ "tags": [ { "name": "description", - "value": "Identifies short windows where the tailnet ACL policy file was rewritten multiple times. Iterative policy edits during an incident can indicate rule-hunting by an attacker or a misconfiguration spiral; legitimate ACL refactors usually land as a single change." + "value": "Identifies short windows where the tailnet ACL policy file was rewritten multiple times. Iterative policy edits during an incident can indicate a defender adjusting rules or an attacker probing." }, { "name": "tactics", @@ -7708,7 +7708,7 @@ "tags": [ { "name": "description", - "value": "Identifies configuration audit events that occurred outside typical business hours (defined as Monday-Friday 08:00-18:00 UTC). Adjust the time window to match your operating region. Useful for spotting impromptu maintenance, account compromise, or insider activity." + "value": "Identifies configuration audit events that occurred outside typical business hours (Monday-Friday 08:00-18:00 UTC). Useful for spotting impromptu maintenance, account compromise, or insider activity." }, { "name": "tactics", @@ -7793,7 +7793,7 @@ "tags": [ { "name": "description", - "value": "Identifies actors creating multiple auth keys in a short window. A single admin creating many keys for unattended enrollment is normal during a rollout; the same pattern can also indicate token-spraying for persistence. Cross-reference with the Tailscale: Auth key created alert to filter context." + "value": "Identifies actors creating multiple auth keys in a short window. A single admin creating many keys for unattended enrollment is normal during a rollout; same pattern can also indicate token-spraying." }, { "name": "tactics", @@ -7878,7 +7878,7 @@ "tags": [ { "name": "description", - "value": "Identifies tailnet devices that have not connected to the control plane for at least 30 days. Dormant devices accumulate risk - they may still have valid keys, advertised routes, or tags but no operational oversight. Candidate for removal or key rotation." + "value": "Identifies tailnet devices that have not connected to the control plane for at least 30 days. Dormant devices accumulate risk - they may still have valid keys, advertised routes, or tags." }, { "name": "tactics", @@ -8048,7 +8048,7 @@ "tags": [ { "name": "description", - "value": "Identifies tailnet users who have no devices currently registered. Orphaned identities are candidates for off-boarding - they retain whatever role/permissions they were granted and can still create auth keys or invite others. Review against your HR / identity-provider join-leaver records." + "value": "Identifies tailnet users who have no devices currently registered. Orphaned identities are candidates for off-boarding - they retain whatever role/permissions they were granted." }, { "name": "tactics", @@ -8133,7 +8133,7 @@ "tags": [ { "name": "description", - "value": "Identifies the per-domain Split-DNS change history from the audit log. For each modification event, expands the Old and New documents and surfaces which domains were added, removed, or had their resolver IPs changed. Useful for forensic review after a suspected DNS hijack and for confirming that a planned DNS migration deployed as intended." + "value": "Identifies the per-domain Split-DNS change history from the audit log. For each modification event, expands Old and New documents and surfaces which domains were added, removed, or changed." }, { "name": "tactics", @@ -8218,7 +8218,7 @@ "tags": [ { "name": "description", - "value": "Identifies devices that currently have Tailscale SSH enabled. Tailscale SSH delivers SSH access over the tailnet using Tailscale identity (no SSH keys needed) and is governed by the SSH ACL block in the policy file. Cross-reference this list with the SSH ACL to confirm only the intended devices are reachable as SSH targets." + "value": "Identifies devices that currently have Tailscale SSH enabled. Tailscale SSH delivers SSH access over the tailnet using Tailscale identity and is governed by the SSH ACL block in the policy file." }, { "name": "tactics", @@ -8303,7 +8303,7 @@ "tags": [ { "name": "description", - "value": "Identifies external (shared-in) devices currently active in the tailnet. These devices belong to another tailnet and have been admitted via a Tailscale sharing arrangement. Confirm each entry maps to a documented collaboration and that the corresponding ACL restricts the device to the intended resources." + "value": "Identifies external (shared-in) devices currently active in the tailnet. These devices belong to another tailnet and have been admitted via a Tailscale sharing arrangement." }, { "name": "tactics", @@ -8388,7 +8388,7 @@ "tags": [ { "name": "description", - "value": "Identifies tailnet devices that report UpdateAvailable=true on the latest snapshot. Tailscale releases security and feature updates regularly; a device showing UpdateAvailable lacks the most recent improvements. Use this list to plan a fleet update, especially before enabling new ACL features that require minimum client versions." + "value": "Identifies tailnet devices that report UpdateAvailable=true on the latest snapshot. Tailscale releases security updates regularly; outdated clients lack the most recent improvements." }, { "name": "tactics", @@ -8473,7 +8473,7 @@ "tags": [ { "name": "description", - "value": "Identifies every device currently advertising or enabling subnet routes (i.e., bridging non-tailnet networks into the tailnet). Excludes pure exit-node advertisements (0.0.0.0/0 and ::/0) so only true subnet exposure is surfaced. Use this for change-control - confirm every CIDR listed is intended to be reachable from the tailnet, and that the device's ACL restricts who can route through it." + "value": "Identifies every device currently advertising or enabling subnet routes (bridging non-tailnet networks into the tailnet). Excludes pure exit-node advertisements so only true subnet exposure is surfaced." }, { "name": "tactics", @@ -8558,7 +8558,7 @@ "tags": [ { "name": "description", - "value": "Identifies tailnet src->dst pairs observed in the last 24h that were NOT observed in the prior 7-day baseline. Useful for spotting lateral movement to nodes that don't usually talk to each other. Requires Tailscale Premium or Enterprise (network flow logs)." + "value": "Identifies tailnet src->dst pairs observed in the last 24h that were NOT observed in the prior 7-day baseline. Useful for spotting lateral movement to nodes that don't usually talk." }, { "name": "tactics", @@ -8728,7 +8728,7 @@ "tags": [ { "name": "description", - "value": "Identifies traffic leaving the tailnet via exit nodes. Exit node use is typically intentional (regional egress, privacy routing) but unexpected exit-node traffic from a node should be investigated as a potential pivot point or unsanctioned egress. Requires Tailscale Premium or Enterprise." + "value": "Identifies traffic leaving the tailnet via exit nodes. Exit-node use is typically intentional (regional egress, privacy routing) but unexpected egress from a node warrants investigation." }, { "name": "tactics", @@ -8813,7 +8813,7 @@ "tags": [ { "name": "description", - "value": "Identifies flows that recur at a highly regular interval, which is the signature of C2 beaconing or scheduled exfiltration jobs. Looks for src->dst pairs where 80%+ of inter-flow gaps cluster around the same delta. Requires Tailscale Premium or Enterprise." + "value": "Identifies flows that recur at a highly regular interval, which is the signature of C2 beaconing or scheduled exfiltration jobs. Looser threshold than the analytic rule - investigation aid." }, { "name": "tactics", @@ -8898,7 +8898,7 @@ "tags": [ { "name": "description", - "value": "Identifies the current set of device-posture integrations configured on the tailnet (latest snapshot per integration). Useful for compliance attestation - confirms what MDM/EDR systems are connected and what their status is. Compare against the expected baseline to detect drift. Requires Tailscale Premium or Enterprise." + "value": "Identifies the current set of device-posture integrations configured on the tailnet (latest snapshot per integration). Useful for compliance attestation and detecting drift from the expected baseline." }, { "name": "tactics", @@ -8983,7 +8983,7 @@ "tags": [ { "name": "description", - "value": "Identifies devices that have consistently fallen back to DERP relay (IsRelayed = true) over the past 24 hours. Sustained relay usage points to NAT/firewall misconfiguration on the device's network, a tunnel-blocking middlebox, or in rare cases deliberate evasion attempting to obscure direct peer-to-peer paths. Useful for proactive network-hygiene investigation and capacity planning. Requires Tailscale Premium or Enterprise." + "value": "Identifies devices that have consistently fallen back to DERP relay (IsRelayed=true) over the past 24 hours. Sustained relay usage points to NAT/firewall misconfiguration or deliberate evasion." }, { "name": "tactics", @@ -9068,7 +9068,7 @@ "tags": [ { "name": "description", - "value": "Identifies tagged services (devices with non-empty DstTags) ranked by inbound diversity over 7 days. Baseline: a tag:plex serving the household typically sees connections from 1-3 user devices; a tag:db or tag:internal that receives connections from 10+ distinct users or 3+ OS families may indicate ACL drift, credential sharing, or unauthorised access. Sort by DistinctSrcDevices to surface services with the broadest blast-radius. Requires Tailscale Premium or Enterprise." + "value": "Identifies tagged services (devices with non-empty DstTags) ranked by inbound diversity over 7 days. Surfaces services with the broadest blast-radius; ACL drift candidates." }, { "name": "tactics", @@ -9153,7 +9153,7 @@ "tags": [ { "name": "description", - "value": "Identifies network flows pivoted by source-tag x destination-tag over the past 7 days, treating untagged user devices as ``. Highlights which tagged services interact - useful for ACL validation, detecting unexpected tag-to-tag traffic, and spotting tag-to-same-tag loops that can indicate worm-style propagation or service-mesh anomalies. Order by Flows to find the heaviest tag pairs; sort by DistinctSrcDevices to find broadly-used services. Requires Tailscale Premium or Enterprise." + "value": "Identifies network flows pivoted by source-tag x destination-tag over 7 days. Highlights tag-to-tag traffic, useful for ACL validation. Same-tag loops can signal worm-style propagation." }, { "name": "tactics", @@ -9238,7 +9238,7 @@ "tags": [ { "name": "description", - "value": "Identifies network flows occurring outside 07:00-19:00 UTC on weekdays, plus all of weekend, over the past 7 days. Filters to virtual/subnet/exit traffic (drops DERP-only keepalive noise). Useful for spotting unattended automation gone wrong, scheduled exfiltration, or unsanctioned after-hours access by humans. The TaggedSource column makes it easy to separate cron-like service traffic (tag:backup, tag:cron) from human user activity. Requires Tailscale Premium or Enterprise." + "value": "Identifies network flows occurring outside 07:00-19:00 UTC on weekdays plus all weekend, over 7 days. Filters to virtual/subnet/exit traffic. Useful for spotting unattended automation gone wrong." }, { "name": "tactics", @@ -9323,7 +9323,7 @@ "tags": [ { "name": "description", - "value": "Identifies users (SrcUser) generating tailnet flows from more than one distinct device in the past 24 hours. Normal for a user with phone + laptop. Useful for spotting account compromise (sudden new device for a user), unauthorised device enrollment, or device sharing across users. Cross-reference NewDevicesToday against Tailscale_Devices_CL.Created to confirm whether each device is genuinely new vs long-known. Requires Tailscale Premium or Enterprise." + "value": "Identifies users (SrcUser) generating tailnet flows from more than one distinct device in the past 24 hours. Useful for spotting account compromise (sudden new device) or unauthorised device enrollment." }, { "name": "tactics", @@ -9416,7 +9416,7 @@ "apiVersion": "2022-01-01-preview", "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('Workbook-', last(split(variables('workbookId1'),'/'))))]", "properties": { - "description": "@{workbookKey=TailscaleStandardOperationsWorkbook; logoFileName=Tailscale.svg; description=Tailscale Operations workbook for Standard tier. Nine tabs covering an at-a-glance KPI hero row, audit activity overview, an actor + device drilldown (Investigate), embedded hunting queries (first-seen actors, off-hours changes, key-expiry-disabled devices, never-expire auth keys, outdated clients, dormant devices, subnet route exposure, SSH-enabled devices), identity (user roles, status, last login recency, role escalation history, orphaned users), devices (OS / version / tag distribution, devices needing attention, full inventory, subnet routers), credentials (expiry timeline, never-expire flag, CRUD events), admin audit (action heatmap, actor x action heatmap, recent 100), network and DNS (current snapshot, tailnet policy gates, ACL change history), and pipeline health (per-table freshness, ingest rate, operational events). Driven by data polled from the Tailscale REST API.; dataTypesDependencies=System.Object[]; dataConnectorsDependencies=System.Object[]; previewImagesFileNames=System.Object[]; version=1.0.0; title=Tailscale Operations (Standard); templateRelativePath=TailscaleStandardOperations.json; subtitle=; provider=Community; support=; source=; categories=}.description", + "description": "@{workbookKey=TailscaleStandardOperationsWorkbook; logoFileName=Tailscale.svg; description=Tailscale Operations workbook for Standard tier. Nine tabs covering an at-a-glance KPI hero row, audit activity overview, an actor + device drilldown (Investigate), embedded hunting queries (first-seen actors, off-hours changes, key-expiry-disabled devices, never-expire auth keys, outdated clients, dormant devices, subnet route exposure, SSH-enabled devices), identity (user roles, status, last login recency, role escalation history, orphaned users), devices (OS / version / tag distribution, devices needing attention, full inventory, subnet routers), credentials (expiry timeline, never-expire flag, CRUD events), admin audit (action heatmap, actor x action heatmap, recent 100), network and DNS (current snapshot, tailnet policy gates, ACL change history), and pipeline health (per-table freshness, ingest rate, operational events). Driven by data polled from the Tailscale REST API.; dataTypesDependencies=System.Object[]; dataConnectorsDependencies=System.Object[]; previewImagesFileNames=System.Object[]; version=1.0.0; title=Tailscale Operations (Standard); templateRelativePath=TailscaleStandardOperations.json; subtitle=; provider=Community; support=; source=; categories=; author=}.description", "parentId": "[variables('workbookId1')]", "contentId": "[variables('_workbookContentId1')]", "kind": "Workbook", @@ -9528,7 +9528,7 @@ "apiVersion": "2022-01-01-preview", "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('Workbook-', last(split(variables('workbookId2'),'/'))))]", "properties": { - "description": "@{workbookKey=TailscalePremiumOperationsWorkbook; logoFileName=Tailscale.svg; description=Tailscale Operations workbook for Premium / Enterprise tier - everything in the Standard workbook plus network flow analysis (top talkers, src-dst pairs, exit-node egress, beaconing candidates) and posture integration inventory.; dataTypesDependencies=System.Object[]; dataConnectorsDependencies=System.Object[]; previewImagesFileNames=System.Object[]; version=1.0.0; title=Tailscale Operations (Premium); templateRelativePath=TailscalePremiumOperations.json; subtitle=; provider=Community; support=; source=; categories=}.description", + "description": "@{workbookKey=TailscalePremiumOperationsWorkbook; logoFileName=Tailscale.svg; description=Tailscale Operations workbook for Premium / Enterprise tier - everything in the Standard workbook plus network flow analysis (top talkers, src-dst pairs, exit-node egress, beaconing candidates) and posture integration inventory.; dataTypesDependencies=System.Object[]; dataConnectorsDependencies=System.Object[]; previewImagesFileNames=System.Object[]; version=1.0.0; title=Tailscale Operations (Premium); templateRelativePath=TailscalePremiumOperations.json; subtitle=; provider=Community; support=; source=; categories=; author=}.description", "parentId": "[variables('workbookId2')]", "contentId": "[variables('_workbookContentId2')]", "kind": "Workbook", diff --git a/Solutions/Tailscale (CCF)/Parsers/ASimNetworkSessionTailscale.yaml b/Solutions/Tailscale (CCF)/Parsers/ASimNetworkSessionTailscale.yaml index ac12904b67e..11677937446 100644 --- a/Solutions/Tailscale (CCF)/Parsers/ASimNetworkSessionTailscale.yaml +++ b/Solutions/Tailscale (CCF)/Parsers/ASimNetworkSessionTailscale.yaml @@ -1,4 +1,4 @@ -id: 1e9af7d2-4b5a-5067-ad9e-2b3c4d5e6f70 +id: 2b9c1f5a-7d3e-4f8b-a456-c1d2e3f4a5b6 Function: Title: ASIM Network Session parser for Tailscale (no-prefix wrapper) Version: '1.0.0' diff --git a/Solutions/Tailscale (CCF)/Tailscale.svg b/Solutions/Tailscale (CCF)/Tailscale.svg deleted file mode 100644 index f3418621b91..00000000000 --- a/Solutions/Tailscale (CCF)/Tailscale.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/Workbooks/WorkbooksMetadata.json b/Workbooks/WorkbooksMetadata.json index a115de24564..5e23a612b73 100644 --- a/Workbooks/WorkbooksMetadata.json +++ b/Workbooks/WorkbooksMetadata.json @@ -10747,6 +10747,9 @@ "Security - Network", "Identity" ] + }, + "author": { + "name": "noodlemctwoodle" } }, { @@ -10786,6 +10789,9 @@ "Security - Network", "Identity" ] + }, + "author": { + "name": "noodlemctwoodle" } } ] From 74a07e68c49efa4d3b846451fa12964bac49611e Mon Sep 17 00:00:00 2001 From: noodlemctwoodle Date: Wed, 17 Jun 2026 09:47:02 +0100 Subject: [PATCH 27/27] fix(tailscale): resolve PR #14482 CI build failures (KQL + logo) Three upstream CI checks failed on commit c0089a19; addressed: KqlValidations - vimNetworkSessionTailscale.yaml KS006/005/198 at byte 1367. The case() expression used the function form startswith(x, y) which KQL does not have - startswith is a comparison operator. Switched both occurrences (Src and Dst host extraction) to operator form: `SrcRawHost startswith "["` and `DstRawHost startswith "["`. Same correction applied to ASimNetworkSessionTailscale.yaml. KqlValidations - ASimNetworkSessionTailscale.yaml KS204 "vimNetworkSessionTailscale does not refer to any known table, tabular variable or function". The wrapper previously had a one-line body that called the vim parser by name, but the KQL validator processes each parser file independently with no registry of other custom functions in the same solution. Replaced the wrapper body with a verbatim copy of the vim parser's KQL so it is self-contained. Both parsers now stand on their own. logoValidator - Logos/Tailscale.svg "Id should be GUID format and uniquely identifiable". The SVG carried `id="Layer_1"` (Adobe export default). Other upstream community logos (UnifiSiteManager.svg, CrowdStrikeFalconHost.svg, etc.) carry no `id` attribute at all - removed `id="Layer_1"` to match that pattern. The third failure (run-arm-ttk) was an infrastructure issue on the upstream runner ("no space left on device" mid Docker build) - not a content problem, will pass on the next push. Verified locally: KQL PASS (56 files), ARM-TTK 48/48 PASS, logoValidator not run locally but SVG now matches the canonical upstream community shape. --- Logos/Tailscale.svg | 2 +- Solutions/Tailscale (CCF)/Package/3.0.0.zip | Bin 77207 -> 77290 bytes .../Tailscale (CCF)/Package/mainTemplate.json | 544 +++++++++--------- .../Parsers/ASimNetworkSessionTailscale.yaml | 88 ++- .../Parsers/vimNetworkSessionTailscale.yaml | 4 +- 5 files changed, 362 insertions(+), 276 deletions(-) diff --git a/Logos/Tailscale.svg b/Logos/Tailscale.svg index f3418621b91..fdd0d65fbae 100644 --- a/Logos/Tailscale.svg +++ b/Logos/Tailscale.svg @@ -1 +1 @@ - + diff --git a/Solutions/Tailscale (CCF)/Package/3.0.0.zip b/Solutions/Tailscale (CCF)/Package/3.0.0.zip index 65656be69d01843d5d83344da05424f28e83b3db..2e07c0f245d92f4a0c3b9d3e1ef8ed3671f90ba1 100644 GIT binary patch delta 68071 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 diff --git a/Solutions/Tailscale (CCF)/Package/mainTemplate.json b/Solutions/Tailscale (CCF)/Package/mainTemplate.json index 24050d808cc..5f88745db6f 100644 --- a/Solutions/Tailscale (CCF)/Package/mainTemplate.json +++ b/Solutions/Tailscale (CCF)/Package/mainTemplate.json @@ -4613,23 +4613,23 @@ ], "entityMappings": [ { + "entityType": "Account", "fieldMappings": [ { - "columnName": "ActorLogin", - "identifier": "FullName" + "identifier": "FullName", + "columnName": "ActorLogin" } - ], - "entityType": "Account" + ] } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { - "enabled": true, - "matchingMethod": "AllEntities", - "groupByEntities": [], "lookbackDuration": "5h", - "reopenClosedIncident": false + "groupByEntities": [], + "matchingMethod": "AllEntities", + "reopenClosedIncident": false, + "enabled": true } } } @@ -4735,23 +4735,23 @@ ], "entityMappings": [ { + "entityType": "Account", "fieldMappings": [ { - "columnName": "ActorLogin", - "identifier": "FullName" + "identifier": "FullName", + "columnName": "ActorLogin" } - ], - "entityType": "Account" + ] } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { - "enabled": true, - "matchingMethod": "AllEntities", - "groupByEntities": [], "lookbackDuration": "5h", - "reopenClosedIncident": false + "groupByEntities": [], + "matchingMethod": "AllEntities", + "reopenClosedIncident": false, + "enabled": true } } } @@ -4850,23 +4850,23 @@ ], "entityMappings": [ { + "entityType": "Account", "fieldMappings": [ { - "columnName": "ActorLogin", - "identifier": "FullName" + "identifier": "FullName", + "columnName": "ActorLogin" } - ], - "entityType": "Account" + ] } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { - "enabled": true, - "matchingMethod": "AllEntities", - "groupByEntities": [], "lookbackDuration": "5h", - "reopenClosedIncident": false + "groupByEntities": [], + "matchingMethod": "AllEntities", + "reopenClosedIncident": false, + "enabled": true } } } @@ -4964,23 +4964,23 @@ ], "entityMappings": [ { + "entityType": "Account", "fieldMappings": [ { - "columnName": "ActorLogin", - "identifier": "FullName" + "identifier": "FullName", + "columnName": "ActorLogin" } - ], - "entityType": "Account" + ] } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { - "enabled": true, - "matchingMethod": "AllEntities", - "groupByEntities": [], "lookbackDuration": "5h", - "reopenClosedIncident": false + "groupByEntities": [], + "matchingMethod": "AllEntities", + "reopenClosedIncident": false, + "enabled": true } } } @@ -5079,32 +5079,32 @@ ], "entityMappings": [ { + "entityType": "Account", "fieldMappings": [ { - "columnName": "ActorLogin", - "identifier": "FullName" + "identifier": "FullName", + "columnName": "ActorLogin" } - ], - "entityType": "Account" + ] }, { + "entityType": "Host", "fieldMappings": [ { - "columnName": "NodeName", - "identifier": "HostName" + "identifier": "HostName", + "columnName": "NodeName" } - ], - "entityType": "Host" + ] } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { - "enabled": true, - "matchingMethod": "AllEntities", - "groupByEntities": [], "lookbackDuration": "5h", - "reopenClosedIncident": false + "groupByEntities": [], + "matchingMethod": "AllEntities", + "reopenClosedIncident": false, + "enabled": true } } } @@ -5203,23 +5203,23 @@ ], "entityMappings": [ { + "entityType": "Account", "fieldMappings": [ { - "columnName": "ActorLogin", - "identifier": "FullName" + "identifier": "FullName", + "columnName": "ActorLogin" } - ], - "entityType": "Account" + ] } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { - "enabled": true, - "matchingMethod": "AllEntities", - "groupByEntities": [], "lookbackDuration": "5h", - "reopenClosedIncident": false + "groupByEntities": [], + "matchingMethod": "AllEntities", + "reopenClosedIncident": false, + "enabled": true } } } @@ -5317,32 +5317,32 @@ ], "entityMappings": [ { + "entityType": "Host", "fieldMappings": [ { - "columnName": "Hostname", - "identifier": "HostName" + "identifier": "HostName", + "columnName": "Hostname" } - ], - "entityType": "Host" + ] }, { + "entityType": "Account", "fieldMappings": [ { - "columnName": "User", - "identifier": "FullName" + "identifier": "FullName", + "columnName": "User" } - ], - "entityType": "Account" + ] } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { - "enabled": true, - "matchingMethod": "AllEntities", - "groupByEntities": [], "lookbackDuration": "1d", - "reopenClosedIncident": false + "groupByEntities": [], + "matchingMethod": "AllEntities", + "reopenClosedIncident": false, + "enabled": true } } } @@ -5442,32 +5442,32 @@ ], "entityMappings": [ { + "entityType": "Host", "fieldMappings": [ { - "columnName": "Hostname", - "identifier": "HostName" + "identifier": "HostName", + "columnName": "Hostname" } - ], - "entityType": "Host" + ] }, { + "entityType": "Account", "fieldMappings": [ { - "columnName": "User", - "identifier": "FullName" + "identifier": "FullName", + "columnName": "User" } - ], - "entityType": "Account" + ] } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { - "enabled": true, - "matchingMethod": "AllEntities", - "groupByEntities": [], "lookbackDuration": "1d", - "reopenClosedIncident": false + "groupByEntities": [], + "matchingMethod": "AllEntities", + "reopenClosedIncident": false, + "enabled": true } } } @@ -5567,23 +5567,23 @@ ], "entityMappings": [ { + "entityType": "Account", "fieldMappings": [ { - "columnName": "LoginName", - "identifier": "FullName" + "identifier": "FullName", + "columnName": "LoginName" } - ], - "entityType": "Account" + ] } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { - "enabled": true, - "matchingMethod": "AllEntities", - "groupByEntities": [], "lookbackDuration": "1d", - "reopenClosedIncident": false + "groupByEntities": [], + "matchingMethod": "AllEntities", + "reopenClosedIncident": false, + "enabled": true } } } @@ -5683,23 +5683,23 @@ ], "entityMappings": [ { + "entityType": "Account", "fieldMappings": [ { - "columnName": "ActorLogin", - "identifier": "FullName" + "identifier": "FullName", + "columnName": "ActorLogin" } - ], - "entityType": "Account" + ] } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { - "enabled": true, - "matchingMethod": "AllEntities", - "groupByEntities": [], "lookbackDuration": "1d", - "reopenClosedIncident": false + "groupByEntities": [], + "matchingMethod": "AllEntities", + "reopenClosedIncident": false, + "enabled": true } } } @@ -5799,23 +5799,23 @@ ], "entityMappings": [ { + "entityType": "Account", "fieldMappings": [ { - "columnName": "ActorLogin", - "identifier": "FullName" + "identifier": "FullName", + "columnName": "ActorLogin" } - ], - "entityType": "Account" + ] } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { - "enabled": true, - "matchingMethod": "AllEntities", - "groupByEntities": [], "lookbackDuration": "1d", - "reopenClosedIncident": false + "groupByEntities": [], + "matchingMethod": "AllEntities", + "reopenClosedIncident": false, + "enabled": true } } } @@ -5913,23 +5913,23 @@ ], "entityMappings": [ { + "entityType": "Account", "fieldMappings": [ { - "columnName": "ActorLogin", - "identifier": "FullName" + "identifier": "FullName", + "columnName": "ActorLogin" } - ], - "entityType": "Account" + ] } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { - "enabled": true, - "matchingMethod": "AllEntities", - "groupByEntities": [], "lookbackDuration": "1d", - "reopenClosedIncident": false + "groupByEntities": [], + "matchingMethod": "AllEntities", + "reopenClosedIncident": false, + "enabled": true } } } @@ -6029,32 +6029,32 @@ ], "entityMappings": [ { + "entityType": "Host", "fieldMappings": [ { - "columnName": "Hostname", - "identifier": "HostName" + "identifier": "HostName", + "columnName": "Hostname" } - ], - "entityType": "Host" + ] }, { + "entityType": "Account", "fieldMappings": [ { - "columnName": "User", - "identifier": "FullName" + "identifier": "FullName", + "columnName": "User" } - ], - "entityType": "Account" + ] } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { - "enabled": true, - "matchingMethod": "AllEntities", - "groupByEntities": [], "lookbackDuration": "1d", - "reopenClosedIncident": false + "groupByEntities": [], + "matchingMethod": "AllEntities", + "reopenClosedIncident": false, + "enabled": true } } } @@ -6154,32 +6154,32 @@ ], "entityMappings": [ { + "entityType": "Host", "fieldMappings": [ { - "columnName": "Hostname", - "identifier": "HostName" + "identifier": "HostName", + "columnName": "Hostname" } - ], - "entityType": "Host" + ] }, { + "entityType": "Account", "fieldMappings": [ { - "columnName": "User", - "identifier": "FullName" + "identifier": "FullName", + "columnName": "User" } - ], - "entityType": "Account" + ] } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { - "enabled": true, - "matchingMethod": "AllEntities", - "groupByEntities": [], "lookbackDuration": "1d", - "reopenClosedIncident": false + "groupByEntities": [], + "matchingMethod": "AllEntities", + "reopenClosedIncident": false, + "enabled": true } } } @@ -6279,32 +6279,32 @@ ], "entityMappings": [ { + "entityType": "Host", "fieldMappings": [ { - "columnName": "Hostname", - "identifier": "HostName" + "identifier": "HostName", + "columnName": "Hostname" } - ], - "entityType": "Host" + ] }, { + "entityType": "Account", "fieldMappings": [ { - "columnName": "User", - "identifier": "FullName" + "identifier": "FullName", + "columnName": "User" } - ], - "entityType": "Account" + ] } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { - "enabled": true, - "matchingMethod": "AllEntities", - "groupByEntities": [], "lookbackDuration": "1d", - "reopenClosedIncident": false + "groupByEntities": [], + "matchingMethod": "AllEntities", + "reopenClosedIncident": false, + "enabled": true } } } @@ -6402,32 +6402,32 @@ ], "entityMappings": [ { + "entityType": "Host", "fieldMappings": [ { - "columnName": "Hostname", - "identifier": "HostName" + "identifier": "HostName", + "columnName": "Hostname" } - ], - "entityType": "Host" + ] }, { + "entityType": "Account", "fieldMappings": [ { - "columnName": "User", - "identifier": "FullName" + "identifier": "FullName", + "columnName": "User" } - ], - "entityType": "Account" + ] } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { - "enabled": true, - "matchingMethod": "AllEntities", - "groupByEntities": [], "lookbackDuration": "1d", - "reopenClosedIncident": false + "groupByEntities": [], + "matchingMethod": "AllEntities", + "reopenClosedIncident": false, + "enabled": true } } } @@ -6527,41 +6527,41 @@ ], "entityMappings": [ { + "entityType": "Host", "fieldMappings": [ { - "columnName": "SrcNodeName", - "identifier": "HostName" + "identifier": "HostName", + "columnName": "SrcNodeName" } - ], - "entityType": "Host" + ] }, { + "entityType": "Account", "fieldMappings": [ { - "columnName": "SrcUser", - "identifier": "FullName" + "identifier": "FullName", + "columnName": "SrcUser" } - ], - "entityType": "Account" + ] }, { + "entityType": "IP", "fieldMappings": [ { - "columnName": "Src", - "identifier": "Address" + "identifier": "Address", + "columnName": "Src" } - ], - "entityType": "IP" + ] } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { - "enabled": true, - "matchingMethod": "AllEntities", - "groupByEntities": [], "lookbackDuration": "5h", - "reopenClosedIncident": false + "groupByEntities": [], + "matchingMethod": "AllEntities", + "reopenClosedIncident": false, + "enabled": true } } } @@ -6661,50 +6661,50 @@ ], "entityMappings": [ { + "entityType": "Host", "fieldMappings": [ { - "columnName": "SrcNodeName", - "identifier": "HostName" + "identifier": "HostName", + "columnName": "SrcNodeName" } - ], - "entityType": "Host" + ] }, { + "entityType": "Host", "fieldMappings": [ { - "columnName": "DstNodeName", - "identifier": "HostName" + "identifier": "HostName", + "columnName": "DstNodeName" } - ], - "entityType": "Host" + ] }, { + "entityType": "Account", "fieldMappings": [ { - "columnName": "SrcUser", - "identifier": "FullName" + "identifier": "FullName", + "columnName": "SrcUser" } - ], - "entityType": "Account" + ] }, { + "entityType": "IP", "fieldMappings": [ { - "columnName": "Src", - "identifier": "Address" + "identifier": "Address", + "columnName": "Src" } - ], - "entityType": "IP" + ] } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { - "enabled": true, - "matchingMethod": "AllEntities", - "groupByEntities": [], "lookbackDuration": "5h", - "reopenClosedIncident": false + "groupByEntities": [], + "matchingMethod": "AllEntities", + "reopenClosedIncident": false, + "enabled": true } } } @@ -6805,50 +6805,50 @@ ], "entityMappings": [ { + "entityType": "Host", "fieldMappings": [ { - "columnName": "SrcNodeName", - "identifier": "HostName" + "identifier": "HostName", + "columnName": "SrcNodeName" } - ], - "entityType": "Host" + ] }, { + "entityType": "Host", "fieldMappings": [ { - "columnName": "DstNodeName", - "identifier": "HostName" + "identifier": "HostName", + "columnName": "DstNodeName" } - ], - "entityType": "Host" + ] }, { + "entityType": "Account", "fieldMappings": [ { - "columnName": "SrcUser", - "identifier": "FullName" + "identifier": "FullName", + "columnName": "SrcUser" } - ], - "entityType": "Account" + ] }, { + "entityType": "IP", "fieldMappings": [ { - "columnName": "Src", - "identifier": "Address" + "identifier": "Address", + "columnName": "Src" } - ], - "entityType": "IP" + ] } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { - "enabled": true, - "matchingMethod": "AllEntities", - "groupByEntities": [], "lookbackDuration": "1d", - "reopenClosedIncident": false + "groupByEntities": [], + "matchingMethod": "AllEntities", + "reopenClosedIncident": false, + "enabled": true } } } @@ -6949,41 +6949,41 @@ ], "entityMappings": [ { + "entityType": "Host", "fieldMappings": [ { - "columnName": "SrcNodeName", - "identifier": "HostName" + "identifier": "HostName", + "columnName": "SrcNodeName" } - ], - "entityType": "Host" + ] }, { + "entityType": "Account", "fieldMappings": [ { - "columnName": "SrcUser", - "identifier": "FullName" + "identifier": "FullName", + "columnName": "SrcUser" } - ], - "entityType": "Account" + ] }, { + "entityType": "IP", "fieldMappings": [ { - "columnName": "Src", - "identifier": "Address" + "identifier": "Address", + "columnName": "Src" } - ], - "entityType": "IP" + ] } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { - "enabled": true, - "matchingMethod": "AllEntities", - "groupByEntities": [], "lookbackDuration": "5h", - "reopenClosedIncident": false + "groupByEntities": [], + "matchingMethod": "AllEntities", + "reopenClosedIncident": false, + "enabled": true } } } @@ -7083,32 +7083,32 @@ ], "entityMappings": [ { + "entityType": "Host", "fieldMappings": [ { - "columnName": "SrcNodeName", - "identifier": "HostName" + "identifier": "HostName", + "columnName": "SrcNodeName" } - ], - "entityType": "Host" + ] }, { + "entityType": "Account", "fieldMappings": [ { - "columnName": "SrcUser", - "identifier": "FullName" + "identifier": "FullName", + "columnName": "SrcUser" } - ], - "entityType": "Account" + ] } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { - "enabled": true, - "matchingMethod": "AllEntities", - "groupByEntities": [], "lookbackDuration": "1d", - "reopenClosedIncident": false + "groupByEntities": [], + "matchingMethod": "AllEntities", + "reopenClosedIncident": false, + "enabled": true } } } @@ -7208,23 +7208,23 @@ ], "entityMappings": [ { + "entityType": "Account", "fieldMappings": [ { - "columnName": "ActorLogin", - "identifier": "FullName" + "identifier": "FullName", + "columnName": "ActorLogin" } - ], - "entityType": "Account" + ] } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { - "enabled": true, - "matchingMethod": "AllEntities", - "groupByEntities": [], "lookbackDuration": "1d", - "reopenClosedIncident": false + "groupByEntities": [], + "matchingMethod": "AllEntities", + "reopenClosedIncident": false, + "enabled": true } } } @@ -7322,23 +7322,23 @@ ], "entityMappings": [ { + "entityType": "Account", "fieldMappings": [ { - "columnName": "ActorLogin", - "identifier": "FullName" + "identifier": "FullName", + "columnName": "ActorLogin" } - ], - "entityType": "Account" + ] } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { - "enabled": true, - "matchingMethod": "AllEntities", - "groupByEntities": [], "lookbackDuration": "1d", - "reopenClosedIncident": false + "groupByEntities": [], + "matchingMethod": "AllEntities", + "reopenClosedIncident": false, + "enabled": true } } } @@ -7436,32 +7436,32 @@ ], "entityMappings": [ { + "entityType": "Host", "fieldMappings": [ { - "columnName": "SrcNodeName", - "identifier": "HostName" + "identifier": "HostName", + "columnName": "SrcNodeName" } - ], - "entityType": "Host" + ] }, { + "entityType": "Account", "fieldMappings": [ { - "columnName": "SrcUser", - "identifier": "FullName" + "identifier": "FullName", + "columnName": "SrcUser" } - ], - "entityType": "Account" + ] } ], "incidentConfiguration": { "createIncident": true, "groupingConfiguration": { - "enabled": true, - "matchingMethod": "AllEntities", - "groupByEntities": [], "lookbackDuration": "6h", - "reopenClosedIncident": false + "groupByEntities": [], + "matchingMethod": "AllEntities", + "reopenClosedIncident": false, + "enabled": true } } } @@ -9636,7 +9636,7 @@ "displayName": "ASIM Network Session parser for Tailscale", "category": "Microsoft Sentinel Parser", "functionAlias": "vimNetworkSessionTailscale", - "query": "let NetworkProtocolLookup = datatable(proto_lookup: int, NetworkProtocol_lookup: string)\n[\n 1, \"ICMP\",\n 6, \"TCP\",\n 17, \"UDP\",\n 58, \"ICMPv6\"\n];\nlet baseEvents = Tailscale_Network_CL;\nlet virtualFlows = baseEvents\n | where HasVirtualTraffic\n | mv-expand t = VirtualTraffic\n | extend NetworkSessionType = \"Virtual\", NetworkDirection = \"Local\";\nlet subnetFlows = baseEvents\n | where HasSubnetTraffic\n | mv-expand t = SubnetTraffic\n | extend NetworkSessionType = \"Subnet\", NetworkDirection = \"Outbound\";\nlet exitFlows = baseEvents\n | where HasExitTraffic\n | mv-expand t = ExitTraffic\n | extend NetworkSessionType = \"Exit\", NetworkDirection = \"Outbound\";\nunion virtualFlows, subnetFlows, exitFlows\n| extend\n SrcIpAddrAndPort = tostring(t.src),\n DstIpAddrAndPort = tostring(t.dst),\n proto_lookup = toint(t.proto),\n SrcBytes = tolong(t.txBytes),\n DstBytes = tolong(t.rxBytes),\n SrcPackets = tolong(t.txPkts),\n DstPackets = tolong(t.rxPkts)\n| extend\n SrcRawHost = extract(@\"^(.+):([0-9]+)$\", 1, SrcIpAddrAndPort),\n SrcPortNumber = toint(extract(@\"^(.+):([0-9]+)$\", 2, SrcIpAddrAndPort)),\n DstRawHost = extract(@\"^(.+):([0-9]+)$\", 1, DstIpAddrAndPort),\n DstPortNumber = toint(extract(@\"^(.+):([0-9]+)$\", 2, DstIpAddrAndPort))\n| extend\n SrcIpAddr = case(\n isempty(SrcRawHost), SrcIpAddrAndPort,\n startswith(SrcRawHost, \"[\"), substring(SrcRawHost, 1, strlen(SrcRawHost) - 2),\n SrcRawHost\n ),\n DstIpAddr = case(\n isempty(DstRawHost), DstIpAddrAndPort,\n startswith(DstRawHost, \"[\"), substring(DstRawHost, 1, strlen(DstRawHost) - 2),\n DstRawHost\n )\n| lookup NetworkProtocolLookup on proto_lookup\n| extend\n NetworkProtocol = coalesce(NetworkProtocol_lookup, tostring(proto_lookup)),\n NetworkBytes = SrcBytes + DstBytes,\n NetworkPackets = SrcPackets + DstPackets\n| extend\n EventStartTime = todatetime(FlowStart),\n EventEndTime = todatetime(FlowEnd),\n EventProduct = \"Tailscale\",\n EventVendor = \"Tailscale\",\n EventSchema = \"NetworkSession\",\n EventSchemaVersion = \"0.2.6\",\n EventType = \"NetworkSession\",\n EventResult = \"Success\",\n EventCount = int(1),\n EventSeverity = \"Informational\",\n DvcAction = \"Allow\"\n| extend\n SrcHostname = SrcNodeName,\n SrcUsername = SrcUser,\n SrcUsernameType = iff(isnotempty(SrcUser), \"Simple\", \"\"),\n SrcDvcOs = SrcOs,\n DstHostname = DstNodeName,\n DstUsername = DstUser,\n DstUsernameType = iff(isnotempty(DstUser), \"Simple\", \"\"),\n DstDvcOs = DstOs,\n Dvc = SrcNodeName,\n Src = SrcIpAddr,\n Dst = DstIpAddr,\n IpAddr = SrcIpAddr,\n User = SrcUser,\n Hostname = SrcNodeName\n| project-away\n t, NetworkProtocol_lookup, proto_lookup,\n VirtualTraffic, SubnetTraffic, ExitTraffic, PhysicalTraffic,\n HasVirtualTraffic, HasSubnetTraffic, HasExitTraffic, HasPhysicalTraffic,\n SrcAddresses, DstAddresses, SrcTags, DstTags,\n SrcOs, DstOs, SrcUser, DstUser, SrcNodeName, DstNodeName,\n DstCount, DstNodeId, NodeId, IsRelayed,\n SrcIpAddrAndPort, DstIpAddrAndPort, SrcRawHost, DstRawHost,\n FlowStart, FlowEnd,\n TenantId, _ResourceId, Type\n", + "query": "let NetworkProtocolLookup = datatable(proto_lookup: int, NetworkProtocol_lookup: string)\n[\n 1, \"ICMP\",\n 6, \"TCP\",\n 17, \"UDP\",\n 58, \"ICMPv6\"\n];\nlet baseEvents = Tailscale_Network_CL;\nlet virtualFlows = baseEvents\n | where HasVirtualTraffic\n | mv-expand t = VirtualTraffic\n | extend NetworkSessionType = \"Virtual\", NetworkDirection = \"Local\";\nlet subnetFlows = baseEvents\n | where HasSubnetTraffic\n | mv-expand t = SubnetTraffic\n | extend NetworkSessionType = \"Subnet\", NetworkDirection = \"Outbound\";\nlet exitFlows = baseEvents\n | where HasExitTraffic\n | mv-expand t = ExitTraffic\n | extend NetworkSessionType = \"Exit\", NetworkDirection = \"Outbound\";\nunion virtualFlows, subnetFlows, exitFlows\n| extend\n SrcIpAddrAndPort = tostring(t.src),\n DstIpAddrAndPort = tostring(t.dst),\n proto_lookup = toint(t.proto),\n SrcBytes = tolong(t.txBytes),\n DstBytes = tolong(t.rxBytes),\n SrcPackets = tolong(t.txPkts),\n DstPackets = tolong(t.rxPkts)\n| extend\n SrcRawHost = extract(@\"^(.+):([0-9]+)$\", 1, SrcIpAddrAndPort),\n SrcPortNumber = toint(extract(@\"^(.+):([0-9]+)$\", 2, SrcIpAddrAndPort)),\n DstRawHost = extract(@\"^(.+):([0-9]+)$\", 1, DstIpAddrAndPort),\n DstPortNumber = toint(extract(@\"^(.+):([0-9]+)$\", 2, DstIpAddrAndPort))\n| extend\n SrcIpAddr = case(\n isempty(SrcRawHost), SrcIpAddrAndPort,\n SrcRawHost startswith \"[\", substring(SrcRawHost, 1, strlen(SrcRawHost) - 2),\n SrcRawHost\n ),\n DstIpAddr = case(\n isempty(DstRawHost), DstIpAddrAndPort,\n DstRawHost startswith \"[\", substring(DstRawHost, 1, strlen(DstRawHost) - 2),\n DstRawHost\n )\n| lookup NetworkProtocolLookup on proto_lookup\n| extend\n NetworkProtocol = coalesce(NetworkProtocol_lookup, tostring(proto_lookup)),\n NetworkBytes = SrcBytes + DstBytes,\n NetworkPackets = SrcPackets + DstPackets\n| extend\n EventStartTime = todatetime(FlowStart),\n EventEndTime = todatetime(FlowEnd),\n EventProduct = \"Tailscale\",\n EventVendor = \"Tailscale\",\n EventSchema = \"NetworkSession\",\n EventSchemaVersion = \"0.2.6\",\n EventType = \"NetworkSession\",\n EventResult = \"Success\",\n EventCount = int(1),\n EventSeverity = \"Informational\",\n DvcAction = \"Allow\"\n| extend\n SrcHostname = SrcNodeName,\n SrcUsername = SrcUser,\n SrcUsernameType = iff(isnotempty(SrcUser), \"Simple\", \"\"),\n SrcDvcOs = SrcOs,\n DstHostname = DstNodeName,\n DstUsername = DstUser,\n DstUsernameType = iff(isnotempty(DstUser), \"Simple\", \"\"),\n DstDvcOs = DstOs,\n Dvc = SrcNodeName,\n Src = SrcIpAddr,\n Dst = DstIpAddr,\n IpAddr = SrcIpAddr,\n User = SrcUser,\n Hostname = SrcNodeName\n| project-away\n t, NetworkProtocol_lookup, proto_lookup,\n VirtualTraffic, SubnetTraffic, ExitTraffic, PhysicalTraffic,\n HasVirtualTraffic, HasSubnetTraffic, HasExitTraffic, HasPhysicalTraffic,\n SrcAddresses, DstAddresses, SrcTags, DstTags,\n SrcOs, DstOs, SrcUser, DstUser, SrcNodeName, DstNodeName,\n DstCount, DstNodeId, NodeId, IsRelayed,\n SrcIpAddrAndPort, DstIpAddrAndPort, SrcRawHost, DstRawHost,\n FlowStart, FlowEnd,\n TenantId, _ResourceId, Type\n", "functionParameters": "", "version": 2, "tags": [ @@ -9701,7 +9701,7 @@ "displayName": "ASIM Network Session parser for Tailscale", "category": "Microsoft Sentinel Parser", "functionAlias": "vimNetworkSessionTailscale", - "query": "let NetworkProtocolLookup = datatable(proto_lookup: int, NetworkProtocol_lookup: string)\n[\n 1, \"ICMP\",\n 6, \"TCP\",\n 17, \"UDP\",\n 58, \"ICMPv6\"\n];\nlet baseEvents = Tailscale_Network_CL;\nlet virtualFlows = baseEvents\n | where HasVirtualTraffic\n | mv-expand t = VirtualTraffic\n | extend NetworkSessionType = \"Virtual\", NetworkDirection = \"Local\";\nlet subnetFlows = baseEvents\n | where HasSubnetTraffic\n | mv-expand t = SubnetTraffic\n | extend NetworkSessionType = \"Subnet\", NetworkDirection = \"Outbound\";\nlet exitFlows = baseEvents\n | where HasExitTraffic\n | mv-expand t = ExitTraffic\n | extend NetworkSessionType = \"Exit\", NetworkDirection = \"Outbound\";\nunion virtualFlows, subnetFlows, exitFlows\n| extend\n SrcIpAddrAndPort = tostring(t.src),\n DstIpAddrAndPort = tostring(t.dst),\n proto_lookup = toint(t.proto),\n SrcBytes = tolong(t.txBytes),\n DstBytes = tolong(t.rxBytes),\n SrcPackets = tolong(t.txPkts),\n DstPackets = tolong(t.rxPkts)\n| extend\n SrcRawHost = extract(@\"^(.+):([0-9]+)$\", 1, SrcIpAddrAndPort),\n SrcPortNumber = toint(extract(@\"^(.+):([0-9]+)$\", 2, SrcIpAddrAndPort)),\n DstRawHost = extract(@\"^(.+):([0-9]+)$\", 1, DstIpAddrAndPort),\n DstPortNumber = toint(extract(@\"^(.+):([0-9]+)$\", 2, DstIpAddrAndPort))\n| extend\n SrcIpAddr = case(\n isempty(SrcRawHost), SrcIpAddrAndPort,\n startswith(SrcRawHost, \"[\"), substring(SrcRawHost, 1, strlen(SrcRawHost) - 2),\n SrcRawHost\n ),\n DstIpAddr = case(\n isempty(DstRawHost), DstIpAddrAndPort,\n startswith(DstRawHost, \"[\"), substring(DstRawHost, 1, strlen(DstRawHost) - 2),\n DstRawHost\n )\n| lookup NetworkProtocolLookup on proto_lookup\n| extend\n NetworkProtocol = coalesce(NetworkProtocol_lookup, tostring(proto_lookup)),\n NetworkBytes = SrcBytes + DstBytes,\n NetworkPackets = SrcPackets + DstPackets\n| extend\n EventStartTime = todatetime(FlowStart),\n EventEndTime = todatetime(FlowEnd),\n EventProduct = \"Tailscale\",\n EventVendor = \"Tailscale\",\n EventSchema = \"NetworkSession\",\n EventSchemaVersion = \"0.2.6\",\n EventType = \"NetworkSession\",\n EventResult = \"Success\",\n EventCount = int(1),\n EventSeverity = \"Informational\",\n DvcAction = \"Allow\"\n| extend\n SrcHostname = SrcNodeName,\n SrcUsername = SrcUser,\n SrcUsernameType = iff(isnotempty(SrcUser), \"Simple\", \"\"),\n SrcDvcOs = SrcOs,\n DstHostname = DstNodeName,\n DstUsername = DstUser,\n DstUsernameType = iff(isnotempty(DstUser), \"Simple\", \"\"),\n DstDvcOs = DstOs,\n Dvc = SrcNodeName,\n Src = SrcIpAddr,\n Dst = DstIpAddr,\n IpAddr = SrcIpAddr,\n User = SrcUser,\n Hostname = SrcNodeName\n| project-away\n t, NetworkProtocol_lookup, proto_lookup,\n VirtualTraffic, SubnetTraffic, ExitTraffic, PhysicalTraffic,\n HasVirtualTraffic, HasSubnetTraffic, HasExitTraffic, HasPhysicalTraffic,\n SrcAddresses, DstAddresses, SrcTags, DstTags,\n SrcOs, DstOs, SrcUser, DstUser, SrcNodeName, DstNodeName,\n DstCount, DstNodeId, NodeId, IsRelayed,\n SrcIpAddrAndPort, DstIpAddrAndPort, SrcRawHost, DstRawHost,\n FlowStart, FlowEnd,\n TenantId, _ResourceId, Type\n", + "query": "let NetworkProtocolLookup = datatable(proto_lookup: int, NetworkProtocol_lookup: string)\n[\n 1, \"ICMP\",\n 6, \"TCP\",\n 17, \"UDP\",\n 58, \"ICMPv6\"\n];\nlet baseEvents = Tailscale_Network_CL;\nlet virtualFlows = baseEvents\n | where HasVirtualTraffic\n | mv-expand t = VirtualTraffic\n | extend NetworkSessionType = \"Virtual\", NetworkDirection = \"Local\";\nlet subnetFlows = baseEvents\n | where HasSubnetTraffic\n | mv-expand t = SubnetTraffic\n | extend NetworkSessionType = \"Subnet\", NetworkDirection = \"Outbound\";\nlet exitFlows = baseEvents\n | where HasExitTraffic\n | mv-expand t = ExitTraffic\n | extend NetworkSessionType = \"Exit\", NetworkDirection = \"Outbound\";\nunion virtualFlows, subnetFlows, exitFlows\n| extend\n SrcIpAddrAndPort = tostring(t.src),\n DstIpAddrAndPort = tostring(t.dst),\n proto_lookup = toint(t.proto),\n SrcBytes = tolong(t.txBytes),\n DstBytes = tolong(t.rxBytes),\n SrcPackets = tolong(t.txPkts),\n DstPackets = tolong(t.rxPkts)\n| extend\n SrcRawHost = extract(@\"^(.+):([0-9]+)$\", 1, SrcIpAddrAndPort),\n SrcPortNumber = toint(extract(@\"^(.+):([0-9]+)$\", 2, SrcIpAddrAndPort)),\n DstRawHost = extract(@\"^(.+):([0-9]+)$\", 1, DstIpAddrAndPort),\n DstPortNumber = toint(extract(@\"^(.+):([0-9]+)$\", 2, DstIpAddrAndPort))\n| extend\n SrcIpAddr = case(\n isempty(SrcRawHost), SrcIpAddrAndPort,\n SrcRawHost startswith \"[\", substring(SrcRawHost, 1, strlen(SrcRawHost) - 2),\n SrcRawHost\n ),\n DstIpAddr = case(\n isempty(DstRawHost), DstIpAddrAndPort,\n DstRawHost startswith \"[\", substring(DstRawHost, 1, strlen(DstRawHost) - 2),\n DstRawHost\n )\n| lookup NetworkProtocolLookup on proto_lookup\n| extend\n NetworkProtocol = coalesce(NetworkProtocol_lookup, tostring(proto_lookup)),\n NetworkBytes = SrcBytes + DstBytes,\n NetworkPackets = SrcPackets + DstPackets\n| extend\n EventStartTime = todatetime(FlowStart),\n EventEndTime = todatetime(FlowEnd),\n EventProduct = \"Tailscale\",\n EventVendor = \"Tailscale\",\n EventSchema = \"NetworkSession\",\n EventSchemaVersion = \"0.2.6\",\n EventType = \"NetworkSession\",\n EventResult = \"Success\",\n EventCount = int(1),\n EventSeverity = \"Informational\",\n DvcAction = \"Allow\"\n| extend\n SrcHostname = SrcNodeName,\n SrcUsername = SrcUser,\n SrcUsernameType = iff(isnotempty(SrcUser), \"Simple\", \"\"),\n SrcDvcOs = SrcOs,\n DstHostname = DstNodeName,\n DstUsername = DstUser,\n DstUsernameType = iff(isnotempty(DstUser), \"Simple\", \"\"),\n DstDvcOs = DstOs,\n Dvc = SrcNodeName,\n Src = SrcIpAddr,\n Dst = DstIpAddr,\n IpAddr = SrcIpAddr,\n User = SrcUser,\n Hostname = SrcNodeName\n| project-away\n t, NetworkProtocol_lookup, proto_lookup,\n VirtualTraffic, SubnetTraffic, ExitTraffic, PhysicalTraffic,\n HasVirtualTraffic, HasSubnetTraffic, HasExitTraffic, HasPhysicalTraffic,\n SrcAddresses, DstAddresses, SrcTags, DstTags,\n SrcOs, DstOs, SrcUser, DstUser, SrcNodeName, DstNodeName,\n DstCount, DstNodeId, NodeId, IsRelayed,\n SrcIpAddrAndPort, DstIpAddrAndPort, SrcRawHost, DstRawHost,\n FlowStart, FlowEnd,\n TenantId, _ResourceId, Type\n", "functionParameters": "", "version": 2, "tags": [ @@ -9768,7 +9768,7 @@ "displayName": "ASIM Network Session parser for Tailscale (no-prefix wrapper)", "category": "Microsoft Sentinel Parser", "functionAlias": "ASimNetworkSessionTailscale", - "query": "vimNetworkSessionTailscale\n", + "query": "let NetworkProtocolLookup = datatable(proto_lookup: int, NetworkProtocol_lookup: string)\n[\n 1, \"ICMP\",\n 6, \"TCP\",\n 17, \"UDP\",\n 58, \"ICMPv6\"\n];\nlet baseEvents = Tailscale_Network_CL;\nlet virtualFlows = baseEvents\n | where HasVirtualTraffic\n | mv-expand t = VirtualTraffic\n | extend NetworkSessionType = \"Virtual\", NetworkDirection = \"Local\";\nlet subnetFlows = baseEvents\n | where HasSubnetTraffic\n | mv-expand t = SubnetTraffic\n | extend NetworkSessionType = \"Subnet\", NetworkDirection = \"Outbound\";\nlet exitFlows = baseEvents\n | where HasExitTraffic\n | mv-expand t = ExitTraffic\n | extend NetworkSessionType = \"Exit\", NetworkDirection = \"Outbound\";\nunion virtualFlows, subnetFlows, exitFlows\n| extend\n SrcIpAddrAndPort = tostring(t.src),\n DstIpAddrAndPort = tostring(t.dst),\n proto_lookup = toint(t.proto),\n SrcBytes = tolong(t.txBytes),\n DstBytes = tolong(t.rxBytes),\n SrcPackets = tolong(t.txPkts),\n DstPackets = tolong(t.rxPkts)\n| extend\n SrcRawHost = extract(@\"^(.+):([0-9]+)$\", 1, SrcIpAddrAndPort),\n SrcPortNumber = toint(extract(@\"^(.+):([0-9]+)$\", 2, SrcIpAddrAndPort)),\n DstRawHost = extract(@\"^(.+):([0-9]+)$\", 1, DstIpAddrAndPort),\n DstPortNumber = toint(extract(@\"^(.+):([0-9]+)$\", 2, DstIpAddrAndPort))\n| extend\n SrcIpAddr = case(\n isempty(SrcRawHost), SrcIpAddrAndPort,\n SrcRawHost startswith \"[\", substring(SrcRawHost, 1, strlen(SrcRawHost) - 2),\n SrcRawHost\n ),\n DstIpAddr = case(\n isempty(DstRawHost), DstIpAddrAndPort,\n DstRawHost startswith \"[\", substring(DstRawHost, 1, strlen(DstRawHost) - 2),\n DstRawHost\n )\n| lookup NetworkProtocolLookup on proto_lookup\n| extend\n NetworkProtocol = coalesce(NetworkProtocol_lookup, tostring(proto_lookup)),\n NetworkBytes = SrcBytes + DstBytes,\n NetworkPackets = SrcPackets + DstPackets\n| extend\n EventStartTime = todatetime(FlowStart),\n EventEndTime = todatetime(FlowEnd),\n EventProduct = \"Tailscale\",\n EventVendor = \"Tailscale\",\n EventSchema = \"NetworkSession\",\n EventSchemaVersion = \"0.2.6\",\n EventType = \"NetworkSession\",\n EventResult = \"Success\",\n EventCount = int(1),\n EventSeverity = \"Informational\",\n DvcAction = \"Allow\"\n| extend\n SrcHostname = SrcNodeName,\n SrcUsername = SrcUser,\n SrcUsernameType = iff(isnotempty(SrcUser), \"Simple\", \"\"),\n SrcDvcOs = SrcOs,\n DstHostname = DstNodeName,\n DstUsername = DstUser,\n DstUsernameType = iff(isnotempty(DstUser), \"Simple\", \"\"),\n DstDvcOs = DstOs,\n Dvc = SrcNodeName,\n Src = SrcIpAddr,\n Dst = DstIpAddr,\n IpAddr = SrcIpAddr,\n User = SrcUser,\n Hostname = SrcNodeName\n| project-away\n t, NetworkProtocol_lookup, proto_lookup,\n VirtualTraffic, SubnetTraffic, ExitTraffic, PhysicalTraffic,\n HasVirtualTraffic, HasSubnetTraffic, HasExitTraffic, HasPhysicalTraffic,\n SrcAddresses, DstAddresses, SrcTags, DstTags,\n SrcOs, DstOs, SrcUser, DstUser, SrcNodeName, DstNodeName,\n DstCount, DstNodeId, NodeId, IsRelayed,\n SrcIpAddrAndPort, DstIpAddrAndPort, SrcRawHost, DstRawHost,\n FlowStart, FlowEnd,\n TenantId, _ResourceId, Type\n", "functionParameters": "", "version": 2, "tags": [ @@ -9833,7 +9833,7 @@ "displayName": "ASIM Network Session parser for Tailscale (no-prefix wrapper)", "category": "Microsoft Sentinel Parser", "functionAlias": "ASimNetworkSessionTailscale", - "query": "vimNetworkSessionTailscale\n", + "query": "let NetworkProtocolLookup = datatable(proto_lookup: int, NetworkProtocol_lookup: string)\n[\n 1, \"ICMP\",\n 6, \"TCP\",\n 17, \"UDP\",\n 58, \"ICMPv6\"\n];\nlet baseEvents = Tailscale_Network_CL;\nlet virtualFlows = baseEvents\n | where HasVirtualTraffic\n | mv-expand t = VirtualTraffic\n | extend NetworkSessionType = \"Virtual\", NetworkDirection = \"Local\";\nlet subnetFlows = baseEvents\n | where HasSubnetTraffic\n | mv-expand t = SubnetTraffic\n | extend NetworkSessionType = \"Subnet\", NetworkDirection = \"Outbound\";\nlet exitFlows = baseEvents\n | where HasExitTraffic\n | mv-expand t = ExitTraffic\n | extend NetworkSessionType = \"Exit\", NetworkDirection = \"Outbound\";\nunion virtualFlows, subnetFlows, exitFlows\n| extend\n SrcIpAddrAndPort = tostring(t.src),\n DstIpAddrAndPort = tostring(t.dst),\n proto_lookup = toint(t.proto),\n SrcBytes = tolong(t.txBytes),\n DstBytes = tolong(t.rxBytes),\n SrcPackets = tolong(t.txPkts),\n DstPackets = tolong(t.rxPkts)\n| extend\n SrcRawHost = extract(@\"^(.+):([0-9]+)$\", 1, SrcIpAddrAndPort),\n SrcPortNumber = toint(extract(@\"^(.+):([0-9]+)$\", 2, SrcIpAddrAndPort)),\n DstRawHost = extract(@\"^(.+):([0-9]+)$\", 1, DstIpAddrAndPort),\n DstPortNumber = toint(extract(@\"^(.+):([0-9]+)$\", 2, DstIpAddrAndPort))\n| extend\n SrcIpAddr = case(\n isempty(SrcRawHost), SrcIpAddrAndPort,\n SrcRawHost startswith \"[\", substring(SrcRawHost, 1, strlen(SrcRawHost) - 2),\n SrcRawHost\n ),\n DstIpAddr = case(\n isempty(DstRawHost), DstIpAddrAndPort,\n DstRawHost startswith \"[\", substring(DstRawHost, 1, strlen(DstRawHost) - 2),\n DstRawHost\n )\n| lookup NetworkProtocolLookup on proto_lookup\n| extend\n NetworkProtocol = coalesce(NetworkProtocol_lookup, tostring(proto_lookup)),\n NetworkBytes = SrcBytes + DstBytes,\n NetworkPackets = SrcPackets + DstPackets\n| extend\n EventStartTime = todatetime(FlowStart),\n EventEndTime = todatetime(FlowEnd),\n EventProduct = \"Tailscale\",\n EventVendor = \"Tailscale\",\n EventSchema = \"NetworkSession\",\n EventSchemaVersion = \"0.2.6\",\n EventType = \"NetworkSession\",\n EventResult = \"Success\",\n EventCount = int(1),\n EventSeverity = \"Informational\",\n DvcAction = \"Allow\"\n| extend\n SrcHostname = SrcNodeName,\n SrcUsername = SrcUser,\n SrcUsernameType = iff(isnotempty(SrcUser), \"Simple\", \"\"),\n SrcDvcOs = SrcOs,\n DstHostname = DstNodeName,\n DstUsername = DstUser,\n DstUsernameType = iff(isnotempty(DstUser), \"Simple\", \"\"),\n DstDvcOs = DstOs,\n Dvc = SrcNodeName,\n Src = SrcIpAddr,\n Dst = DstIpAddr,\n IpAddr = SrcIpAddr,\n User = SrcUser,\n Hostname = SrcNodeName\n| project-away\n t, NetworkProtocol_lookup, proto_lookup,\n VirtualTraffic, SubnetTraffic, ExitTraffic, PhysicalTraffic,\n HasVirtualTraffic, HasSubnetTraffic, HasExitTraffic, HasPhysicalTraffic,\n SrcAddresses, DstAddresses, SrcTags, DstTags,\n SrcOs, DstOs, SrcUser, DstUser, SrcNodeName, DstNodeName,\n DstCount, DstNodeId, NodeId, IsRelayed,\n SrcIpAddrAndPort, DstIpAddrAndPort, SrcRawHost, DstRawHost,\n FlowStart, FlowEnd,\n TenantId, _ResourceId, Type\n", "functionParameters": "", "version": 2, "tags": [ diff --git a/Solutions/Tailscale (CCF)/Parsers/ASimNetworkSessionTailscale.yaml b/Solutions/Tailscale (CCF)/Parsers/ASimNetworkSessionTailscale.yaml index 11677937446..4b8af4fb11a 100644 --- a/Solutions/Tailscale (CCF)/Parsers/ASimNetworkSessionTailscale.yaml +++ b/Solutions/Tailscale (CCF)/Parsers/ASimNetworkSessionTailscale.yaml @@ -7,4 +7,90 @@ Category: Microsoft Sentinel Parser FunctionName: ASimNetworkSessionTailscale FunctionAlias: ASimNetworkSessionTailscale FunctionQuery: | - vimNetworkSessionTailscale + let NetworkProtocolLookup = datatable(proto_lookup: int, NetworkProtocol_lookup: string) + [ + 1, "ICMP", + 6, "TCP", + 17, "UDP", + 58, "ICMPv6" + ]; + let baseEvents = Tailscale_Network_CL; + let virtualFlows = baseEvents + | where HasVirtualTraffic + | mv-expand t = VirtualTraffic + | extend NetworkSessionType = "Virtual", NetworkDirection = "Local"; + let subnetFlows = baseEvents + | where HasSubnetTraffic + | mv-expand t = SubnetTraffic + | extend NetworkSessionType = "Subnet", NetworkDirection = "Outbound"; + let exitFlows = baseEvents + | where HasExitTraffic + | mv-expand t = ExitTraffic + | extend NetworkSessionType = "Exit", NetworkDirection = "Outbound"; + union virtualFlows, subnetFlows, exitFlows + | extend + SrcIpAddrAndPort = tostring(t.src), + DstIpAddrAndPort = tostring(t.dst), + proto_lookup = toint(t.proto), + SrcBytes = tolong(t.txBytes), + DstBytes = tolong(t.rxBytes), + SrcPackets = tolong(t.txPkts), + DstPackets = tolong(t.rxPkts) + | extend + SrcRawHost = extract(@"^(.+):([0-9]+)$", 1, SrcIpAddrAndPort), + SrcPortNumber = toint(extract(@"^(.+):([0-9]+)$", 2, SrcIpAddrAndPort)), + DstRawHost = extract(@"^(.+):([0-9]+)$", 1, DstIpAddrAndPort), + DstPortNumber = toint(extract(@"^(.+):([0-9]+)$", 2, DstIpAddrAndPort)) + | extend + SrcIpAddr = case( + isempty(SrcRawHost), SrcIpAddrAndPort, + SrcRawHost startswith "[", substring(SrcRawHost, 1, strlen(SrcRawHost) - 2), + SrcRawHost + ), + DstIpAddr = case( + isempty(DstRawHost), DstIpAddrAndPort, + DstRawHost startswith "[", substring(DstRawHost, 1, strlen(DstRawHost) - 2), + DstRawHost + ) + | lookup NetworkProtocolLookup on proto_lookup + | extend + NetworkProtocol = coalesce(NetworkProtocol_lookup, tostring(proto_lookup)), + NetworkBytes = SrcBytes + DstBytes, + NetworkPackets = SrcPackets + DstPackets + | extend + EventStartTime = todatetime(FlowStart), + EventEndTime = todatetime(FlowEnd), + EventProduct = "Tailscale", + EventVendor = "Tailscale", + EventSchema = "NetworkSession", + EventSchemaVersion = "0.2.6", + EventType = "NetworkSession", + EventResult = "Success", + EventCount = int(1), + EventSeverity = "Informational", + DvcAction = "Allow" + | extend + SrcHostname = SrcNodeName, + SrcUsername = SrcUser, + SrcUsernameType = iff(isnotempty(SrcUser), "Simple", ""), + SrcDvcOs = SrcOs, + DstHostname = DstNodeName, + DstUsername = DstUser, + DstUsernameType = iff(isnotempty(DstUser), "Simple", ""), + DstDvcOs = DstOs, + Dvc = SrcNodeName, + Src = SrcIpAddr, + Dst = DstIpAddr, + IpAddr = SrcIpAddr, + User = SrcUser, + Hostname = SrcNodeName + | project-away + t, NetworkProtocol_lookup, proto_lookup, + VirtualTraffic, SubnetTraffic, ExitTraffic, PhysicalTraffic, + HasVirtualTraffic, HasSubnetTraffic, HasExitTraffic, HasPhysicalTraffic, + SrcAddresses, DstAddresses, SrcTags, DstTags, + SrcOs, DstOs, SrcUser, DstUser, SrcNodeName, DstNodeName, + DstCount, DstNodeId, NodeId, IsRelayed, + SrcIpAddrAndPort, DstIpAddrAndPort, SrcRawHost, DstRawHost, + FlowStart, FlowEnd, + TenantId, _ResourceId, Type diff --git a/Solutions/Tailscale (CCF)/Parsers/vimNetworkSessionTailscale.yaml b/Solutions/Tailscale (CCF)/Parsers/vimNetworkSessionTailscale.yaml index 31a739d01ae..9f4faca048a 100644 --- a/Solutions/Tailscale (CCF)/Parsers/vimNetworkSessionTailscale.yaml +++ b/Solutions/Tailscale (CCF)/Parsers/vimNetworkSessionTailscale.yaml @@ -44,12 +44,12 @@ FunctionQuery: | | extend SrcIpAddr = case( isempty(SrcRawHost), SrcIpAddrAndPort, - startswith(SrcRawHost, "["), substring(SrcRawHost, 1, strlen(SrcRawHost) - 2), + SrcRawHost startswith "[", substring(SrcRawHost, 1, strlen(SrcRawHost) - 2), SrcRawHost ), DstIpAddr = case( isempty(DstRawHost), DstIpAddrAndPort, - startswith(DstRawHost, "["), substring(DstRawHost, 1, strlen(DstRawHost) - 2), + DstRawHost startswith "[", substring(DstRawHost, 1, strlen(DstRawHost) - 2), DstRawHost ) | lookup NetworkProtocolLookup on proto_lookup

\u2022 Review the solution Release Notes

• Review the solution Release Notes