51從零開始用Rust編寫nginx,江湖救急,TLS證書快過期了

来源:https://www.cnblogs.com/wmproxy/p/18042594/wmproxy51
-Advertisement-
Play Games

現在免費證書只能申請三個月(之前還能申請十二個月),擁有acme能力對於小的站點來說就比較需要,可以比較好的部署也不用關心TLS帶來的煩惱。 ...


wmproxy

wmproxy已用Rust實現http/https代理, socks5代理, 反向代理, 負載均衡, 靜態文件伺服器,websocket代理,四層TCP/UDP轉發,內網穿透等,會將實現過程分享出來,感興趣的可以一起造個輪子

項目地址

國內: https://gitee.com/tickbh/wmproxy

github: https://github.com/tickbh/wmproxy

設計目標

證書的自動續期,讓系統免除證書過期的煩惱,保證系統的正確運行。

關於證書的驗證

證書的組成部分:公鑰,私鑰

公鑰部分

公開的信息cert,也稱公鑰,在nginx體系中通常以.pem結尾

Cert,作為“Certificate”(證書)的縮寫,通常用於表示網路安全和加密領域中的數字證書。這些證書是用於證明身份和保障安全性的重要工具,包含了許多關鍵信息。

一般來說,證書中存放的信息主要包括:

  1. 證書頒發機構(Certificate Authority,CA)的信息:這包括CA的名稱、公鑰和證書頒發者的數字簽名等。這些信息用於驗證證書的合法性和真實性。
  2. 證書持有者的信息:這通常包括組織或個人的名稱、功能變數名稱、公鑰和證書持有者的數字簽名等。這些信息用於標識證書的所有者和驗證其身份。
  3. 證書的有效期:證書通常有一個有效期限,包括開始日期和結束日期。這用於確定證書是否仍在有效期內。

此外,證書中還可能包含其他信息,例如證書的序列號、擴展欄位等。這些信息對於特定的應用場景可能具有重要意義。

總之,Cert中存放的信息是數字證書的重要組成部分,對於保障網路安全和身份認證具有重要意義。

私鑰部分

伺服器專用的信息,稱為私鑰,在nginx體系中通常以.key結尾

私鑰的主要作用是在TLS加密通信過程中,對從伺服器發送到客戶端的數據進行加密,以確保數據的機密性和安全性。當客戶端向伺服器發送請求時,伺服器會使用其私鑰對響應數據進行加密,然後發送給客戶端。客戶端在接收到加密數據後,會使用伺服器公鑰進行解密,從而獲取到原始數據。

由於私鑰的非公開性,如果私鑰被泄露,將會對TLS加密通信的安全性造成嚴重威脅。因此,私鑰的生成、存儲和使用都需要遵循嚴格的安全標準和最佳實踐。通常,私鑰應該在安全的環境中生成,並且只由授權的人員管理和使用。

在TLS證書的生命周期中,私鑰的管理和使用也是非常重要的。一旦私鑰丟失或泄露,就需要重新生成新的密鑰對和證書,以確保加密通信的安全性。因此,對於TLS證書的私鑰部分,必須採取嚴格的安全措施,以確保其機密性和安全性。

證書無效的可能

SSL證書可能會因為多種原因而無效。以下是一些常見的情況:

  1. 證書過期:SSL證書有有效期限,一旦過期,瀏覽器會拒絕連接並顯示證書無效的警告。為了避免這種情況,管理員需要定期檢查證書的到期日期,併在必要時進行更新或續訂。
  2. 功能變數名稱不匹配:SSL證書是針對特定的功能變數名稱頒發的,如果證書中的功能變數名稱與實際訪問的功能變數名稱不匹配,瀏覽器也會顯示證書無效。這可能是因為證書是為另一個功能變數名稱頒發的,或者證書中包含的功能變數名稱拼寫錯誤。
  3. 證書鏈不完整:SSL證書通常依賴於一個證書頒發機構(CA)的證書鏈。如果證書鏈中的任何證書丟失或損壞,瀏覽器可能無法驗證證書的有效性,並顯示證書無效。
  4. 瀏覽器不受信任:如果證書頒發機構(CA)的證書被瀏覽器標記為不受信任或被撤銷,那麼使用該CA頒發的SSL證書也將被視為無效。

