Skip to content

Commit 81681eb

Browse files
authored
Update technitium-dns-to-unifi-log-insights.json
1 parent 2598327 commit 81681eb

1 file changed

Lines changed: 14 additions & 72 deletions

File tree

technitium-dns-to-unifi-log-insights.json

Lines changed: 14 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"name": "Technitium DNS -> UniFi Log Insights DNS (Public Template) v0.1.1",
2+
"name": "Technitium DNS -> UniFi Log Insights DNS (Public Template) v0.1.2",
33
"nodes": [
44
{
55
"parameters": {
@@ -10,10 +10,7 @@
1010
},
1111
"type": "n8n-nodes-base.webhook",
1212
"typeVersion": 2.1,
13-
"position": [
14-
-1424,
15-
-208
16-
],
13+
"position": [-1424, -208],
1714
"id": "92c183f1-3018-41d4-a600-1dadfa8e7ebd",
1815
"name": "Webhook"
1916
},
@@ -44,10 +41,7 @@
4441
},
4542
"type": "n8n-nodes-base.if",
4643
"typeVersion": 2.3,
47-
"position": [
48-
-1200,
49-
-208
50-
],
44+
"position": [-1200, -208],
5145
"id": "32dc90c4-0891-45d9-9ebf-73297e5b9f27",
5246
"name": "If"
5347
},
@@ -61,10 +55,7 @@
6155
},
6256
"type": "n8n-nodes-base.respondToWebhook",
6357
"typeVersion": 1.5,
64-
"position": [
65-
-528,
66-
-304
67-
],
58+
"position": [-528, -304],
6859
"id": "05473b99-1c82-4b15-9da9-e6b3a97ebdc7",
6960
"name": "Respond to Webhook 'true'"
7061
},
@@ -78,10 +69,7 @@
7869
},
7970
"type": "n8n-nodes-base.respondToWebhook",
8071
"typeVersion": 1.5,
81-
"position": [
82-
-976,
83-
-112
84-
],
72+
"position": [-976, -112],
8573
"id": "c515fd27-ea49-49c8-8d24-740c03752a3a",
8674
"name": "Respond to Webhook 'forbidden'"
8775
},
@@ -91,83 +79,37 @@
9179
},
9280
"type": "n8n-nodes-base.executeCommand",
9381
"typeVersion": 1,
94-
"position": [
95-
-752,
96-
-304
97-
],
82+
"position": [-752, -304],
9883
"id": "d522ddd8-3ae5-4ca9-9b20-4b9d4cd90c88",
9984
"name": "Execute Command"
10085
},
10186
{
10287
"parameters": {
103-
"jsCode": "const inputItem = $input.first();\nconst payload = inputItem.json?.body ?? [];\nconst entries = Array.isArray(payload) ? payload : [payload];\n\nconst blockedTypes = new Set([\"SOA\", \"IXFR\"]);\nconst blockedClientIps = new Set([\n \"192.0.2.10\",\n \"192.0.2.11\",\n \"198.51.100.10\",\n \"172.16.0.1\",\n]);\n\n// RFC3164 BSD timestamp in UTC with space-padded day (\"Apr 4\").\n// The unifi-log-insight container must also run in UTC (its default) so\n// the receiver's parse_syslog_timestamp() interprets these correctly.\nfunction formatRfc3164(ts) {\n const date = ts ? new Date(ts) : new Date();\n if (Number.isNaN(date.getTime())) {\n throw new Error(Invalid timestamp: ${ts});\n }\n const months = ['Jan','Feb','Mar','Apr','May','Jun',\n 'Jul','Aug','Sep','Oct','Nov','Dec'];\n const mon = months[date.getUTCMonth()];\n const day = date.getUTCDate();\n const dd = day < 10 ? ${day} : ${day};\n const hh = String(date.getUTCHours()).padStart(2, '0');\n const mm = String(date.getUTCMinutes()).padStart(2, '0');\n const ss = String(date.getUTCSeconds()).padStart(2, '0');\n return ${mon} ${dd} ${hh}:${mm}:${ss};\n}\n\nfunction tryParseJsonString(value) {\n if (typeof value !== \"string\" || !value.trim()) return null;\n try {\n return JSON.parse(value);\n } catch {\n return null;\n }\n}\n\nconst out = [];\n\nfor (const entry of entries) {\n const parsed =\n tryParseJsonString(entry.RenderedMessage) ??\n tryParseJsonString(entry.MessageTemplate) ??\n (entry && typeof entry === \"object\" ? entry : null);\n\n if (!parsed || typeof parsed !== \"object\") {\n out.push({\n json: {\n ok: true,\n forwarded: false,\n skipReason: \"unparsable_log_entry\",\n original: entry,\n },\n });\n continue;\n }\n\n const timestampRaw =\n parsed.timestamp ??\n parsed.Timestamp ??\n entry.Timestamp ??\n null;\n\n const clientIpRaw =\n parsed.clientIp ??\n parsed.clientIPAddress ??\n parsed.client ??\n parsed.remoteAddress ??\n null;\n\n const question = parsed.question ?? {};\n const questionName =\n question.questionName ??\n question.name ??\n parsed.questionName ??\n parsed.domain ??\n null;\n\n const questionType =\n question.questionType ??\n question.type ??\n parsed.questionType ??\n parsed.type ??\n null;\n\n const protocolRaw = String(parsed.protocol ?? \"\").trim();\n const responseCodeRaw = String(parsed.responseCode ?? \"\").trim();\n const responseTypeRaw = String(parsed.responseType ?? \"\").trim();\n\n const normalizedType = String(questionType ?? \"\").trim().toUpperCase();\n const normalizedName = String(questionName ?? \"\").trim();\n\n // Strip optional brackets and port from client IPs (e.g. \"[::1]:5353\" -> \"::1\").\n let normalizedClientIp = String(clientIpRaw ?? \"\").trim();\n const ipMatch = normalizedClientIp.match(/^\\[?([0-9a-fA-F:.]+)\\]?/);\n if (ipMatch) normalizedClientIp = ipMatch[1];\n\n let skipReason = null;\n\n if (!normalizedType) {\n skipReason = \"missing_question_type\";\n } else if (!normalizedName) {\n skipReason = \"missing_question_name\";\n } else if (!normalizedClientIp) {\n skipReason = \"missing_client_ip\";\n } else if (blockedTypes.has(normalizedType)) {\n skipReason = filtered_type_${normalizedType};\n } else if (blockedClientIps.has(normalizedClientIp)) {\n skipReason = filtered_client_${normalizedClientIp};\n }\n\n if (skipReason) {\n out.push({\n json: {\n ok: true,\n forwarded: false,\n skipReason,\n original: parsed,\n originalEntry: entry,\n },\n });\n continue;\n }\n\n const rfc3164Timestamp = formatRfc3164(timestampRaw);\n\n const extras = [\n protocolRaw ? proto=${protocolRaw} : null,\n responseCodeRaw ? rcode=${responseCodeRaw} : null,\n responseTypeRaw ? rtype=${responseTypeRaw} : null,\n ].filter(Boolean).join(\" \");\n\n const syslogLine =\n <14>${rfc3164Timestamp} technitium-dns dnsmasq[1]: +\n query[${normalizedType}] ${normalizedName} from ${normalizedClientIp} +\n (extras ? ${extras} : \"\");\n\n out.push({\n json: {\n ok: true,\n forwarded: true,\n skipReason: null,\n syslogLine,\n parsed: {\n timestampRaw,\n clientIp: normalizedClientIp,\n questionName: normalizedName,\n questionType: normalizedType,\n protocol: protocolRaw || null,\n responseCode: responseCodeRaw || null,\n responseType: responseTypeRaw || null,\n },\n original: parsed,\n originalEntry: entry,\n },\n });\n}\n\nreturn out;\n"
88+
"jsCode": "const inputItem = $input.first();\nconst payload = inputItem.json?.body ?? [];\nconst entries = Array.isArray(payload) ? payload : [payload];\n\nconst blockedTypes = new Set([\"SOA\", \"IXFR\"]);\nconst blockedClientIps = new Set([\n \"192.0.2.10\",\n \"192.0.2.11\",\n \"198.51.100.10\",\n \"172.16.0.1\",\n]);\n\n// RFC3164 BSD timestamp in UTC with space-padded day (\"Apr 4\").\n// The unifi-log-insight container must also run in UTC (its default) so\n// the receiver's parse_syslog_timestamp() interprets these correctly.\nfunction formatRfc3164(ts) {\n const date = ts ? new Date(ts) : new Date();\n if (Number.isNaN(date.getTime())) {\n throw new Error(`Invalid timestamp: ${ts}`);\n }\n const months = ['Jan','Feb','Mar','Apr','May','Jun',\n 'Jul','Aug','Sep','Oct','Nov','Dec'];\n const mon = months[date.getUTCMonth()];\n const day = date.getUTCDate();\n const dd = day < 10 ? ` ${day}` : `${day}`;\n const hh = String(date.getUTCHours()).padStart(2, '0');\n const mm = String(date.getUTCMinutes()).padStart(2, '0');\n const ss = String(date.getUTCSeconds()).padStart(2, '0');\n return `${mon} ${dd} ${hh}:${mm}:${ss}`;\n}\n\nfunction tryParseJsonString(value) {\n if (typeof value !== \"string\" || !value.trim()) return null;\n try {\n return JSON.parse(value);\n } catch {\n return null;\n }\n}\n\nconst out = [];\n\nfor (const entry of entries) {\n const parsed =\n tryParseJsonString(entry.RenderedMessage) ??\n tryParseJsonString(entry.MessageTemplate) ??\n (entry && typeof entry === \"object\" ? entry : null);\n\n if (!parsed || typeof parsed !== \"object\") {\n out.push({\n json: {\n ok: true,\n forwarded: false,\n skipReason: \"unparsable_log_entry\",\n original: entry,\n },\n });\n continue;\n }\n\n const timestampRaw =\n parsed.timestamp ??\n parsed.Timestamp ??\n entry.Timestamp ??\n null;\n\n const clientIpRaw =\n parsed.clientIp ??\n parsed.clientIPAddress ??\n parsed.client ??\n parsed.remoteAddress ??\n null;\n\n const question = parsed.question ?? {};\n const questionName =\n question.questionName ??\n question.name ??\n parsed.questionName ??\n parsed.domain ??\n null;\n\n const questionType =\n question.questionType ??\n question.type ??\n parsed.questionType ??\n parsed.type ??\n null;\n\n const protocolRaw = String(parsed.protocol ?? \"\").trim();\n const responseCodeRaw = String(parsed.responseCode ?? \"\").trim();\n const responseTypeRaw = String(parsed.responseType ?? \"\").trim();\n\n const normalizedType = String(questionType ?? \"\").trim().toUpperCase();\n const normalizedName = String(questionName ?? \"\").trim();\n\n // Strip optional brackets and port from client IPs (e.g. \"[::1]:5353\" -> \"::1\").\n let normalizedClientIp = String(clientIpRaw ?? \"\").trim();\n const ipMatch = normalizedClientIp.match(/^\\[?([0-9a-fA-F:.]+)\\]?/);\n if (ipMatch) normalizedClientIp = ipMatch[1];\n\n let skipReason = null;\n\n if (!normalizedType) {\n skipReason = \"missing_question_type\";\n } else if (!normalizedName) {\n skipReason = \"missing_question_name\";\n } else if (!normalizedClientIp) {\n skipReason = \"missing_client_ip\";\n } else if (blockedTypes.has(normalizedType)) {\n skipReason = `filtered_type_${normalizedType}`;\n } else if (blockedClientIps.has(normalizedClientIp)) {\n skipReason = `filtered_client_${normalizedClientIp}`;\n }\n\n if (skipReason) {\n out.push({\n json: {\n ok: true,\n forwarded: false,\n skipReason,\n original: parsed,\n originalEntry: entry,\n },\n });\n continue;\n }\n\n const rfc3164Timestamp = formatRfc3164(timestampRaw);\n\n const extras = [\n protocolRaw ? `proto=${protocolRaw}` : null,\n responseCodeRaw ? `rcode=${responseCodeRaw}` : null,\n responseTypeRaw ? `rtype=${responseTypeRaw}` : null,\n ].filter(Boolean).join(\" \");\n\n const syslogLine =\n `<14>${rfc3164Timestamp} technitium-dns dnsmasq[1]: ` +\n `query[${normalizedType}] ${normalizedName} from ${normalizedClientIp}` +\n (extras ? ` ${extras}` : \"\");\n\n out.push({\n json: {\n ok: true,\n forwarded: true,\n skipReason: null,\n syslogLine,\n parsed: {\n timestampRaw,\n clientIp: normalizedClientIp,\n questionName: normalizedName,\n questionType: normalizedType,\n protocol: protocolRaw || null,\n responseCode: responseCodeRaw || null,\n responseType: responseTypeRaw || null,\n },\n original: parsed,\n originalEntry: entry,\n },\n });\n}\n\nreturn out;\n"
10489
},
10590
"type": "n8n-nodes-base.code",
10691
"typeVersion": 2,
107-
"position": [
108-
-976,
109-
-304
110-
],
92+
"position": [-976, -304],
11193
"id": "ef2d3bd2-1818-4ebd-9f7d-580c0a63a82a",
11294
"name": "Transform -> RFC3164"
11395
}
11496
],
11597
"connections": {
11698
"Webhook": {
117-
"main": [
118-
[
119-
{
120-
"node": "If",
121-
"type": "main",
122-
"index": 0
123-
}
124-
]
125-
]
99+
"main": [[{ "node": "If", "type": "main", "index": 0 }]]
126100
},
127101
"If": {
128102
"main": [
129-
[
130-
{
131-
"node": "Transform -> RFC3164",
132-
"type": "main",
133-
"index": 0
134-
}
135-
],
136-
[
137-
{
138-
"node": "Respond to Webhook 'forbidden'",
139-
"type": "main",
140-
"index": 0
141-
}
142-
]
143-
]
144-
},
145-
"Respond to Webhook 'forbidden'": {
146-
"main": [
147-
[]
103+
[{ "node": "Transform -> RFC3164", "type": "main", "index": 0 }],
104+
[{ "node": "Respond to Webhook 'forbidden'", "type": "main", "index": 0 }]
148105
]
149106
},
107+
"Respond to Webhook 'forbidden'": { "main": [[]] },
150108
"Execute Command": {
151-
"main": [
152-
[
153-
{
154-
"node": "Respond to Webhook 'true'",
155-
"type": "main",
156-
"index": 0
157-
}
158-
]
159-
]
109+
"main": [[{ "node": "Respond to Webhook 'true'", "type": "main", "index": 0 }]]
160110
},
161111
"Transform -> RFC3164": {
162-
"main": [
163-
[
164-
{
165-
"node": "Execute Command",
166-
"type": "main",
167-
"index": 0
168-
}
169-
]
170-
]
112+
"main": [[{ "node": "Execute Command", "type": "main", "index": 0 }]]
171113
}
172114
},
173115
"active": true,

0 commit comments

Comments
 (0)