一、背景小故事
筆者手里有個朋友交給我去日常運維項目是PHP+微信小程序,部署在Linux系統上。
這個項目是用寶塔面板去進行日常的可視化運維管理,用起來蠻香的。
如不清楚寶塔的同學,可以自行了解,這里就不詳細說明。
寶塔是一款簡單好用的服務器運維面板,並且永久免費。
我們都知道, 小程序請求的后端接口,要求是https協議的。所以后端服務器得配置上SSL證書。
我接手之前,這個項目的SSL證書是直接購買的,而且也要到期了。當時,我對寶塔面板也是第一次接觸,不是特熟悉。經過一番摸索,看到寶塔面板提供Let's Encrypt這樣免費的證書。這個證書有個缺點就是只有3個月的免費期限,到期后需要再去續期。
經過一番折騰,就給網站安排上了這個免費的證書。而且寶塔面板在這里也有明確提示:將在距離到期時間一個月內嘗試自動續簽。
看到那個提示后,發現這個證書可以一直免費使用,那倒是省錢又省心了。
隔了3個月后,老板發來一條消息說:網站不正常了,給我看看唄。
經過我一頓熟悉操作分析,排查服務器,排查應用,並利用fiddler抓包工具去進行抓包分析后,確定是Https協議到期導致的問題。
然后我在SSL配置界面上,手動去點續簽,等了一會兒,續簽成功。網站又可以正常訪問,告訴老板完美解決。
又經過一段時間后,老板又發來一條消息說,網站又不正常,再給我看看什么問題。
我一看到消息,知道又翻車了。不過這次我是輕車熟路,直接去手動點了一下續簽,解決。
......
就這樣,重復了很多次。
這樣長久下去,也不是辦法。
二、萌動想法
我得想出一個法子來解決這個問題。畢竟我們都是一枚程序員,專門去解決生活中出現的重復勞動力。
既然點一下續簽,就可以解決證書到期問題。那我們能不能在程序里用定時任務+模擬請求來自動續簽?
我就開始構想一下實現思路:
第一步:我們需要拿到續簽按鈕觸發的后端服務接口及請求參數,后續能模擬請求。
第二步:驗證接口是否可以直接請求成功,是否需要權限驗證?經過驗證,需要先登陸,才能請求成功。
第三步:還需要一個定時任務功能。經過確認 寶塔面板自帶有任務計划功能。
帶着這樣的想法,開始去嘗試實現,過程中有遇到很多問題,就不詳細說明,主要都是在寫Shell腳本構造請求參數傳遞。
我就直接上解決方案供同學們參考。
三、方案實踐
3.1 找到續簽請求接口
接口地址:http://IP:8888/ssl?action=Renew_SSL
如果我們直接請求該接口,會發現需要登陸,不能直接請求成功。
3.2 設置寶塔 API接口
經過查閱資料,寶塔面板提供了API接口,用密鑰key生成token后,再發起請求就可以,而不需要用戶名和密碼。我們能方便直接使用寶塔里面的任何API接口。
面板設置 =》打開 API接口,拿到密鑰key,並配置IP白名單。IP可以直接添加服務器IP。
而且寶塔也提供了多個版本的API接口 Demo樣例,可以很方便的集成。
樣例地址:https://www.bt.cn/bbs/thread-20376-1-1.html
我比較熟悉Java,也就下載的JavaDemo研究的。
package com.raysonfang.bt.test;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.math.BigInteger;
import java.net.URL;
import java.net.URLConnection;
import java.security.MessageDigest;
/**
* 寶塔API測試
*
* @author fanglei
* @date 2021/02/21 10:44
**/
public class BTTest {
public static void main(String[] args)
{
try {
String btSign = "寶塔API密鑰";
String url = "http://IP:8888/ssl?action=Renew_SSL";
String timestamp = System.currentTimeMillis() + "";
String md5Sign = getMd5(btSign);
String temp = timestamp+md5Sign;
String token = getMd5(temp);
String json = "request_time="+timestamp+"&request_token="+token;
String responseText = sendPost(url,json);
System.out.println(responseText);
} catch (Exception e) {
e.printStackTrace();
}
}
public static String getMd5(String str) throws Exception
{
try {
// 生成一個MD5加密計算摘要
MessageDigest md = MessageDigest.getInstance("MD5");
// 計算md5函數
md.update(str.getBytes());
// digest()最后確定返回md5 hash值,返回值為8為字符串。因為md5 hash值是16位的hex值,實際上就是8位的字符
// BigInteger函數則將8位的字符串轉換成16位hex值,用字符串來表示;得到字符串形式的hash值
return new BigInteger(1, md.digest()).toString(16);
} catch (Exception e) {
throw new Exception("MD5加密出現錯誤,"+e.toString());
}
}
public static String sendPost(String url, String param) {
PrintWriter out = null;
BufferedReader in = null;
StringBuffer result = new StringBuffer();
try {
URL realUrl = new URL(url);
// 打開和URL之間的連接
URLConnection conn = realUrl.openConnection();
// 設置通用的請求屬性
conn.setRequestProperty("accept", "text/xml,text/javascript,text/html,application/json");
conn.setRequestProperty("connection", "Keep-Alive");
// 發送POST請求必須設置如下兩行
conn.setDoOutput(true);
conn.setDoInput(true);
// 獲取URLConnection對象對應的輸出流
out = new PrintWriter(conn.getOutputStream());
// 發送請求參數
out.print(param);
// flush輸出流的緩沖
out.flush();
// 定義BufferedReader輸入流來讀取URL的響應
in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
result.append(line);
}
} catch (Exception e) {
System.out.println("發送 POST 請求出現異常!"+e);
e.printStackTrace();
}
//使用finally塊來關閉輸出流、輸入流
finally{
try{
if(out!=null){
out.close();
}
if(in!=null){
in.close();
}
}
catch(IOException ex){
ex.printStackTrace();
}
}
return result.toString();
}
}
下載demo 研究請求參數構成,並調試成功。
3.3 設置定時任務,模擬請求
寶塔 直接提供有計划任務功能,我們先看看能不能實現我們想要的功能,如果不能,我們再想其他辦法解決。
我看了任務類型有:Shell腳本, 備份網站,備份數據庫,日志切割,釋放內存,訪問URL。
其中 Shell腳本
和訪問URL
這兩種任務類型跟我們想要的很接近,其他的都不怎么適合。
訪問URL
這種類型也可以排除,是因為這里采用直接配置URL,適合無動態參數,無權限驗證的URL。
那剩下的就只有Shell腳本
來實現。
我們知道Shell腳本
也是一種編程語言腳本,那我們就用它來模擬請求了。
經過幾個小時的研究,把shell腳本寫出來。還是很費勁,對於Shell腳本里的參數傳遞語法不怎么熟悉,也反復去嘗試,才摸索清楚。
#!/bin/bash
# 獲取時間戳
cur_timestamp=$((`date '+%s'`*1000+`date '+%N'`/1000000))
# 寶塔密鑰
api_sk='uSth3rmADQ9Np5Zyhxxxxxxxxxxxxxxx'
# 密鑰MD5加密
key=`echo -n $api_sk|md5sum|cut -d" " -f1`
# 生成token
request_token=`echo -n $cur_timestamp$key|md5sum|cut -d" " -f1`
# 構造請求參數,並通過curl發送請求
curl -i -X POST -d "request_token=$request_token&request_time=$cur_timestamp" http://ip:8888/ssl?action=Renew_SSL
把Shell腳本的密鑰和IP進行替換,就可以直接去任務計划添加上,然后手動執行一下看看 是否可以運行成功
注意:第一次添加任務后,需要手動點執行,並在日志去查看是否執行成功
至此,以后可以放心交給程序自動續簽。
四、總結
也許官方已經解決了自動續簽的問題,而我這個也許是個偏方,但這里面包含抓包,定時任務,接口鑒權,Shell腳本等知識運用
。