此篇中主要介紹證書過期如何維護的可能。

獲取過期時間

關於tls的處理庫,這裡選擇的是rustls,查詢其相關Api及源碼,發現其並未提供Cert的過期時間。這裡選擇用第三方庫x509-certificate來獲取證書的過期時間,他並不依賴於openssl,可以在不載入openssl的情況下獲取到證書的過期時間。

api相關函數:

pub fn validity_not_after(&self) -> DateTime<Utc>

// Obtain the certificate validity “not after” time.

設計要點

  1. 區分是否為acme的證書(只有acme證書才能自動獲取)
  2. 讀取證書的時候獲取過期時間
  3. 在接受證書時判斷是否過期
    1. 未過期,直接繼續執行
    2. 將過期或者已過期未載入,請求新的證書
    3. 已有新的證書,進行載入
    4. 保證不會頻繁載入
  4. 用有效的證書進行tls操作

源碼相關設計

新設計類

/// 包裝tls accepter, 用於適應acme及自有證書兩種
#[derive(Clone)]
pub struct WrapTlsAccepter {
    /// 最後請求的時間
    pub last: Instant,
    /// 最後成功載入證書的時間
    pub last_success: Instant,
    /// 功能變數名稱
    pub domain: Option<String>,
    pub accepter: Option<TlsAcceptor>,
    /// 證書的過期時間,將載入證書的時候同步讀取
    pub expired: Option<DateTime<Utc>>,
    pub is_acme: bool,
}

添加最後成功載入的時間,與全局的載入成功時間做比對。

lazy_static! {
    // 成功載入時間記錄,以方便將過期的數據做更新
    static ref SUCCESS_CERT: Mutex<HashMap<String, Instant>> = Mutex::new(HashMap::new());
}

判斷是否即將到期,到期前一天將自動更新

fn is_tls_will_expired(&self) -> bool {
    if let Some(expire) = &self.expired {
        let now = Utc::now();
        if now.timestamp() > expire.timestamp() - 86400 {
            return true;
        }
    }
    false
}

將過期時將重新觸發載入:

if self.is_acme && self.is_tls_will_expired() {
    let _ = self.check_and_request_cert();
}

#[cfg(feature = "acme-lib")]
fn check_and_request_cert(&self)  -> Result<(), Error> {
    if self.domain.is_none() {
        return Err(io::Error::new(io::ErrorKind::Other, "未知功能變數名稱").into());
    }
    {
        let mut map = CACHE_REQUEST
            .lock()
            .map_err(|_| io::Error::new(io::ErrorKind::Other, "Fail get Lock"))?;
        if let Some(last) = map.get(self.domain.as_ref().unwrap()) {
            if last.elapsed() < self.get_delay_time() {
                return Err(io::Error::new(io::ErrorKind::Other, "等待上次請求結束").into());
            }
        }
        map.insert(self.domain.clone().unwrap(), Instant::now());
    };

    let obj = self.clone();
    std::thread::spawn(move || {
        let _ = obj.request_cert();
    });
    Ok(())
}

最後在載入成功後,下一輪的處理中將嘗試的載入ssl證書

pub fn update_last(&mut self) {
    if self.accepter.is_none() {
        if self.last.elapsed() > Duration::from_secs(5) {
            self.try_load_cert();
            self.last = Instant::now();
        }
    } else {
        if self.domain.is_none() {
            return;
        }
        let map = SUCCESS_CERT.lock().unwrap();
        let doamin = &self.domain.clone().unwrap();
        if !map.contains_key(doamin) {
            return;
        }
        if self.last_success < map[doamin] && self.last < map[doamin] {
            self.try_load_cert();
            self.last = map[doamin];
        }
    }
}

如此一個擁有自動請求且自動更新的acme請求已完成。
如果有細心的已發現相關代碼用了feature,基本上等於Cpp中的#ifdef xxx也是用來控制代碼是否啟用相關的。

