從零開始使用Rust編寫nginx(TLS證書快過期了)
wmproxy
wmproxy
已用Rust
實(shí)現(xiàn)http/https
代理, socks5
代理, 反向代理, 負(fù)載均衡, 靜態(tài)文件服務(wù)器,websocket
代理,四層TCP/UDP轉(zhuǎn)發(fā),內(nèi)網(wǎng)穿透等,會將實(shí)現(xiàn)過程分享出來,感興趣的可以一起造個輪子
項目地址
國內(nèi): https://gitee.com/tickbh/wmproxy
github: https://github.com/tickbh/wmproxy
設(shè)計目標(biāo)
證書的自動續(xù)期,讓系統(tǒng)免除證書過期的煩惱,保證系統(tǒng)的正確運(yùn)行。
關(guān)于證書的驗(yàn)證
證書的組成部分:公鑰,私鑰
公鑰部分
公開的信息cert,也稱公鑰,在nginx體系中通常以
.pem
結(jié)尾
Cert,作為“Certificate”(證書)的縮寫,通常用于表示網(wǎng)絡(luò)安全和加密領(lǐng)域中的數(shù)字證書。這些證書是用于證明身份和保障安全性的重要工具,包含了許多關(guān)鍵信息。
一般來說,證書中存放的信息主要包括:
- 證書頒發(fā)機(jī)構(gòu)(Certificate Authority,CA)的信息:這包括CA的名稱、公鑰和證書頒發(fā)者的數(shù)字簽名等。這些信息用于驗(yàn)證證書的合法性和真實(shí)性。
- 證書持有者的信息:這通常包括組織或個人的名稱、域名、公鑰和證書持有者的數(shù)字簽名等。這些信息用于標(biāo)識證書的所有者和驗(yàn)證其身份。
- 證書的有效期:證書通常有一個有效期限,包括開始日期和結(jié)束日期。這用于確定證書是否仍在有效期內(nèi)。
此外,證書中還可能包含其他信息,例如證書的序列號、擴(kuò)展字段等。這些信息對于特定的應(yīng)用場景可能具有重要意義。
總之,Cert中存放的信息是數(shù)字證書的重要組成部分,對于保障網(wǎng)絡(luò)安全和身份認(rèn)證具有重要意義。
私鑰部分
服務(wù)器專用的信息,稱為私鑰,在nginx體系中通常以
.key
結(jié)尾
私鑰的主要作用是在TLS加密通信過程中,對從服務(wù)器發(fā)送到客戶端的數(shù)據(jù)進(jìn)行加密,以確保數(shù)據(jù)的機(jī)密性和安全性。當(dāng)客戶端向服務(wù)器發(fā)送請求時,服務(wù)器會使用其私鑰對響應(yīng)數(shù)據(jù)進(jìn)行加密,然后發(fā)送給客戶端??蛻舳嗽诮邮盏郊用軘?shù)據(jù)后,會使用服務(wù)器公鑰進(jìn)行解密,從而獲取到原始數(shù)據(jù)。
由于私鑰的非公開性,如果私鑰被泄露,將會對TLS加密通信的安全性造成嚴(yán)重威脅。因此,私鑰的生成、存儲和使用都需要遵循嚴(yán)格的安全標(biāo)準(zhǔn)和最佳實(shí)踐。通常,私鑰應(yīng)該在安全的環(huán)境中生成,并且只由授權(quán)的人員管理和使用。
在TLS證書的生命周期中,私鑰的管理和使用也是非常重要的。一旦私鑰丟失或泄露,就需要重新生成新的密鑰對和證書,以確保加密通信的安全性。因此,對于TLS證書的私鑰部分,必須采取嚴(yán)格的安全措施,以確保其機(jī)密性和安全性。
證書無效的可能
SSL證書可能會因?yàn)槎喾N原因而無效。以下是一些常見的情況:
- 證書過期:SSL證書有有效期限,一旦過期,瀏覽器會拒絕連接并顯示證書無效的警告。為了避免這種情況,管理員需要定期檢查證書的到期日期,并在必要時進(jìn)行更新或續(xù)訂。
- 域名不匹配:SSL證書是針對特定的域名頒發(fā)的,如果證書中的域名與實(shí)際訪問的域名不匹配,瀏覽器也會顯示證書無效。這可能是因?yàn)樽C書是為另一個域名頒發(fā)的,或者證書中包含的域名拼寫錯誤。
- 證書鏈不完整:SSL證書通常依賴于一個證書頒發(fā)機(jī)構(gòu)(CA)的證書鏈。如果證書鏈中的任何證書丟失或損壞,瀏覽器可能無法驗(yàn)證證書的有效性,并顯示證書無效。
- 瀏覽器不受信任:如果證書頒發(fā)機(jī)構(gòu)(CA)的證書被瀏覽器標(biāo)記為不受信任或被撤銷,那么使用該CA頒發(fā)的SSL證書也將被視為無效。
此篇中主要介紹證書過期如何維護(hù)的可能。
獲取過期時間
關(guān)于tls的處理庫,這里選擇的是rustls,查詢其相關(guān)Api及源碼,發(fā)現(xiàn)其并未提供Cert的過期時間。這里選擇用第三方庫x509-certificate來獲取證書的過期時間,他并不依賴于openssl,可以在不加載openssl的情況下獲取到證書的過期時間。
api相關(guān)函數(shù):
pub fn validity_not_after(&self) -> DateTime<Utc> // Obtain the certificate validity “not after” time.
設(shè)計要點(diǎn)
- 區(qū)分是否為acme的證書(只有acme證書才能自動獲取)
- 讀取證書的時候獲取過期時間
- 在接受證書時判斷是否過期
未過期,直接繼續(xù)執(zhí)行
將過期或者已過期未加載,請求新的證書
已有新的證書,進(jìn)行加載
保證不會頻繁加載
- 用有效的證書進(jìn)行tls操作
源碼相關(guān)設(shè)計
新設(shè)計類
/// 包裝tls accepter, 用于適應(yīng)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! { // 成功加載時間記錄,以方便將過期的數(shù)據(jù)做更新 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 }
將過期時將重新觸發(fā)加載:
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, "等待上次請求結(jié)束").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請求已完成。
如果有細(xì)心的已發(fā)現(xiàn)相關(guān)代碼用了feature,基本上等于Cpp中的#ifdef xxx
也是用來控制代碼是否啟用相關(guān)的。
關(guān)于條件編譯 Features
Cargo Feature 是非常強(qiáng)大的機(jī)制,可以為大家提供條件編譯和可選依賴的高級特性。
相關(guān)鏈接可以參考features
將其中的依賴改成了
acme-lib = { version = "^0.9.1", default-features = true, optional = true} openssl = { version = "0.10.32", default-features = false, features = ["vendored"], optional = true }
因?yàn)?code>acme-lib依賴于openssl庫,在編譯方面可能會相對比較麻煩,需要額外的依賴,此處openssl配置是覆蓋acme-lib中的默認(rèn)features,達(dá)到可以不依賴外部openssl庫的能力,使用源碼編譯,所以如果要啟用acme-lib能力可以使用
cargo build --features "acme-lib" 如果openssl不好依賴可以使用來編譯系統(tǒng) cargo build --features "acme-lib openssl"
總結(jié)
現(xiàn)在免費(fèi)證書只能申請三個月(之前還能申請十二個月),擁有acme能力對于小的站點(diǎn)來說就比較需要,可以比較好的部署也不用關(guān)心TLS帶來的煩惱。
到此這篇關(guān)于從零開始使用Rust編寫nginx(TLS證書快過期了)的文章就介紹到這了,更多相關(guān)Rust編寫nginx內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Rust 中的 Packages 與 Crates模塊化構(gòu)建的基礎(chǔ)及開發(fā)流程
Rust中的Crate是編譯器處理的最小代碼單元,可以是二進(jìn)制或庫,每個Crate由一個CrateRoot文件(通常是src/main.rs或src/lib.rs)定義,本文給大家介紹Rust 中的 Packages 與 Crates模塊化構(gòu)建的基礎(chǔ)及開發(fā)流程,感興趣的朋友一起看看吧2025-02-02Rust聲明宏在不同K線bar類型中的應(yīng)用小結(jié)
在K線bar中,往往有很多不同分時k線圖,比如1,2,3,5,,,,,60,120,250,300…,,不同分鐘類型,如果不用宏,那么手寫會比較麻煩,下面就試用一下宏來實(shí)現(xiàn)不同類型的bar,感興趣的朋友一起看看吧2024-05-05vscode搭建rust開發(fā)環(huán)境的圖文教程
本文主要介紹了vscode搭建rust開發(fā)環(huán)境的圖文教程,文中通過圖文介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-08-08