uuWAF icon indicating copy to clipboard operation
uuWAF copied to clipboard

希望添加SSL证书挂载目录

Open wodeupan1 opened this issue 1 year ago • 7 comments

因为没有公网80和443端口,现阶段无法使用南墙代替acme。 但南墙手动添加的证书是写入数据库中,无法通过替换文件的方式更新最新的证书。 希望能给个文件目录,系统自动将目录中的证书导入到数据库。

wodeupan1 avatar Dec 31 '24 03:12 wodeupan1

我们会视情况而定

Safe3 avatar Dec 31 '24 06:12 Safe3

https://gitee.com/lsmir2/docker-uuwaf 我做了处理 data/updateWafssl 程序可以更新数据库证书

#配置路径
wafupdate=/opt/docker/docker-uuwaf/data/updateWafssl 
#修改证书的路径, sql 账号密码 
$wafupdate -n "server.crt"   -k "server.key"  -s "uuwaf:[email protected]:3306/uuwaf" --id 1

21307369 avatar Dec 31 '24 19:12 21307369

抛砖引玉吧,写了个python脚本,青龙面板每个月执行一次

from datetime import datetime
import os
import requests
import json
import time

# 读取当前目录下指定文件名的文件,返回字符串
def read_file(filename):
    return open(os.path.join(os.path.dirname(__file__), filename), 'r').read()

# 提取acme证书目录配置中的时间戳
def get_time(time):
    return time.split('Le_CertCreateTime')[1].split('\'')[1]

cert = read_file('fullchain.cer')
key = read_file('com.key')
dt = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(int(get_time(read_file('com.conf')))))
ct= datetime.now().strftime('%Y-%m-%d %H:%M:%S')

# 组合成请求体
post_json = {
    "id": "0",
    "sni": "[\"*.证书域名.com\",\"证书域名.com\"]",
    "cert": cert,
    "key": key,
    "expire_time": dt,
    "update_time": ct
}


pwd = '{"usr":"南墙管理员","pwd":"密码","otp":""}'

# 获取token
token = requests.post('https://192.168.20.112:4443/api/v1/users/login',data=pwd,verify=False).json()['token']

# 提交证书数据
requests.post('https://192.168.20.112:4443/api/v1/certs/config',data=json.dumps(post_json),headers={"authorization": token},verify=False)

wodeupan1 avatar Jan 06 '25 08:01 wodeupan1

用上述大佬的脚本存在一个问题:提交证书数据如果用post方法的话是新增证书,更新证书需要用put方法。 脚本使用:python3 auto_update_ssl.py --cert-dir ./ --base-url https://waf服务器IP地址:4443 --cert-id 1 以下是我修正后的脚本: `# !/usr/bin/env python3

-- coding: utf-8 --

import argparse import json import logging import os import sys from datetime import datetime from typing import Optional

import pytz import requests import urllib3 from cryptography import x509 from cryptography.hazmat._oid import ExtensionOID from cryptography.hazmat.backends import default_backend

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

def setup_logging() -> None: """配置日志记录器""" logging.basicConfig( level=logging.INFO, format='%(asctime)s [%(levelname)s] %(message)s', handlers=[ logging.FileHandler('ssl_update.log'), logging.StreamHandler() ] )

def read_file(filepath: str) -> str: """读取文件内容并验证非空""" try: with open(filepath, 'r', encoding='utf-8') as f: content = f.read().strip() if not content: raise ValueError(f"Empty file: {filepath}") return content except (IOError, ValueError) as e: logging.error("File read failed: %s", str(e)) sys.exit(1)

def get_cert_expiry(cert_data: str) -> str: """获取证书过期时间(上海时区)""" try: cert = x509.load_pem_x509_certificate( cert_data.encode('utf-8'), default_backend() ) sans = cert.extensions.get_extension_for_oid(ExtensionOID.SUBJECT_ALTERNATIVE_NAME).value dns_names = [san.value for san in sans if isinstance(san, x509.DNSName)] print(dns_names) return cert.not_valid_after_utc.astimezone( pytz.timezone('Asia/Shanghai') ).strftime('%Y-%m-%d %H:%M:%S') except Exception as e: logging.error("证书过期时间获取失败: %s", str(e)) sys.exit(1)

def get_cert_sni(cert_data: str) -> list[str]: """获取证书SNI信息""" try: cert = x509.load_pem_x509_certificate( cert_data.encode('utf-8'), default_backend() ) sans = cert.extensions.get_extension_for_oid(ExtensionOID.SUBJECT_ALTERNATIVE_NAME).value dns_names = [san.value for san in sans if isinstance(san, x509.DNSName)]

    return dns_names
except Exception as e:
    logging.error("证书SNI获取失败: %s", str(e))
    sys.exit(1)

def get_auth_token(base_url: str, user: str, pwd: str) -> Optional[str]: """获取WAF认证token""" try: resp = requests.post( f"{base_url}/api/v1/users/login", json={"usr": user, "pwd": pwd, "otp": ""}, headers={"Content-Type": "application/json"}, verify=False, timeout=10 ) return resp.json().get('token') except Exception as e: logging.error("Auth failed: %s", str(e)) sys.exit(1)

