小言_互联网的博客

宝塔面板配置Let's Encrypt证书自动续签失效及解决方案

638人阅读  评论(0)

一、背景小故事

笔者手里有个朋友交给我去日常运维项目是PHP+微信小程序,部署在Linux系统上。

这个项目是用宝塔面板去进行日常的可视化运维管理,用起来蛮香的。

如不清楚宝塔的同学,可以自行了解,这里就不详细说明。

宝塔是一款简单好用的服务器运维面板,并且永久免费。

如果有兴趣领宝塔优惠券点击阅读原文领取哈

我们都知道, 小程序请求的后端接口,要求是https协议的。所以后端服务器得配置上SSL证书。

我接手之前,这个项目的SSL证书是直接购买的,而且也要到期了。当时,我对宝塔面板也是第一次接触,不是特熟悉。经过一番摸索,看到宝塔面板提供Let's Encrypt这样免费的证书。这个证书有个缺点就是只有3个月的免费期限,到期后需要再去续期。

SSL证书配置

经过一番折腾,就给网站安排上了这个免费的证书。而且宝塔面板在这里也有明确提示:「将在距离到期时间一个月内尝试自动续签」

看到那个提示后,发现这个证书可以一直免费使用,那倒是省钱又省心了。

隔了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接口打开
第二步拿到密钥和配置IP白名单

而且宝塔也提供了多个版本的API接口 Demo样例,可以很方便的集成。样例地址:https://www.bt.cn/bbs/thread-20376-1-1.html

API接口文档

我比较熟悉Java,也就下载的JavaDemo研究的。


   
  1. package com.raysonfang.bt.test;
  2. import java.io.BufferedReader;
  3. import java.io.IOException;
  4. import java.io.InputStreamReader;
  5. import java.io.PrintWriter;
  6. import java.math.BigInteger;
  7. import java.net.URL;
  8. import java.net.URLConnection;
  9. import java.security.MessageDigest;
  10. /**
  11.  * 宝塔API测试
  12.  *
  13.  * @author fanglei
  14.  * @date 2021/02/21 10:44
  15.  **/
  16. public class BTTest {
  17.     public static void main(String[] args)
  18.     {
  19.         try {
  20.             String btSign =  "宝塔API密钥";
  21.             String url =  "http://IP:8888/ssl?action=Renew_SSL";
  22.             String timestamp = System.currentTimeMillis() +  "";
  23.             String md5Sign = getMd5(btSign);
  24.             String temp = timestamp+md5Sign;
  25.             String token = getMd5(temp);
  26.             String json =  "request_time="+timestamp+ "&request_token="+token;
  27.             String responseText = sendPost(url,json);
  28.             System.out. println(responseText);
  29.         } catch (Exception e) {
  30.             e.printStackTrace();
  31.         }
  32.     }
  33.     
  34.     public static String getMd5(String str) throws Exception
  35.     {
  36.         try {
  37.              // 生成一个MD5加密计算摘要
  38.             MessageDigest md = MessageDigest.getInstance( "MD5");
  39.              // 计算md5函数
  40.             md.update(str.getBytes());
  41.              // digest()最后确定返回md5 hash值,返回值为8为字符串。因为md5 hash值是16位的hex值,实际上就是8位的字符
  42.              // BigInteger函数则将8位的字符串转换成16位hex值,用字符串来表示;得到字符串形式的hash值
  43.              return  new BigInteger( 1, md.digest()).toString( 16);
  44.         } catch (Exception e) {
  45.             throw  new Exception( "MD5加密出现错误,"+e.toString());
  46.         }
  47.     }
  48.     
  49.     public static String sendPost(String url, String param) {
  50.         PrintWriter out = null;
  51.         BufferedReader in = null;
  52.         StringBuffer result =  new StringBuffer();
  53.         try {
  54.             URL realUrl =  new URL(url);
  55.              // 打开和URL之间的连接
  56.             URLConnection conn = realUrl.openConnection();
  57.              // 设置通用的请求属性
  58.             conn.setRequestProperty( "accept""text/xml,text/javascript,text/html,application/json");
  59.             conn.setRequestProperty( "connection""Keep-Alive");
  60.              // 发送POST请求必须设置如下两行
  61.             conn.setDoOutput( true);
  62.             conn.setDoInput( true);
  63.              // 获取URLConnection对象对应的输出流
  64.             out =  new PrintWriter(conn.getOutputStream());
  65.              // 发送请求参数
  66.             out. print(param);
  67.              // flush输出流的缓冲
  68.             out.flush();
  69.              // 定义BufferedReader输入流来读取URL的响应
  70.             in =  new BufferedReader( new InputStreamReader(conn.getInputStream()));
  71.             String line;
  72.             while ((line = in.readLine()) != null) {
  73.                 result. append(line);
  74.             }
  75.         } catch (Exception e) {
  76.             System.out. println( "发送 POST 请求出现异常!"+e);
  77.             e.printStackTrace();
  78.         }
  79.          //使用finally块来关闭输出流、输入流
  80.         finally{
  81.             try{
  82.                  if(out!=null){
  83.                     out. close();
  84.                 }
  85.                  if(in!=null){
  86.                     in. close();
  87.                 }
  88.             }
  89.             catch(IOException ex){
  90.                 ex.printStackTrace();
  91.             }
  92.         }
  93.          return result.toString();
  94.     }
  95. }

下载demo 研究请求参数构成,并调试成功。

3.3 设置定时任务,模拟请求

宝塔 直接提供有计划任务功能,我们先看看能不能实现我们想要的功能,如果不能,我们再想其他办法解决。

计划任务

我看了任务类型有:Shell脚本, 备份网站,备份数据库,日志切割,释放内存,访问URL。

其中 Shell脚本访问URL这两种任务类型跟我们想要的很接近,其他的都不怎么适合。

访问URL这种类型也可以排除,是因为这里采用直接配置URL,适合无动态参数,无权限验证的URL。

那剩下的就只有Shell脚本来实现。

我们知道Shell脚本也是一种编程语言脚本,那我们就用它来模拟请求了。

经过几个小时的研究,把shell脚本写出来。还是很费劲,对于Shell脚本里的参数传递语法不怎么熟悉,也反复去尝试,才摸索清楚。


   
  1. #!/bin/bash
  2. # 获取时间戳
  3. cur_timestamp=$(( `date '+%s'`* 1000+ `date '+%N'`/ 1000000))
  4. # 宝塔密钥
  5. api_sk= 'uSth3rmADQ9Np5Zyhxxxxxxxxxxxxxxx'
  6. # 密钥MD5加密
  7. key= `echo -n $api_sk|md5sum|cut -d" " -f1`
  8. # 生成token
  9. request_token= `echo -n $cur_timestamp$key|md5sum|cut -d" " -f1`
  10. # 构造请求参数,并通过curl发送请求
  11. curl -i -X POST -d  "request_token=$request_token&request_time=$cur_timestamp" http: //ip:8888/ssl?action=Renew_SSL

把Shell脚本的密钥和IP进行替换,就可以直接去任务计划添加上,然后手动执行一下看看 是否可以运行成功

注意:第一次添加任务后,需要手动点执行,并在日志去查看是否执行成功

任务执行成功

至此,以后可以放心交给程序自动续签。

四、总结

也许官方已经解决了自动续签的问题,而我这个也许是个偏方,但这里面包含抓包,定时任务,接口鉴权,Shell脚本等知识运用

文章好看就点这里


转载:https://blog.csdn.net/qq_33399709/article/details/113931390
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场