關於條件編譯 Features

Cargo Feature 是非常強大的機制,可以為大家提供條件編譯和可選依賴的高級特性。

相關鏈接可以參考features

將其中的依賴改成了

acme-lib = { version = "^0.9.1", default-features = true, optional = true}
openssl = { version = "0.10.32", default-features = false, features = ["vendored"], optional = true }

因為acme-lib依賴於openssl庫,在編譯方面可能會相對比較麻煩,需要額外的依賴,此處openssl配置是覆蓋acme-lib中的預設features,達到可以不依賴外部openssl庫的能力,使用源碼編譯,所以如果要啟用acme-lib能力可以使用

cargo build --features "acme-lib"
如果openssl不好依賴可以使用來編譯系統
cargo build --features "acme-lib openssl"

總結

現在免費證書只能申請三個月(之前還能申請十二個月),擁有acme能力對於小的站點來說就比較需要,可以比較好的部署也不用關心TLS帶來的煩惱。

點擊 [關註][在看][點贊] 是對作者最大的支持


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 至於未來會怎樣,要走下去才知道反正路還很長,天總會亮。 1. 面向對象 1.1 什麼是面向對象(OOP) 面向對象 Object Oriented Programming。在軟體開發中,我們雖然用的是面向對象的語言,但我相信絕大多數入門或者工作經驗不長的同學敲出來的代碼依然是大段的面向過程的思想,我 ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 一、如何部署 前後端分離開發模式下,前後端是獨立佈署的,前端只需要將最後的構建物上傳至目標伺服器的web容器指定的靜態目錄下即可 我們知道vue項目在構建後,是生成一系列的靜態文件 常規佈署我們只需要將這個目錄上傳至目標伺服器即可 // ...
  • 寫在前面 學習、寫作、工作、生活,都跟心情有很大關係,甚至有時候我更喜歡一個人獨處,戴上耳機coding的感覺。 明顯現在的心情,比中午和上午好多了,心情超棒的,靠自己解決了兩個問題: 新增的時候點擊TreeSelect控制項控制台會給出報錯 分類新增和編輯時,報錯父類和電子書iD不能為空的問題 富文 ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 一、錯誤類型 任何一個框架,對於錯誤的處理都是一種必備的能力 在Vue 中,則是定義了一套對應的錯誤處理規則給到使用者,且在源代碼級別,對部分必要的過程做了一定的錯誤處理。 主要的錯誤來源包括: 後端介面錯誤 代碼中本身邏輯錯誤 二、如何 ...
  • accent-color 是從 Chrome 93 開始被得到支持的一個不算太新屬性。之前一直沒有好好介紹一下這個屬性。直到最近在給一些系統整體切換主題色的時候,更深入的瞭解了一下這個屬性。 簡單而言,CSS accent-color 支持使用幾行簡單的 CSS 為表單元素著色,是的,只需幾行代碼就 ...
  • 寫在前面 我知道自己現在的狀態很不好,以為放個假能好好放鬆下心情,結果昨晚做夢還在工作,調試代碼,和領導彙報工作。 天吶,明明是在放假,可大腦還在考慮工作的事,我的天那,這是怎麼了? Vue頁面參數傳遞 1、任務拆解 頁面跳轉時帶上當前電子書id參數ebookId 新增/編輯文檔時,讀取電子書id參 ...
  • 1. 有人說 Python 性能沒那麼 Low? 這個我用 pypy 2.7 確認了下,確實沒那麼差, 如果用 NumPy 或其他版本 Python 的話,性能更快。但 pypy 還不完善,pypy3 在 beta, 所以一般情況,我是說一般情況下,這點比較讓人不爽。 2. 有人說怎麼沒有 C#、R ...
  • 歡迎來到從零開始學Spring Boot的旅程!在Spring Boot中,返回JSON數據是很常見的需求,特別是當我們構建RESTful API時。我們對上一篇的Hello World進行簡單的修改。 添加依賴 首先,確保你的build.gradle文件中已經包含了Spring Web的依賴,因為 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...