def update_waf_cert( cert_dir: str, base_url: str, cert_id: int, user: str, pwd: str ) -> bool: """更新WAF证书""" try: cert = read_file(os.path.join(cert_dir, 'fullchain.cer')) key = read_file(os.path.join(cert_dir, 'ipiginc.com.key'))

    domains = get_cert_sni(cert)

    resp = requests.put(
        f"{base_url}/api/v1/certs/config",
        json={
            "id": cert_id,
            "sni": json.dumps(domains),
            "cert": cert,
            "key": key,
            "expire_time": get_cert_expiry(cert),
            "update_time": datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        },
        headers={
            "Authorization": get_auth_token(base_url, user, pwd),
            "Content-Type": "application/json"
        },
        verify=False,
        timeout=30
    )
    return resp.status_code == 200
except Exception as e:
    logging.error("Update failed: %s", str(e))
    return False

if name == "main": setup_logging() parser = argparse.ArgumentParser( description="WAF SSL证书自动更新工具" ) parser.add_argument("--cert-dir", required=True, help="证书目录路径") parser.add_argument("--base-url", required=True, help="WAF API基础URL") parser.add_argument("--cert-id", required=True, type=int, help="证书ID") waf_user ="waf管理员账号" waf_pwd ="waf管理员密码"

args = parser.parse_args()
status = update_waf_cert(
    args.cert_dir,
    args.base_url,
    args.cert_id,
    waf_user,
    waf_pwd
)
logging.info("WAF证书更新: %s", "成功" if status else "失败")`

Nuyoah66 avatar May 19 '25 02:05 Nuyoah66

v7更新了接口,并添加了api token,脚本也同步更新一下

` import os import requests import json

requests.packages.urllib3.disable_warnings()

def read_file(filename): return open(os.path.join(os.path.dirname(file), filename), 'r', encoding='utf-8').read()

cert = read_file('fullchain.cer') key = read_file('hutnis.com.key') post_json = {"id":3,"name":"域名","type":1,"email":"","dns_challenge":False,"dns_provider":"","dns_credential":"","sni":"[域名]","crt":cert,"key":key} headers={'Api-Token': 'token','content-type': 'application/json'} updata = requests.put('https://192.168.20.112:4443/api/v1/certs',data=json.dumps(post_json),headers=headers,verify=False) `

wodeupan1 avatar Aug 19 '25 04:08 wodeupan1

这边做一个优化的版本,可以提示错误 updatecert.py 需要修改mysite、mytoken、证书路径、uusec url


import os
import requests
import json

requests.packages.urllib3.disable_warnings()

def read_file(filename):
    return open(os.path.join(os.path.dirname(__file__), filename), 'r', encoding='utf-8').read()

cert = read_file('./nginx/cert/mysite.top.crt')
key = read_file('./nginx/cert/mysite.top.key')

post_json = {
    "id": 2,
    "name": "*.mysite.top",
    "type": 1,
    "email": "",
    "dns_challenge": False,
    "dns_provider": "",
    "dns_credential": "",
    "sni": "[\"*.mysite.top\"]",
    "crt": cert,
    "key": key
}

headers = {
    'Api-Token': 'mytoken',
    'Content-Type': 'application/json'
}

url = 'https://192.168.100.47:4443/api/v1/certs'

print(f"➡️ Sending PUT request to {url} ...")

try:
    response = requests.put(url, data=json.dumps(post_json), headers=headers, verify=False)
    print(f"✅ Status Code: {response.status_code}")
    print("🔹 Response Body:")
    print(response.text)

    # 如果返回JSON,也可以尝试解析
    try:
        print("🔹 Parsed JSON:")
        print(json.dumps(response.json(), indent=4, ensure_ascii=False))
    except Exception:
        pass

except requests.exceptions.RequestException as e:
    print(f"❌ Request failed: {e}")

shell脚本,同样修改mysite、mytoken、证书路径、uusec url,已在neilpang/acme.sh docker容器中校验过

#!/bin/sh
set -e

API_TOKEN="mytoken"
API_URL="https://192.168.100.47:4443/api/v1/certs"

CRT_FILE="./nginx/cert/mysite.top.crt"
KEY_FILE="./nginx/cert/mysite.top.key"

# 读取证书内容并转义为 JSON 安全格式(换行 → \n,双引号 → \")
CRT_CONTENT=$(sed ':a;N;$!ba;s/\\/\\\\/g;s/"/\\"/g;s/\n/\\n/g' "$CRT_FILE")
KEY_CONTENT=$(sed ':a;N;$!ba;s/\\/\\\\/g;s/"/\\"/g;s/\n/\\n/g' "$KEY_FILE")

# 组装 JSON(用 printf 避免变量展开中的空格问题)
POST_JSON=$(printf '{
  "id": 2,
  "name": "*.mysite.top",
  "type": 1,
  "email": "",
  "dns_challenge": false,
  "dns_provider": "",
  "dns_credential": "",
  "sni": "[\"*.mysite.top\"]",
  "crt": "%s",
  "key": "%s"
}' "$CRT_CONTENT" "$KEY_CONTENT")

echo "➡️ Sending PUT request to $API_URL ..."
RESPONSE=$(curl -sk -X PUT "$API_URL" \
  -H "Api-Token: $API_TOKEN" \
  -H "Content-Type: application/json" \
  -d "$POST_JSON")

echo "✅ Response:"
echo "$RESPONSE"

Lxeon avatar Oct 21 '25 02:10 Lxeon

Nice work!

Safe3 avatar Oct 21 '25 09:10 Safe3