Skip to main content

tailscale_rest/models/
logging.rs

1//! Audit logs, network flow logs, and streaming either of them somewhere else.
2
3use serde_json::Value;
4
5use crate::Secret;
6use crate::model;
7use crate::models::KnownValues;
8
9/// The two log streams a tailnet produces.
10pub const LOG_TYPES: &[&str] = &["configuration", "network"];
11
12/// The systems logs can be streamed to.
13pub const DESTINATION_TYPES: &[&str] = &[
14    "splunk",
15    "elastic",
16    "panther",
17    "cribl",
18    "crowdstrike",
19    "datadog",
20    "axiom",
21    "s3",
22];
23
24/// How a log stream is compressed. `none` is the default.
25pub const COMPRESSION_FORMATS: &[&str] = &["zstd", "gzip", "none"];
26
27/// How Tailscale authenticates to S3. `rolearn` is the recommended one.
28pub const S3_AUTHENTICATION_TYPES: &[&str] = &["accesskey", "rolearn"];
29
30/// What kind of log an audit record is. One member today, which is the whole
31/// reason it is a string here (Q60).
32pub const AUDIT_LOG_TYPES: &[&str] = &["CONFIG"];
33
34/// What set a configuration change in motion.
35pub const AUDIT_ORIGINS: &[&str] = &[
36    "ADMIN_CONSOLE",
37    "CONFIG_API",
38    "CONTROL",
39    "IDENTITY_PROVIDER",
40    "NODE",
41    "SUPPORT_REQUEST",
42    "STRIPE",
43    "SECURITY_NOTIFICATION",
44    "LEGAL_NOTIFICATION",
45    "BORDER0_API",
46];
47
48/// What kind of thing acted.
49pub const AUDIT_ACTOR_TYPES: &[&str] = &[
50    "USER",
51    "NODE",
52    "AUTOMATED_WORKER",
53    "OAUTH_CLIENT",
54    "SCIM",
55    "MULLVAD",
56    "LOGSTREAM",
57    "SECRET_SCANNER",
58    "PAM_CONNECTOR",
59    "PAM_SERVICE_ACCOUNT",
60];
61
62/// What kind of thing was acted on.
63pub const AUDIT_TARGET_TYPES: &[&str] = &[
64    "TAILNET",
65    "USER",
66    "GROUP",
67    "NODE",
68    "API_KEY",
69    "INVITE",
70    "SHARE",
71    "BILLING",
72    "ADMIN_CONSOLE",
73    "WEB_INTERFACE",
74    "WEBHOOK_ENDPOINT",
75    "FAILED_REQUEST",
76];
77
78/// Which property of the target changed. The longest of these lists and the
79/// one most likely to grow, since every new setting adds a member.
80pub const AUDIT_TARGET_PROPERTIES: &[&str] = &[
81    "ACL",
82    "ACL_TAGS",
83    "ACCOUNT_EMAIL",
84    "ADDRESS",
85    "ALLOWED_IPS",
86    "AUTO_APPROVED_ROUTES",
87    "ATTRIBUTES",
88    "BILLING_OWNER",
89    "COLLECT_SERVICES",
90    "COLLECT_POSTURE_IDENTITY",
91    "MULLVAD_VPN",
92    "DNS_CONFIG",
93    "EMAIL",
94    "EXIT_NODE",
95    "FEATURE",
96    "FILE_SHARING",
97    "HTTPS",
98    "KEY_EXPIRY_TIME",
99    "KEY_EXPIRY",
100    "LOG_EXIT_FLOWS",
101    "LOGSTREAM_ENDPOINT",
102    "MAGIC_DNS",
103    "MACHINE_AUTH_NEEDED",
104    "MACHINE_APPROVAL_NEEDED",
105    "USER_APPROVAL_REQUIRED",
106    "MACHINE_NAME",
107    "MAX_KEY_DURATION",
108    "NETWORK_FLOW_LOGGING",
109    "GEOSTEERING",
110    "NODE_SHARE",
111    "TAILNET_INVITE",
112    "PAYMENT_INFO",
113    "POSTURE_IDENTITY",
114    "POSTURE_INTEGRATION",
115    "USER_ROLE",
116    "SCIM",
117    "SECURITY_EMAIL",
118    "STRIPE_CUSTOMER_ID",
119    "SUBSCRIPTION",
120    "SUBSCRIBED_EVENTS",
121    "SUPPORT_EMAIL",
122    "SECRET",
123    "TCD",
124    "TKA",
125    "AUTH_PROVIDER",
126    "ROUTE_SELECTION",
127];
128
129/// What was attempted against the target.
130pub const AUDIT_ACTIONS: &[&str] = &[
131    "LOGIN",
132    "LOGOUT",
133    "CREATE",
134    "UPDATE",
135    "DELETE",
136    "CANCEL",
137    "REVOKE",
138    "APPROVE",
139    "SUSPEND",
140    "RESTORE",
141    "ENABLE",
142    "DISABLE",
143    "ACCEPT",
144    "EXPIRED",
145    "PUSH_USER",
146    "PUSH_GROUP",
147    "VERIFY",
148    "JOIN_WAITLIST",
149    "INVITE",
150    "JOIN",
151    "LEAVE",
152    "RESEND",
153    "MIGRATE_AUTH_PROVIDER",
154];
155
156/// The IP protocols a flow log names. A flow over anything else is reported by
157/// number, so this list is a spelling aid rather than a set of possibilities.
158pub const FLOW_PROTOCOLS: &[&str] = &[
159    "ah",
160    "dccp",
161    "egp",
162    "esp",
163    "gre",
164    "icmp",
165    "igmp",
166    "igp",
167    "ipv4",
168    "ipv6-icmp",
169    "sctp",
170    "tcp",
171    "udp",
172];
173
174/// Every event the audit log can be filtered by.
175///
176/// A hundred and forty of them, and the list that a  parameter
177/// quotes. Kept whole rather than summarised because the caller has to spell one
178/// exactly, and a truncated list is worse than none.
179pub const AUDIT_EVENTS: &[&str] = &[
180    "ADMIN_CONSOLE.LOGIN", "ADMIN_CONSOLE.LOGOUT", "API_KEY.CREATE", "API_KEY.EXPIRED",
181    "API_KEY.REVOKE", "BILLING.CANCEL.SUBSCRIPTION", "BILLING.CREATE.SUBSCRIPTION",
182    "BILLING.UPDATE.ADDRESS", "BILLING.UPDATE.BILLING_OWNER", "BILLING.UPDATE.EMAIL",
183    "BILLING.UPDATE.PAYMENT_INFO", "BILLING.UPDATE.STRIPE_CUSTOMER_ID",
184    "BILLING.UPDATE.SUBSCRIPTION", "FAILED_REQUEST.UPDATE", "GROUP.PUSH_GROUP.ATTRIBUTES",
185    "GROUP.UPDATE.USER_ROLE",
186    "INVITE.ACCEPT.FEATURE", "INVITE.ACCEPT.NODE_SHARE", "INVITE.ACCEPT.TAILNET_INVITE",
187    "INVITE.CREATE.FEATURE", "INVITE.CREATE.NODE_SHARE", "INVITE.CREATE.TAILNET_INVITE",
188    "INVITE.DELETE.NODE_SHARE", "INVITE.DELETE.TAILNET_INVITE", "INVITE.RESEND.NODE_SHARE",
189    "INVITE.RESEND.TAILNET_INVITE", "NODE.APPROVE", "NODE.CREATE", "NODE.CREATE.ATTRIBUTES",
190    "NODE.DELETE", "NODE.DELETE.ATTRIBUTES", "NODE.DISABLE.KEY_EXPIRY",
191    "NODE.DISCONNECT_NODE.CLIENT_LOG", "NODE.ENABLE.KEY_EXPIRY",
192    "NODE.EXPIRED.KEY_EXPIRY_TIME", "NODE.LOGIN", "NODE.LOGOUT", "NODE.REVOKE",
193    "NODE.UPDATE.ACL_TAGS", "NODE.UPDATE.ALLOWED_IPS", "NODE.UPDATE.ATTRIBUTES",
194    "NODE.UPDATE.AUTO_APPROVED_ROUTES", "NODE.UPDATE.EXIT_NODE",
195    "NODE.UPDATE.KEY_EXPIRY_TIME", "NODE.UPDATE.MACHINE_NAME",
196    "NODE.UPDATE.POSTURE_IDENTITY", "NODE.UPDATE.TKA", "SHARE.CREATE", "SHARE.DELETE",
197    "SHARE.UPDATE", "TAILNET.ACCEPT.FEATURE", "TAILNET.CREATE",
198    "TAILNET.CREATE.LOGSTREAM_ENDPOINT", "TAILNET.CREATE.POSTURE_INTEGRATION",
199    "TAILNET.CREATE.TKA", "TAILNET.DELETE.LOGSTREAM_ENDPOINT",
200    "TAILNET.DELETE.POSTURE_INTEGRATION", "TAILNET.DELETE.TKA",
201    "TAILNET.DISABLE.COLLECT_POSTURE_IDENTITY", "TAILNET.DISABLE.COLLECT_SERVICES",
202    "TAILNET.DISABLE.FILE_SHARING", "TAILNET.DISABLE.GEOSTEERING", "TAILNET.DISABLE.HTTPS",
203    "TAILNET.DISABLE.LOG_EXIT_FLOWS", "TAILNET.DISABLE.MACHINE_APPROVAL_NEEDED",
204    "TAILNET.DISABLE.MAGIC_DNS", "TAILNET.DISABLE.MULLVAD_VPN",
205    "TAILNET.DISABLE.NETWORK_FLOW_LOGGING", "TAILNET.DISABLE.SCIM", "TAILNET.DISABLE.TKA",
206    "TAILNET.DISABLE.USER_APPROVAL_REQUIRED", "TAILNET.ENABLE.COLLECT_POSTURE_IDENTITY",
207    "TAILNET.ENABLE.COLLECT_SERVICES", "TAILNET.ENABLE.FILE_SHARING",
208    "TAILNET.ENABLE.GEOSTEERING", "TAILNET.ENABLE.HTTPS", "TAILNET.ENABLE.LOG_EXIT_FLOWS",
209    "TAILNET.ENABLE.MACHINE_APPROVAL_NEEDED", "TAILNET.ENABLE.MAGIC_DNS",
210    "TAILNET.ENABLE.MULLVAD_VPN", "TAILNET.ENABLE.NETWORK_FLOW_LOGGING",
211    "TAILNET.ENABLE.SCIM", "TAILNET.ENABLE.TKA", "TAILNET.ENABLE.USER_APPROVAL_REQUIRED",
212    "TAILNET.JOIN", "TAILNET.JOIN_WAITLIST.FEATURE", "TAILNET.LEAVE",
213    "TAILNET.UPDATE.ACCOUNT_EMAIL", "TAILNET.UPDATE.ACL", "TAILNET.UPDATE.DNS_CONFIG",
214    "TAILNET.UPDATE.LOGSTREAM_ENDPOINT", "TAILNET.UPDATE.MAX_KEY_DURATION",
215    "TAILNET.UPDATE.POSTURE_INTEGRATION", "TAILNET.UPDATE.ROUTE_SELECTION",
216    "TAILNET.UPDATE.SECURITY_EMAIL",
217    "TAILNET.UPDATE.SUPPORT_EMAIL", "TAILNET.UPDATE.TCD", "TAILNET.UPDATE.TKA",
218    "TAILNET.VERIFY.ACCOUNT_EMAIL", "TAILNET.VERIFY.SECURITY_EMAIL",
219    "TAILNET.VERIFY.SUPPORT_EMAIL", "USER.APPROVE", "USER.CREATE", "USER.DELETE",
220    "USER.INVITE", "USER.PUSH_USER.ATTRIBUTES", "USER.RESEND.TAILNET_INVITE",
221    "USER.RESTORE", "USER.RESTORE_GLOBAL", "USER.SUSPEND", "USER.SUSPEND_GLOBAL",
222    "USER.UPDATE.USER_ROLE", "WEBHOOK_ENDPOINT.CREATE", "WEBHOOK_ENDPOINT.DELETE",
223    "WEBHOOK_ENDPOINT.UPDATE.SECRET", "WEBHOOK_ENDPOINT.UPDATE.SUBSCRIBED_EVENTS",
224    "WEB_INTERFACE.LOGIN", "WEB_INTERFACE.LOGOUT", "PAM_CONNECTOR.CREATE",
225    "PAM_CONNECTOR.CREATE.ACCESS_TOKEN", "PAM_CONNECTOR.DELETE",
226    "PAM_CONNECTOR.DISABLE.ACCESS_TOKEN", "PAM_CONNECTOR.UPDATE", "PAM_SERVICE.CREATE",
227    "PAM_SERVICE.DELETE", "PAM_SERVICE.UPDATE", "PAM_SERVICE_ACCOUNT.CREATE",
228    "PAM_SERVICE_ACCOUNT.CREATE.ACCESS_TOKEN", "PAM_SERVICE_ACCOUNT.DELETE",
229    "PAM_SERVICE_ACCOUNT.UPDATE", "PAM_SETTINGS.CREATE.CUSTOM_DOMAIN",
230    "PAM_SETTINGS.CREATE.NOTIFICATION", "PAM_SETTINGS.CREATE.RECORDING_STORAGE",
231    "PAM_SETTINGS.DELETE.CUSTOM_DOMAIN", "PAM_SETTINGS.DELETE.NOTIFICATION",
232    "PAM_SETTINGS.DELETE.RECORDING_STORAGE", "PAM_SETTINGS.UPDATE",
233    "PAM_SETTINGS.UPDATE.CUSTOM_DOMAIN", "PAM_SETTINGS.UPDATE.NOTIFICATION",
234    "PAM_SETTINGS.UPDATE.SETUP_WIZARD",
235];
236
237pub const KNOWN_VALUES: &[KnownValues] = &[
238    ("LogType", LOG_TYPES),
239    (
240        "LogstreamEndpointConfiguration.destinationType",
241        DESTINATION_TYPES,
242    ),
243    (
244        "LogstreamEndpointConfiguration.compressionFormat",
245        COMPRESSION_FORMATS,
246    ),
247    (
248        "LogstreamEndpointConfiguration.s3AuthenticationType",
249        S3_AUTHENTICATION_TYPES,
250    ),
251    ("ConfigurationAuditLog.type", AUDIT_LOG_TYPES),
252    ("ConfigurationAuditLog.origin", AUDIT_ORIGINS),
253    ("ConfigurationAuditLog.actor.type", AUDIT_ACTOR_TYPES),
254    ("ConfigurationAuditLog.target.type", AUDIT_TARGET_TYPES),
255    (
256        "ConfigurationAuditLog.target.property",
257        AUDIT_TARGET_PROPERTIES,
258    ),
259    ("ConfigurationAuditLog.action", AUDIT_ACTIONS),
260    ("ConnectionCounts.proto", FLOW_PROTOCOLS),
261    ("?event[]", AUDIT_EVENTS),
262];
263
264model! {
265    /// One configuration change, as the audit log records it.
266    ConfigurationAuditLog {
267        event_time: "eventTime" => String,
268        /// One of [`AUDIT_LOG_TYPES`].
269        log_type: "type" => String,
270        /// Set where the rate limiter held the record back, naming the time it
271        /// was enqueued rather than the time it was written.
272        deferred_at: "deferredAt" => String,
273        /// Shared by every event that came out of one operation.
274        event_group_id: "eventGroupID" => String,
275        /// One of [`AUDIT_ORIGINS`].
276        origin: "origin" => String,
277        actor: "actor" => AuditActor,
278        target: "target" => AuditTarget,
279        /// One of [`AUDIT_ACTIONS`].
280        action: "action" => String,
281        /// `target.property` before the change; of whatever shape that
282        /// property has.
283        old: "old" => Value,
284        /// `target.property` after the change.
285        new: "new" => Value,
286        /// A reason, where the caller gave one.
287        action_details: "actionDetails" => String,
288        /// Present where the change failed, and readable by the person who
289        /// attempted it.
290        error: "error" => String,
291    }
292
293    /// Who or what made the change.
294    AuditActor as "ConfigurationAuditLog.actor" {
295        /// A user ID or a node ID, depending on `type`.
296        id: "id" => String,
297        /// One of [`AUDIT_ACTOR_TYPES`].
298        actor_type: "type" => String,
299        /// As it was at the time, not as it is now.
300        login_name: "loginName" => String,
301        display_name: "displayName" => String,
302        tags: "tags" => Vec<String>,
303    }
304
305    /// What the change was made to.
306    AuditTarget as "ConfigurationAuditLog.target" {
307        id: "id" => String,
308        /// As it was at the time.
309        name: "name" => String,
310        /// One of [`AUDIT_TARGET_TYPES`].
311        target_type: "type" => String,
312        /// Only meaningful where `type` is `NODE`.
313        is_ephemeral: "isEphemeral" => bool,
314        /// One of [`AUDIT_TARGET_PROPERTIES`]; what `old` and `new` hold.
315        property: "property" => String,
316    }
317
318    /// Traffic between two addresses over one protocol.
319    ConnectionCounts {
320        /// One of [`FLOW_PROTOCOLS`], or the protocol number for anything else.
321        proto: "proto" => String,
322        /// `addr:port`.
323        src: "src" => String,
324        /// `addr:port`.
325        dst: "dst" => String,
326        tx_pkts: "txPkts" => i64,
327        tx_bytes: "txBytes" => i64,
328        rx_pkts: "rxPkts" => i64,
329        rx_bytes: "rxBytes" => i64,
330    }
331
332    /// One node's traffic over one interval, by the path it took.
333    NetworkFlowLog {
334        logged: "logged" => String,
335        node_id: "nodeId" => String,
336        start: "start" => String,
337        end: "end" => String,
338        /// Tailscale address to Tailscale address.
339        virtual_traffic: "virtualTraffic" => Vec<ConnectionCounts>,
340        /// Through a subnet router.
341        subnet_traffic: "subnetTraffic" => Vec<ConnectionCounts>,
342        /// Through an exit node.
343        exit_traffic: "exitTraffic" => Vec<ConnectionCounts>,
344        /// The underlying transport, which is what the other three ride on.
345        physical_traffic: "physicalTraffic" => Vec<ConnectionCounts>,
346    }
347
348    /// Where a log stream goes and how it authenticates.
349    ///
350    /// Most of this is conditional on `destinationType`: the `s3*` fields
351    /// apply to S3, the `gcs*` fields to GCS, and `url`, `user` and `token` to
352    /// the vendors.
353    LogstreamEndpointConfiguration {
354        /// One of [`LOG_TYPES`].
355        log_type: "logType" => String,
356        /// One of [`DESTINATION_TYPES`].
357        destination_type: "destinationType" => String,
358        /// Often empty for S3, where the official endpoint is used.
359        url: "url" => String,
360        user: "user" => String,
361        /// A wait between uploads. Logs that do not fit in one upload are sent
362        /// in several regardless.
363        upload_period_minutes: "uploadPeriodMinutes" => i64,
364        /// One of [`COMPRESSION_FORMATS`], defaulting to `none`.
365        compression_format: "compressionFormat" => String,
366        token: "token" => Secret,
367        s3_bucket: "s3Bucket" => String,
368        s3_region: "s3Region" => String,
369        s3_key_prefix: "s3KeyPrefix" => String,
370        /// One of [`S3_AUTHENTICATION_TYPES`].
371        s3_authentication_type: "s3AuthenticationType" => String,
372        s3_access_key_id: "s3AccessKeyId" => String,
373        s3_secret_access_key: "s3SecretAccessKey" => Secret,
374        /// The role Tailscale assumes under `rolearn` authentication.
375        s3_role_arn: "s3RoleArn" => String,
376        /// What Tailscale presents to AWS under `rolearn` authentication; see
377        /// [`AwsExternalId`].
378        s3_external_id: "s3ExternalId" => String,
379        gcs_bucket: "gcsBucket" => String,
380        gcs_key_prefix: "gcsKeyPrefix" => String,
381        gcs_scopes: "gcsScopes" => Vec<String>,
382        /// Workload identity credentials, as GCS's own JSON document.
383        gcs_credentials: "gcsCredentials" => Secret,
384    }
385
386    /// How a log stream proves to AWS that it is this tailnet.
387    ///
388    /// The pair goes in the AWS role's trust policy, which is what makes
389    /// `rolearn` authentication work without a stored access key.
390    AwsExternalId {
391        external_id: "externalId" => String,
392        tailscale_aws_account_id: "tailscaleAwsAccountId" => String,
393    }
394
395    /// Whether the endpoint is being reached, and how well.
396    LogstreamEndpointPublishingStatus {
397        last_activity: "lastActivity" => String,
398        last_error: "lastError" => String,
399        max_body_size: "maxBodySize" => i64,
400        num_bytes_sent: "numBytesSent" => i64,
401        num_entries_sent: "numEntriesSent" => i64,
402        num_spoofed_entries: "numSpoofedEntries" => i64,
403        num_total_requests: "numTotalRequests" => i64,
404        num_failed_requests: "numFailedRequests" => i64,
405        rate_bytes_sent: "rateBytesSent" => f64,
406        rate_entries_sent: "rateEntriesSent" => f64,
407        rate_total_requests: "rateTotalRequests" => f64,
408        rate_failed_requests: "rateFailedRequests" => f64,
409    }
410
411    // -----------------------------------------------------------------------
412    // The shapes the routes carry.
413    // -----------------------------------------------------------------------
414
415    /// A page of the configuration audit log.
416    ///
417    /// `version` and `tailnet` come back beside the records, which is why this
418    /// is not simply a list.
419    AuditLogPage as "GET /tailnet/{tailnet}/logging/configuration 200" {
420        version: "version" => String,
421        tailnet: "tailnet" => String,
422        logs: "logs" => Vec<ConfigurationAuditLog>,
423    }
424
425    /// A page of the network flow log.
426    NetworkFlowLogPage as "GET /tailnet/{tailnet}/logging/network 200" {
427        logs: "logs" => Vec<NetworkFlowLog>,
428    }
429
430    /// What asking for an AWS external identifier sends.
431    AwsExternalIdRequest as "POST /tailnet/{tailnet}/aws-external-id body" {
432        /// Whether the identifier may be used for more than one role.
433        reusable: "reusable" => bool,
434    }
435
436    /// What checking an AWS trust policy sends.
437    AwsTrustPolicyRequest
438        as "POST /tailnet/{tailnet}/aws-external-id/{id}/validate-aws-trust-policy body" {
439        /// The role Tailscale should be able to assume.
440        role_arn: "roleArn" => String,
441    }
442
443    /// What a failed trust-policy check answers with.
444    ///
445    /// The one place in the description where a failure has a shape of its own
446    /// rather than the shared `Error`: the check answers 422 with what is
447    /// wrong with the policy.
448    AwsTrustPolicyFailure
449        as "POST /tailnet/{tailnet}/aws-external-id/{id}/validate-aws-trust-policy 422" {
450        message: "message" => String,
451    }
452}