Files
rust_raidguardx/GC_EmailAlert.md
T
2026-03-29 12:07:12 +08:00

13 KiB

Email Alert 架構分析

概述

RAIDGuard X 支援透過 SMTP 發送郵件通知功能,包括:

  • 手動發送測試郵件
  • 事件觸發自動郵件通知

架構流程

┌─────────────────────────────────────────────────────────────────────┐
│                        Email Alert 架構                            │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  ┌──────────────┐        ┌──────────────┐        ┌──────────────┐ │
│  │   GUI        │        │  Controller  │        │  SMTP Server │ │
│  │  frmEmail    │◄──────►│   (Port 8922)│        │              │ │
│  └──────┬───────┘        └──────────────┘        └──────┬───────┘ │
│         │                                                │         │
│         │  In-Band API (OpCode 0x01)                   │         │
│         │  - SMTP 伺服器                                │         │
│         │  - SMTP 使用者名稱/密碼                       │         │
│         │  - 寄件人/收件人                              │         │
│         │  - 啟用事件通知                               │         │
│         └────────────────────────┬───────────────────────┘         │
│                                  │                                 │
│                                  ▼                                 │
│  ┌──────────────────────────────────────────────────────────────┐  │
│  │              JavaMail API (javax.mail)                      │  │
│  │  - Session.getInstance()                                   │  │
│  │  - MimeMessage                                             │  │
│  │  - Transport.send()                                        │  │
│  └──────────────────────────────────────────────────────────────┘  │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

設定介面 (frmEmail)

┌─────────────────────────────────────────────────────────────────┐
│  Email Settings                                                │
├─────────────────────────────────────────────────────────────────┤
│  Mailing List:                              [Send Test Email]  │
│  ┌─────────────────────────────────────────┐                  │
│  │ user1@example.com                       │ [Remove]         │
│  │ user2@example.com                       │                  │
│  │                                         │                  │
│  └─────────────────────────────────────────┘                  │
│                                                                  │
│  SMTP Settings:                                                 │
│  ┌───────────────────────────────────────────────────────────┐  │
│  │ Mail Server (SMTP):  [smtp.example.com    ]              │  │
│  │ From Email:         [sender@example.com   ]              │  │
│  │ ☐ SMTP Auth        Username: [__________]                │  │
│  │                    Password: [__________]                │  │
│  │ ☐ Post Event to Email (Enable auto-alert)               │  │
│  └───────────────────────────────────────────────────────────┘  │
│                                                                  │
│                                    [OK]  [Cancel]               │
└─────────────────────────────────────────────────────────────────┘

儲存的設定

透過 In-Band API 儲存到控制器:

OpCode 說明
0x07 更新 SMTP 伺服器名稱
0x09 更新 SMTP 使用者名稱
0x0A 更新 SMTP 密碼
0x0B 取得 SMTP 使用者名稱
0x0C 取得 SMTP 密碼
0x05 取得 SMTP 伺服器名稱
0x0E 儲存 Post Event 設定
0x19 取得 Post Event 設定

程式碼分析

1. 郵件發送 (frmEmail.java)

// 使用 JavaMail API
import javax.mail.*;
import javax.mail.internet.*;

public void jbSendMail_actionPerformed(ActionEvent e) {
    // 建立 Session
    Properties props = new Properties();
    props.put("mail.smtp.host", this.jtServerIp.getText());
    
    if (jcSMTPCheck.isSelected()) {
        // SMTP 認證
        props.put("mail.smtp.auth", "true");
        props.put("mail.smtp.user", sUsername);
        props.put("mail.smtp.password", sPassword);
        session = Session.getInstance(props, new Authenticator() {
            protected PasswordAuthentication getPasswordAuthentication() {
                return new PasswordAuthentication(sUsername, sPassword);
            }
        });
    } else {
        // 無認證
        props.put("mail.smtp.auth", "false");
        session = Session.getInstance(props);
    }
    
    // 建立郵件
    Message msg = new MimeMessage(session);
    msg.setFrom(new InternetAddress(sNameMailFrom));
    msg.setSubject(sMailTitle);
    msg.setText(sMailBody);
    msg.setSentDate(new Date());
    
    // 發送到所有收件人
    for (int j = 0; j < dmMailList.getRowCount(); j++) {
        sNameMailTo = dmMailList.getValueAt(j, 0).toString().trim();
        if (!sNameMailTo.equals("")) {
            msg.setRecipients(Message.RecipientType.TO,
                InternetAddress.parse(sNameMailTo, false));
            Transport.send(msg);  // 發送郵件
        }
    }
}

2. SMTP 認證設定

