闲碎记事本 闲碎记事本
首页
  • JAVA
  • Cloudflare
  • 学完再改一遍UI
友链
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

YAN

我要偷偷记录...
首页
  • JAVA
  • Cloudflare
  • 学完再改一遍UI
友链
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • java

  • linux

  • docker

  • redis

  • nginx

  • mysql

  • 其他

    • 跨域处理
    • Homebrew简单语法
    • 音视频处理库
    • ES设置设置密码
    • 关于与或运算的一些理解
    • AI绘画学习
    • Content-Type对应处理
    • gitlab迁移
    • opssl命令
    • certd证书续期
      • git使用
      • Google搜索
      • Emoji速查
    • 环境搭建

    • 知识库
    • 其他
    YAN
    2025-03-13
    目录

    certd证书续期

    你是否还在为3个月的免费证书而烦恼?现在Certd可以解决你的问题。

    [Certd](#https://github.com/certd/certd) 是一个免费全自动申请和自动部署更新SSL证书的管理系统。 后缀d取自linux守护进程的命名风格,意为证书守护进程。
    项目不仅支持证书申请过程自动化,还可以自动化部署更新证书,让你的证书永不过期。

    • 全自动申请证书(支持所有注册商注册的域名)
    • 全自动部署更新证书(目前支持部署到主机、阿里云、腾讯云等,目前已支持60+部署插件)
    • 支持DNS-01、HTTP-01、CNAME代理等多种域名验证方式
    • 支持通配符域名/泛域名,支持多个域名打到一个证书上,支持pem、pfx、der、jks等多种证书格式
    • 邮件通知、webhook通知
    • 私有化部署,数据保存本地,授权信息加密存储,镜像由Github Actions构建,过程公开透明

    # docker部署

    此次和官方示例略有差异,修改了挂载路径

    version: '3.3'
    services:
      certd:
        #指定版本
        image: registry.cn-shenzhen.aliyuncs.com/handsfree/certd:1.31.2
        # 最新版本
        #image: registry.cn-shenzhen.aliyuncs.com/handsfree/certd:latest
        container_name: certd
        restart: unless-stopped
        volumes:
          - ./data:/app/data
        ports:
          - "7001:7001"
          - "7002:7002"
        environment:
          - certd_system_resetAdminPasswd=false
    

    提示

    • http 访问http://你的IP:7001
    • https访问 https://你的IP:7002

    默认账号密码:`admin/123456

    注意

    首次登录需要强制修改密码

    # 创建流水线

    登录后查看使用教程 非常详细

    提示

    可将多个域名打在一个证书中:

    img.png

    提示

    证书申请成功后执行主机命令,如nginx重启等操作

    img.png

    提示

    一切结束后执行操作。发送通知什么的

    img.png

    # 数据备份

    数据默认存在/data/certd目录下,可配置自定义流水线自动备份。

    # 备份恢复

    将备份的db.sqlite及同目录下的其他文件一起覆盖到原来的位置,重启certd即可

    # 补一个钉钉推送JS

    查看代码

    代码目录

    const crypto = await import('crypto');
    
    //钉钉授权
    const auth = {
        "accessToken": "xxx",
        "secret": "xxx"
    };
    
    //根路径
    const URL = "https://oapi.dingtalk.com/robot/send";
    
    
    // 使用标准 URL 编码
    function urlEncode(data) {
      return encodeURIComponent(data);
    }
    
    
    
    // 生成签名
    function sign(timestamp) {
        const secret = auth.secret;
        // 构造待签名字符串
        const stringToSign = `${timestamp}\n${secret}`;
    
        // 创建 HmacSHA256 哈希
        const hmacHash = crypto.createHmac('sha256', secret)
            .update(stringToSign)
            .digest(); // 得到二进制数据
    
        // 进行 Base64 编码
        const base64Encoded = Buffer.from(hmacHash).toString('base64');
    
        // URL Encode 得到最终的签名
        return urlEncode(base64Encoded);
    }
    
    
    
    // 构造请求 URL
    function getUrl(timestamp) {
        return `${URL}?access_token=${auth.accessToken}&timestamp=${timestamp}&sign=${sign(timestamp)}`;
    }
    
    // 获取当前时间戳(毫秒)
    const timestamp = Math.round(Date.now());
    
    
    // 生成请求 URL
    const url = getUrl(timestamp);
    
    ctx.logger.info("开始向钉钉机器人推送通知")
    
    // 构造消息内容
    const msg = `
    ### 证书更新通知
    **你有一张证书即将过期**,<font color="#FF0000">[去处理](http://localhose:7001)</font></br></br>
    `;
    
    // 准备要发送的数据
    const data = {
        msgtype: "markdown",
        markdown: {
            title: "证书通知",
            text: msg
        }
    };
    
    //axios发起http请求上传证书
    const res = await ctx.http.request({
      url:url,
      data:JSON.stringify(data),
      method:"post",
      headers: {
    	"Content-Type": "application/json"
      }
    })
    
    if(!res || res.errcode !== 0){
    	ctx.logger.info("调用失败",res)
        //抛异常才能让任务失败
        throw new Error(res)
    }
    
    //不能用console.log,需要用ctx.logger 才能把日志打印在ui上
    ctx.logger.info("推送钉钉机器人成功",res.data)
    
    

    # 自动备份脚本

    查看代码
    dingding-bot/
    ├── dingdingbot.py
    ├── main.py
    ├── Dockerfile
    ├── docker-compose.yml
    └── requirements.txt
    

    docker-compose.yaml

    version: '3.8'
    
    services:
      task-bot:
        image: python:3.11-slim  # 使用官方 Python 镜像
        container_name: task-bot
        working_dir: /app        # 设置工作目录
        volumes:
          - .:/app               # 将当前目录挂载到容器中的 /app 目录
          - /root/certd/data:/app/certd # 挂载certd存储路径
        command: sh -c "pip install --no-cache-dir -r requirements.txt && python main.py"
        environment:
          - PYTHONUNBUFFERED=1   # 确保 Python 输出是无缓冲的,方便调试
    

    main.py

    import paramiko
    import os
    import shutil
    from datetime import datetime
    from schedule import every, repeat, run_pending
    import time
    from dingdingbot import send_message
    
    
    files = [
        {
            "prefix":"ssl",
            "path":'/app/certd/db.sqlite'
        }
    ]
    
    
    def open_ssh_client():
        # 创建SSH客户端实例
        ssh_client = paramiko.SSHClient()
        # 自动添加远程主机的主机密钥到本地known_hosts文件中
        ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    
        remote_host = '123456'
        pwd =  'xxx'
    
        # 连接到远程服务器
        ssh_client.connect(
            hostname=remote_host,  # 远程服务器的IP地址或域名
            port=22,          # SSH端口号,默认为22
            username='root',  # 远程服务器的用户名
            password=pwd # 远程服务器的密码
        )
        print(f"初始化服务器:{remote_host} SSH 成功")
        return ssh_client
    
    
    def get_back_filename(prefix,index):
            # 生成备份文件名(包含时间戳)
        timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
        return  f"backup_{prefix}_{timestamp}_{index}"
    
    
    def del_back_file(local_backup_path):
        # 删除本地压缩文件(可选)
        os.remove(local_backup_path)
        print(f"本地压缩文件 {local_backup_path} 已删除")
    
    def check_local_file(local_file):
        if not os.path.exists(local_file):
            raise FileNotFoundError(f"本地文件 {local_file} 不存在")
    
    def check_remote_path(ssh_client,remote_file_path):
        # 确保远程备份目录存在
        stdin, stdout, stderr = ssh_client.exec_command(f'mkdir -p {remote_file_path}')
        # 检查命令执行是否成功
        if stdout.channel.recv_exit_status() != 0:
            error_message = stderr.read().decode()
            raise Exception(f"创建远程目录失败: {error_message}")
    
    
    def main():
    
        ssh_client = open_ssh_client()
    
        # 创建SFTP客户端
        sftp_client = ssh_client.open_sftp()
        n =0
        for item in files:
            prefix = item.get("prefix")
            zip_name = get_back_filename(prefix,n)
            # 次数加1
            n+=1
            # 压缩本地文件
            local_file = item.get("path")
    
            local_zip = f"{os.path.dirname(local_file)}/{zip_name}"
            print(f"开始压缩:{local_file} 为=>{local_zip} 压缩路径=>{os.path.dirname(local_file)}")
            shutil.make_archive(local_zip, 'zip', os.path.dirname(local_file),os.path.basename(local_file))
    
            # 后续使用补上zip后缀
            local_zip+=".zip"
            # 检查本地文件
            check_local_file(local_zip)
            # 远程服务器上的目标路径
            remote_file_path = f'/root/back/{prefix}'
            # 检查远程路径
            check_remote_path(ssh_client,remote_file_path)
    
            remote_file=f"{remote_file_path}/{zip_name}.zip"
            # ftp上传
            sftp_client.put(local_zip, remote_file)
            print(f"文件 {local_zip} 已成功上传到远程服务器  {remote_file} 路径下")
    
            #删除压缩包
            del_back_file(local_zip)
    
        # 关闭SFTP客户端和SSH连接
        sftp_client.close()
        ssh_client.close()
        print("执行结束,下发通知到钉钉机器人")
    
        # 此次不能缩进
        msg = f"""### 证书备份通知
    **备份时间**:{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
    
    **证书更新系统备份成功**
        """
        send_message(msg)
    
    
    @repeat(every().day.at("00:00"))
    # @repeat(every().minutes)
    def job_one():
        print(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}]开始定时执行备份任务...")
        main()
    
    
    
    
    if __name__ == "__main__":
        #立即执行一次
        main()
        #开始定时执行
        print("开始定时执行 每天 00:00 运行备份")
        while True:
            run_pending()
            time.sleep(1)
    

    dingdingbot.py

    import hmac
    import hashlib
    import base64
    import urllib.parse
    import time
    import requests
    import json
    from datetime import datetime
    
    # 假设 TALK 是包含 accessToken 和 secret 的对象
    TALK = {
        "accessToken": "xx",
        "secret": "xx"
    }
    
    URL = "https://oapi.dingtalk.com/robot/send"
    
    def url_encode(data):
        # 使用 urllib.parse.quote 进行 URL 编码
        return urllib.parse.quote(data)
    
    def sign(timestamp):
        secret = TALK["secret"]
        # 构造待签名字符串
        string_to_sign = f"{timestamp}\n{secret}"
    
        # 创建 HmacSHA256 哈希
        hmac_hash = hmac.new(secret.encode('utf-8'), string_to_sign.encode('utf-8'), hashlib.sha256)
        digest = hmac_hash.digest()  # 得到二进制数据
    
        # 进行 Base64 编码
        base64_encoded = base64.b64encode(digest)
    
        # URL Encode 得到最终的签名
        return url_encode(base64_encoded.decode('utf-8'))
    
    def get_url(timestamp):
        return f"{URL}?access_token={TALK['accessToken']}&timestamp={timestamp}&sign={sign(timestamp)}"
    
    
    def send_message(msg):
        # 示例调用
        timestamp = int(round(time.time() * 1000))
        # print(timestamp)
        url = get_url(timestamp)
        # print(url)
    
        data = {
            "msgtype":"markdown",
            "markdown":{
                "title":"备份通知",
                "text":msg,
            }
        }
    
        resp = requests.post(url=url,data=json.dumps(data),headers={
                "Content-Type": "application/json"
        })
        if not resp.ok:
            print("fail")
    
        print(resp.text)
        print("success")
    

    requirements.txt

    requests
    paramiko
    schedule
    

    也可以使用Dockerfile

    # 使用官方 Python 镜像作为基础镜像
    FROM python:3.11-slim
    
    # 设置工作目录
    WORKDIR /app
    
    # 将当前目录下的文件复制到容器中的 /app 目录
    COPY . /app
    
    # 安装依赖模块
    RUN pip install --no-cache-dir -r requirements.txt
    
    # 设置默认命令
    CMD ["python", "main.py"]
    

    更多用法后续补充..

    上次更新: 2025/05/14, 01:34:05
    opssl命令
    git使用

    ← opssl命令 git使用→

    最近更新
    01
    Caddy操作指南
    04-25
    02
    Swap空间
    04-22
    03
    Alist使用
    04-21
    更多文章>
    Theme by Vdoing | Copyright © 2022-2025 YAN | MIT License
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式