我的工作流
高级
这是一个SecOps领域的自动化工作流,包含 16 个节点。主要使用 If, Ssh, Code, Notion, Discord 等节点。 全面的SSL证书监控,集成Discord警报和Notion
前置要求
- •Notion API Key
- •Discord Bot Token 或 Webhook
- •可能需要目标 API 的认证凭证
分类
工作流预览
可视化展示节点连接关系,支持缩放和平移
导出工作流
复制以下 JSON 配置到 n8n 导入,即可使用此工作流
{
"id": "REwp6JdhNjvbGQ1J",
"meta": {
"instanceId": "0728bbcb360bfc31668af41e0b3086e417a4d7ae6718b1f1eb6ce6f25309b188"
},
"name": "我的工作流",
"tags": [],
"nodes": [
{
"id": "106c2b40-43f5-432d-9fdd-446e7309abc7",
"name": "检查 SSL",
"type": "n8n-nodes-base.httpRequest",
"position": [
2300,
580
],
"parameters": {
"url": "=https://ssl-checker.io/api/v1/check/{{ $json[\"property_domains\"].replace(/^https?:\\/\\//, \"\").replace(/\\/$/, \"\") }}",
"options": {}
},
"typeVersion": 4.2
},
{
"id": "92e421ce-6f88-44d4-9018-8b39e9fad396",
"name": "到期提醒",
"type": "n8n-nodes-base.if",
"position": [
2660,
580
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "ee6e2ce8-569a-4f1f-91b5-2c55f605a16b",
"operator": {
"type": "number",
"operation": "lte"
},
"leftValue": "={{ $json.result.days_left }}",
"rightValue": 7
},
{
"id": "d82f8203-0908-4a48-9eb7-48e11555c1c2",
"operator": {
"name": "filter.operator.equals",
"type": "string",
"operation": "equals"
},
"leftValue": "",
"rightValue": ""
}
]
}
},
"typeVersion": 2.2
},
{
"id": "bafe935b-b283-428f-934a-49d03de5d38f",
"name": "每日触发器",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
1620,
960
],
"parameters": {
"rule": {
"interval": [
{
"triggerAtHour": 10
}
]
}
},
"typeVersion": 1.2
},
{
"id": "873fc171-6b69-4e80-b81a-f14d68c885f4",
"name": "便签",
"type": "n8n-nodes-base.stickyNote",
"position": [
540,
140
],
"parameters": {
"width": 920,
"height": 1760,
"content": "## 🔐 高级 SSL 健康监控器"
},
"typeVersion": 1
},
{
"id": "b3cf7e3e-3d7b-484c-bd26-2d1aa996d020",
"name": "便签1",
"type": "n8n-nodes-base.stickyNote",
"position": [
1560,
780
],
"parameters": {
"height": 380,
"content": "### 🕒 每日触发器"
},
"typeVersion": 1
},
{
"id": "d55ad3b8-6cf1-479c-9646-973ff2946964",
"name": "便签2",
"type": "n8n-nodes-base.stickyNote",
"position": [
1860,
780
],
"parameters": {
"height": 380,
"content": "### 📄 获取 URL"
},
"typeVersion": 1
},
{
"id": "fbb9752a-9231-4a3e-93ed-149770335f64",
"name": "便签3",
"type": "n8n-nodes-base.stickyNote",
"position": [
2260,
420
],
"parameters": {
"height": 320,
"content": "### 🔍 检查 SSL"
},
"typeVersion": 1
},
{
"id": "b5018cec-3c2e-4824-9d57-87157fc28616",
"name": "便签说明4",
"type": "n8n-nodes-base.stickyNote",
"disabled": true,
"position": [
2600,
420
],
"parameters": {
"height": 320,
"content": "### ⚠️ 到期提醒"
},
"typeVersion": 1
},
{
"id": "a9913ede-0f83-4352-bf31-3634b856c35e",
"name": "便签说明5",
"type": "n8n-nodes-base.stickyNote",
"disabled": true,
"position": [
2940,
420
],
"parameters": {
"width": 260,
"height": 320,
"content": "### 📧 发送 Discord 警报"
},
"typeVersion": 1
},
{
"id": "819e2cb7-d1a4-4fdd-b62a-d2378161373a",
"name": "便签 7",
"type": "n8n-nodes-base.stickyNote",
"position": [
2280,
1180
],
"parameters": {
"width": 520,
"height": 320,
"content": "### 🔍 检查 SSL 健康评分"
},
"typeVersion": 1
},
{
"id": "e5ef4591-f45c-4712-97b4-0d7a520c238d",
"name": "SSH - 分析系统",
"type": "n8n-nodes-base.ssh",
"position": [
2380,
1340
],
"parameters": {
"command": "=node /opt/sysadmin-toolkit/scripts/ssl/ssl-health-assessment.js {{ $json.property_domains }} --json",
"authentication": "privateKey"
},
"typeVersion": 1,
"alwaysOutputData": true
},
{
"id": "240f78ee-96ab-4f53-bb9c-c40e0f8d0961",
"name": "Discord",
"type": "n8n-nodes-base.discord",
"position": [
2980,
1340
],
"webhookId": "6e1014b5-d89d-4939-a1f3-b74c280ae73b",
"parameters": {
"content": "={{ $json.discord.title }}\n\n{{ $json.discord.fullDescription }}",
"guildId": {
"__rl": true,
"mode": "list",
"value": "1254730427805990942",
"cachedResultUrl": "https://discord.com/channels/1254730427805990942",
"cachedResultName": "b0ld8"
},
"options": {},
"resource": "message",
"channelId": {
"__rl": true,
"mode": "list",
"value": "1254730427805990945",
"cachedResultUrl": "https://discord.com/channels/1254730427805990942/1254730427805990945",
"cachedResultName": "general"
}
},
"typeVersion": 2
},
{
"id": "edc63e4c-8a87-4fbb-ba61-195b3e587965",
"name": "Discord1",
"type": "n8n-nodes-base.discord",
"position": [
3000,
560
],
"webhookId": "6e1014b5-d89d-4939-a1f3-b74c280ae73b",
"parameters": {
"content": "=SSL Expiry - {{ $json.result.days_left }} days Left - {{ $json.result.host }}",
"guildId": {
"__rl": true,
"mode": "list",
"value": "1254730427805990942",
"cachedResultUrl": "https://discord.com/channels/1254730427805990942",
"cachedResultName": "b0ld8"
},
"options": {},
"resource": "message",
"channelId": {
"__rl": true,
"mode": "list",
"value": "1254730427805990945",
"cachedResultUrl": "https://discord.com/channels/1254730427805990942/1254730427805990945",
"cachedResultName": "general"
}
},
"typeVersion": 2
},
{
"id": "c58aad29-de35-46e1-b43a-dfcfac0cffc2",
"name": "获取要检查 SSL 的域",
"type": "n8n-nodes-base.notion",
"position": [
1920,
960
],
"parameters": {
"options": {},
"resource": "databasePage",
"operation": "getAll",
"returnAll": true,
"databaseId": {
"__rl": true,
"mode": "list",
"value": "22612359-20f8-80f4-897f-eae0ad313057",
"cachedResultUrl": "https://www.notion.so/2261235920f880f4897feae0ad313057",
"cachedResultName": "n8n flow - SSL Certificate Expiry Monitoring"
},
"filterType": "manual"
},
"typeVersion": 2.2
},
{
"id": "b544b226-ceb6-439d-b350-4e77b39e2fde",
"name": "代码 - 格式化输出",
"type": "n8n-nodes-base.code",
"position": [
2620,
1340
],
"parameters": {
"jsCode": "// n8n Code Node - Parse SSL Scanner Output\n// This code processes the SSH node output and formats it for Discord notifications\n\nconst results = [];\n\nfor (const item of $input.all()) {\n try {\n // Extract the stdout from SSH response\n const sshOutput = item.json;\n \n // Check if SSH command was successful\n if (sshOutput.code !== 0) {\n results.push({\n json: {\n hostname: 'unknown',\n status: 'error',\n error: sshOutput.stderr || 'SSH command failed',\n alert: true,\n alertLevel: 'critical'\n }\n });\n continue;\n }\n \n // Parse the JSON output from stdout\n let sslData;\n try {\n sslData = JSON.parse(sshOutput.stdout);\n } catch (parseError) {\n results.push({\n json: {\n hostname: 'unknown',\n status: 'error',\n error: 'Failed to parse SSL scanner output',\n alert: true,\n alertLevel: 'critical'\n }\n });\n continue;\n }\n \n // Extract key information for Discord notification\n const parsedResult = {\n // Basic info\n hostname: sslData.hostname,\n port: sslData.port || 443,\n scanTime: sslData.scanTime,\n scanDuration: sslData.scanDuration,\n \n // Overall status\n overallGrade: sslData.overallGrade,\n connectionStatus: sslData.connectionStatus || 'unknown',\n success: sslData.success || false,\n \n // Certificate info\n certificate: {\n subject: sslData.certificate?.subject || 'unknown',\n issuer: sslData.certificate?.issuer || 'unknown',\n validUntil: sslData.certificate?.validUntil,\n daysUntilExpiry: sslData.certificate?.daysUntilExpiry,\n isValid: sslData.certificate?.isValid || false,\n issues: sslData.certificate?.issues || [],\n hostnameMatch: sslData.certificate?.hostnameMatch || false\n },\n \n // Protocol support\n protocols: {\n tls13: sslData.protocols?.tls13 || false,\n tls12: sslData.protocols?.tls12 || false,\n tls11: sslData.protocols?.tls11 || false,\n tls10: sslData.protocols?.tls10 || false,\n ssl3: sslData.protocols?.ssl3 || false\n },\n \n // Security info\n vulnerabilities: {\n hasVulnerabilities: sslData.vulnerabilities?.hasVulnerabilities || false,\n poodle: sslData.vulnerabilities?.poodle || false,\n freak: sslData.vulnerabilities?.freak || false,\n details: sslData.vulnerabilities || {}\n },\n \n // Grades breakdown\n grades: sslData.grades || {},\n \n // Recommendations\n recommendations: sslData.recommendations || [],\n \n // Error handling\n error: sslData.error,\n \n // Alert logic\n alert: false,\n alertLevel: 'info',\n alertReasons: []\n };\n \n // Determine if this needs an alert\n const alertReasons = [];\n let alertLevel = 'info';\n \n // Critical alerts\n if (sslData.error) {\n alertReasons.push(`Scan failed: ${sslData.error}`);\n alertLevel = 'critical';\n }\n \n if (!parsedResult.certificate.isValid) {\n alertReasons.push('Certificate is invalid');\n alertLevel = 'critical';\n }\n \n if (parsedResult.certificate.daysUntilExpiry <= 0) {\n alertReasons.push('Certificate has expired');\n alertLevel = 'critical';\n }\n \n // High priority alerts\n if (parsedResult.certificate.daysUntilExpiry <= 7 && parsedResult.certificate.daysUntilExpiry > 0) {\n alertReasons.push(`Certificate expires in ${parsedResult.certificate.daysUntilExpiry} days`);\n if (alertLevel === 'info') alertLevel = 'high';\n }\n \n if (parsedResult.vulnerabilities.hasVulnerabilities) {\n alertReasons.push('Security vulnerabilities detected');\n if (alertLevel === 'info') alertLevel = 'high';\n }\n \n if (!parsedResult.certificate.hostnameMatch) {\n alertReasons.push('Hostname mismatch detected');\n if (alertLevel === 'info') alertLevel = 'high';\n }\n \n // Medium priority alerts\n if (parsedResult.certificate.daysUntilExpiry <= 30 && parsedResult.certificate.daysUntilExpiry > 7) {\n alertReasons.push(`Certificate expires in ${parsedResult.certificate.daysUntilExpiry} days`);\n if (alertLevel === 'info') alertLevel = 'medium';\n }\n \n if (['C', 'D', 'F'].includes(parsedResult.overallGrade)) {\n alertReasons.push(`Poor SSL grade: ${parsedResult.overallGrade}`);\n if (alertLevel === 'info') alertLevel = 'medium';\n }\n \n if (parsedResult.protocols.ssl3 || parsedResult.protocols.tls10) {\n alertReasons.push('Deprecated protocols enabled');\n if (alertLevel === 'info') alertLevel = 'medium';\n }\n \n // Low priority alerts\n if (parsedResult.recommendations && parsedResult.recommendations.length > 0) {\n alertReasons.push(`${parsedResult.recommendations.length} recommendations available`);\n if (alertLevel === 'info') alertLevel = 'low';\n }\n \n // Set alert status\n parsedResult.alert = alertReasons.length > 0;\n parsedResult.alertLevel = alertLevel;\n parsedResult.alertReasons = alertReasons;\n \n // Add Discord formatting helpers\n parsedResult.discord = {\n color: getDiscordColor(alertLevel, parsedResult.overallGrade),\n title: `SSL Health Check: ${parsedResult.hostname}`,\n description: getDiscordDescription(parsedResult),\n fields: getDiscordFields(parsedResult),\n timestamp: new Date().toISOString(),\n \n // Pre-formatted content for easy Discord input\n content: alertLevel === 'critical' ? '@here 🚨 SSL Health Monitor Alert' : '🔐 SSL Health Monitor Alert',\n \n // Simple descriptions without complex logic\n simpleDescription: parsedResult.alert \n ? `⚠️ SSL issues detected for ${parsedResult.hostname}`\n : `✅ SSL looks healthy for ${parsedResult.hostname}`,\n \n // Detailed info as simple strings\n certificateInfo: `Grade: ${parsedResult.overallGrade} | Expires in ${parsedResult.certificate.daysUntilExpiry} days`,\n \n // Alert details as simple string\n alertSummary: parsedResult.alert \n ? `Issues: ${parsedResult.alertReasons.join(', ')}`\n : `Certificate expires ${parsedResult.certificate.validUntil}`,\n \n // Complete formatted description\n fullDescription: getFullDiscordDescription(parsedResult)\n };\n \n results.push({ json: parsedResult });\n \n } catch (error) {\n // Handle any unexpected errors\n results.push({\n json: {\n hostname: 'unknown',\n status: 'error',\n error: `Processing error: ${error.message}`,\n alert: true,\n alertLevel: 'critical'\n }\n });\n }\n}\n\n// Helper functions for Discord formatting\nfunction getDiscordColor(alertLevel, grade) {\n // Color mapping for Discord embeds\n const colors = {\n critical: 15158332, // Red\n high: 15105570, // Orange\n medium: 15844367, // Yellow\n low: 5763719, // Blue\n info: 5763719 // Blue\n };\n \n // Grade-based colors for non-alerts\n const gradeColors = {\n 'A+': 5763719, // Green\n 'A': 3066993, // Green\n 'B': 15844367, // Yellow\n 'C': 15105570, // Orange\n 'D': 15158332, // Red\n 'F': 10038562 // Dark Red\n };\n \n if (alertLevel !== 'info') {\n return colors[alertLevel] || colors.info;\n }\n \n return gradeColors[grade] || colors.info;\n}\n\nfunction getFullDiscordDescription(result) {\n if (result.error) {\n return `❌ SSL scan failed: ${result.error}`;\n }\n \n if (result.alert) {\n let description = `⚠️ SSL issues detected for ${result.hostname}\\n`;\n description += `Grade: ${result.overallGrade} | Expires in ${result.certificate.daysUntilExpiry} days\\n\\n`;\n description += `Issues found:\\n`;\n result.alertReasons.forEach(reason => {\n description += `• ${reason}\\n`;\n });\n return description.trim();\n }\n \n return `✅ SSL configuration looks good for ${result.hostname}\\nGrade: ${result.overallGrade} | Certificate expires in ${result.certificate.daysUntilExpiry} days`;\n}\n\nfunction getDiscordDescription(result) {\n if (result.error) {\n return `❌ SSL scan failed: ${result.error}`;\n }\n \n if (result.alert) {\n // Create simple alert description\n let description = \"⚠️ SSL issues detected:\\n\";\n result.alertReasons.forEach(reason => {\n description += `• ${reason}\\n`;\n });\n return description.trim();\n }\n \n return `✅ SSL configuration looks good\\nGrade: ${result.overallGrade} | Expires in ${result.certificate.daysUntilExpiry} days`;\n}\n\nfunction getDiscordFields(result) {\n const fields = [];\n \n // Certificate info\n fields.push({\n name: \"📜 Certificate\",\n value: [\n `**Subject:** ${result.certificate.subject}`,\n `**Issuer:** ${result.certificate.issuer}`,\n `**Expires:** ${new Date(result.certificate.validUntil).toLocaleDateString()}`,\n `**Days Left:** ${result.certificate.daysUntilExpiry}`\n ].join('\\n'),\n inline: true\n });\n \n // Security info\n fields.push({\n name: \"🔐 Security\",\n value: [\n `**Overall Grade:** ${result.overallGrade}`,\n `**TLS 1.2:** ${result.protocols.tls12 ? '✅' : '❌'}`,\n `**TLS 1.3:** ${result.protocols.tls13 ? '✅' : '❌'}`,\n `**Vulnerabilities:** ${result.vulnerabilities.hasVulnerabilities ? '⚠️ Found' : '✅ None'}`\n ].join('\\n'),\n inline: true\n });\n \n // Add recommendations if any\n if (result.recommendations.length > 0) {\n fields.push({\n name: \"💡 Recommendations\",\n value: result.recommendations.slice(0, 3).map(rec => `• ${rec.message || rec}`).join('\\n') + \n (result.recommendations.length > 3 ? `\\n... and ${result.recommendations.length - 3} more` : ''),\n inline: false\n });\n }\n \n return fields;\n}\n\nreturn results;"
},
"typeVersion": 2
},
{
"id": "8e86994c-5ebc-4600-80b1-ad187b17d125",
"name": "便签 6",
"type": "n8n-nodes-base.stickyNote",
"disabled": true,
"position": [
2900,
1180
],
"parameters": {
"width": 260,
"height": 320,
"content": "### 📧 发送 Discord 警报"
},
"typeVersion": 1
}
],
"active": false,
"pinData": {},
"settings": {
"executionOrder": "v1"
},
"versionId": "746cb098-9c5f-4b65-9bd2-c7f20d40df61",
"connections": {
"Check SSL": {
"main": [
[
{
"node": "Expiry Alert",
"type": "main",
"index": 0
}
]
]
},
"Expiry Alert": {
"main": [
[
{
"node": "Discord1",
"type": "main",
"index": 0
}
]
]
},
"Daily Trigger": {
"main": [
[
{
"node": "Fetch domains to check SSL",
"type": "main",
"index": 0
}
]
]
},
"Code - Format output": {
"main": [
[
{
"node": "Discord",
"type": "main",
"index": 0
}
]
]
},
"SSH - Analyze system": {
"main": [
[
{
"node": "Code - Format output",
"type": "main",
"index": 0
}
]
]
},
"Fetch domains to check SSL": {
"main": [
[
{
"node": "SSH - Analyze system",
"type": "main",
"index": 0
},
{
"node": "Check SSL",
"type": "main",
"index": 0
}
]
]
}
}
}常见问题
如何使用这个工作流?
复制上方的 JSON 配置代码,在您的 n8n 实例中创建新工作流并选择「从 JSON 导入」,粘贴配置后根据需要修改凭证设置即可。
这个工作流适合什么场景?
高级 - 安全运维
需要付费吗?
本工作流完全免费,您可以直接导入使用。但请注意,工作流中使用的第三方服务(如 OpenAI API)可能需要您自行付费。
相关工作流推荐
基于Notion和Telegram的自动化SSL证书监控与续期
使用Notion和Telegram实现SSL证书的自动化监控与续期
If
Set
Ssh
+10
21 节点Frank Chen
安全运维
AI驱动的域名与IP安全检查自动化
AI驱动的域名与IP安全检查自动化
If
Code
Http Request
+6
50 节点Garri
安全运维
CyberScan Github 副本
基于 Nessus、风险分级和 Google Sheets 报告的 AI 漏洞扫描器
If
Set
Code
+8
39 节点Adnan Tariq
安全运维
CYBERPULSEBlueOps_模块1 客户端副本1
自动CVE和IOC数据源摄取,含OpenAI风险评估和邮件警报
If
Code
Merge
+7
21 节点Adnan Tariq
安全运维
使用Nuclei和Project Discovery的漏洞赏金项目自动CVE扫描
使用Nuclei和Project Discovery的漏洞赏金项目自动CVE扫描
If
Set
Ssh
+9
32 节点Javier Rieiro
安全运维
基于Slack的自动化域名/IP黑名单监控
使用Slack警报监控AbuseIPDB黑名单中的域名和IP
If
Code
Slack
+3
7 节点Marth
安全运维
工作流信息
难度等级
高级
节点数量16
分类1
节点类型8
作者
Tom Cao
@tomcaoFounder at Bubobot - AI-powered monitoring platform Sharing n8n automation flows to help DevOps engineers and IT admins build better monitoring and incident response workflows. 🚀
外部链接
在 n8n.io 查看 →
分享此工作流