// 有認證
if (jcSMTPCheck.isSelected()) {
    props.put("mail.smtp.auth", "true");
    props.put("mail.smtp.ssl.enable", "true");  // SSL
    session = Session.getInstance(props, new Authenticator() {
        protected PasswordAuthentication getPasswordAuthentication() {
            return new PasswordAuthentication(username, password);
        }
    });
}

// 無認證
else {
    props.put("mail.smtp.host", smtpServer);
    props.put("mail.smtp.auth", "false");
    session = Session.getInstance(props);
}

3. 事件通知開關

// 儲存 Post Event 設定到控制器
bCmds[2] = 0x0E;  // OpCode

if (jcPostEvent.isSelected()) {
    bCmds[4] = 1;  // Enable
} else {
    bCmds[4] = 0;  // Disable
}

// 透過 CmdQueue 發送到控制器
CmdQueue.NewCommand(ip, sn, 1, 1, 0x01);

依賴庫

<!-- pom.xml -->
<dependency>
    <groupId>com.sun.mail</groupId>
    <artifactId>javax.mail</artifactId>
    <version>1.6.2</version>
</dependency>

Polling 與事件觸發

┌─────────────────────────────────────────────────────────────────┐
│  Polling Timer (定時輪詢)                                       │
│  - wkPolling1: 每 12 秒                                        │
│  - wkPolling2: 每 30 秒                                        │
│  - wkEventPolling: 每 18 秒                                    │
│  - wkEventPollingForSNMP: 每 22 秒                             │
└──────────────────────────┬──────────────────────────────────────┘
                           │
                           ▼
┌─────────────────────────────────────────────────────────────────┐
│  檢測事件                                                       │
│  1. 取得控制器事件 (Page 0x24)                                  │
│  2. 比對新事件                                                  │
│  3. 觸發通知                                                    │
└──────────────────────────┬──────────────────────────────────────┘
                           │
                           ▼
┌─────────────────────────────────────────────────────────────────┐
│  通知方式                                                       │
│  - SNMP Trap (frmMain.EventPollingForSNMP)                     │
│  - Email Alert (如果 jcPostEvent 啟用)                         │
│  - UI Event List 更新                                           │
└─────────────────────────────────────────────────────────────────┘

關鍵類

類別 檔案 功能
frmEmail frmEmail.java 郵件設定介面
dlgServer dlgServer.java Server 設定 (包含 SMTP)
PageDataDB PageDataDB.java 郵件設定儲存
SNMPSender SNMP/SNMPSender.java SNMP Trap 發送

Code Flow

1. 設定 SMTP

frmEmail (開啟)
  → 輸入 SMTP 伺服器
  → 輸入寄件人/收件人
  → (可選) 啟用 SMTP 認證
  → (可選) 啟用 Post Event
  → OK → 儲存到控制器 (OpCode 0x07/0x09/0x0A/0x0E)

2. 發送測試郵件

點擊 [Send Test Email]
  → frmEmail.jbSendMail_actionPerformed()
  → 建立 Session (JavaMail)
  → 發送到所有收件人
  → 顯示成功/失敗訊息

3. 事件自動通知

Polling 偵測到新事件
  → 檢查 jcPostEvent 是否啟用
  → 組建郵件內容 (事件描述)
  → 發送到所有收件人

郵件格式

Subject: RAIDGuard X Alert - {Controller Name}

Body:
Controller: {SN}
Event: {Event Description}
Time: {Timestamp}
Severity: {Info/Warning/Error}

安全考量

  1. 密碼儲存: SMTP 密碼儲存在控制器端,可能有安全風險
  2. 傳輸加密: 支援 SSL/TLS (jcSMTPCheck)
  3. 認證: 支援 SMTP AUTH (PLAIN/LOGIN)

Rust 實作建議

// 使用 lettre crate
use lettre::{SmtpClient, Transport, Message, SendableEmail};
use lettre::smtp::authentication::Credentials;

pub fn send_email(
    smtp_server: &str,
    username: &str,
    password: &str,
    from: &str,
    to: &str,
    subject: &str,
    body: &str,
    use_auth: bool,
    use_tls: bool,
) -> Result<(), Box<dyn std::error::Error>> {
    
    let email = Message::builder()
        .from(from.parse()?)
        .to(to.parse()?)
        .subject(subject)
        .body(body.to_string())?;
    
    let mut smtp = SmtpClient::new_localhost()?;
    
    if use_tls {
        smtp = SmtpClient::new_new_ssl(smtp_server)?;
    } else {
        smtp = SmtpClient::new_new(smtp_server)?;
    }
    
    if use_auth {
        let creds = Credentials::new(username.to_string(), password.to_string());
        smtp = smtp.credentials(creds);
    }
    
    let mut transport = smtp.transport();
    transport.send(email)?;
    
    Ok(())
}