8
n8n 中文网amn8n.com

我的工作流

高级

这是一个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)可能需要您自行付费。

工作流信息
难度等级
高级
节点数量16
分类1
节点类型8
难度说明

适合高级用户,包含 16+ 个节点的复杂工作流

作者
Tom Cao

Tom Cao

@tomcao

Founder 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 查看

分享此工作流