管理后台这个东西,当然可以不在公网暴露,但是如果一旦在公网允许访问配置,此时就会出现一个很尴尬的问题,各种证书错误提示。
在上一篇文章提过,waf 是通过 docker 部署的。相对来说管理倒是也算方便,按照官方文档的说法,管理后台的证书在下面的位置:
南墙管理后台的配置位于/uuwaf/web/conf/config.json中,addr字段值即为ip地址和端口。替换SSL证书可以替换/uuwaf/web/conf/目录中的server.crt和server.key文件,之后执行systemctl restart uuwaf重启服务使配置生效。
那么要更新证书,只需要解决下面几个问题就行了,由于不想付费买证书,那么现在最好的思路就是直接通过 acme.sh 自动申请证书,让后写个小工具自动将相关的文件复制到指定的目录下,重启 docker 服务就可以了。
1.acme.sh 自动申请证书。
a.安装 acme.sh:
curl https://get.acme.sh | sh -s email=my@example.com
b.配置 dnspod的 api key 和 secret(使用子账号):
创建策略,输入以下内容保存:
{
"statement": [
{
"action": [
"dnspod:DescribeRecordFilterList",
"dnspod:DescribeRecordList",
"dnspod:CreateRecord",
"dnspod:DeleteRecord"
],
"effect": "allow",
"resource": [
"*"
]
}
],
"version": "2.0"
}
登录 腾讯云控制台,进入 访问管理 页面,单击左侧菜单栏的 用户列表,进入用户列表页面,并单击新建用户。
创建 api 访问账号之后,写一个获取证书的脚本:
export Tencent_SecretId="key" export Tencent_SecretKey="secret" "/usr/local/acme.sh"/acme.sh --issue --dns dns_tencent -d lang.bi -d *.lang.bi
到这里第一步就完成了,不过需要注意的事有的扩展名不支持,例如 by,本来想用 oba.by 域名的,结果提示失败了:
sh cert_get.sh
[Wed Apr 2 08:51:41 AM CST 2025] Using CA: https://acme.zerossl.com/v2/DV90
[Wed Apr 2 08:51:41 AM CST 2025] Account key creation OK.
[Wed Apr 2 08:51:41 AM CST 2025] No EAB credentials found for ZeroSSL, let's obtain them
[Wed Apr 2 08:51:43 AM CST 2025] Registering account: https://acme.zerossl.com/v2/DV90
[Wed Apr 2 08:52:14 AM CST 2025] Registered
[Wed Apr 2 08:52:14 AM CST 2025] ACCOUNT_THUMBPRINT='mri378DxKFRt5hzNd_P7HBLV1zo4c7n1g7HBVNAKG-s'
[Wed Apr 2 08:52:14 AM CST 2025] Creating domain key
[Wed Apr 2 08:52:14 AM CST 2025] The domain key is here: /root/.acme.sh/oba.by_ecc/oba.by.key
[Wed Apr 2 08:52:14 AM CST 2025] Multi domain='DNS:oba.by,DNS:*.oba.by'
[Wed Apr 2 08:52:16 AM CST 2025] Error creating new order. Le_OrderFinalize not found. {"type":"urn:ietf:params:acme:error:rejectedIdentifier","status":400,"detail":"DNS identifier is disallowed [oba.by]"}
[Wed Apr 2 08:52:16 AM CST 2025] Please add '--debug' or '--log' to see more information.
[Wed Apr 2 08:52:16 AM CST 2025] See: https://github.com/acmesh-official/acme.sh/wiki/How-to-debug-acme.sh
h4ck# vim cert_get.sh
h4ck# sh cert_get.sh
[Wed Apr 2 08:54:34 AM CST 2025] Using CA: https://acme.zerossl.com/v2/DV90
[Wed Apr 2 08:54:34 AM CST 2025] Multi domain='DNS:oba.by,DNS:www.oba.by,DNS:nas.oba.by'
[Wed Apr 2 08:55:23 AM CST 2025] Error creating new order. Le_OrderFinalize not found. {"type":"urn:ietf:params:acme:error:rejectedIdentifier","status":400,"detail":"DNS identifier is disallowed [oba.by]"}
[Wed Apr 2 08:55:23 AM CST 2025] Please add '--debug' or '--log' to see more information.
[Wed Apr 2 08:55:23 AM CST 2025] See: https://github.com/acmesh-official/acme.sh/wiki/How-to-debug-acme.sh
2.复制文件重启服务
至于第二步就更简单了,直接让 cursor 给写一个:
参考下面的内容给我编写一个 Python3 的文件,实现以下功能:首先需要调用 get_cert.sh 脚本,自动获取证书文件文件,如果获取成功,证书会保存在下面的路径:/root/.acme.sh/lang.bi_ecc/fullchain.cer 私钥会保存在下面的路径/root/.acme.sh/lang.bi_ecc/lang.bi.key;获取到这两个文件之后,需要根据判断证书文件是否变化(记录旧证书内容,用于判断文件变更),如果变化则需要更新对应 docker 下的证书和私钥文件;docker 对应container Id 为f7dd0b0a990b,对应的证书文件路径为/uuwaf/web/conf/目录中的server.crt和server.key文件,在替换文件之后,需要重启对应的 docker 容器。按照步骤实现上面的内容,并且完成代码编写
最终代码:
#!/usr/bin/env python3
import os
import subprocess
import hashlib
import json
from pathlib import Path
# Configuration
DOCKER_CONTAINER_ID = "f7dd0b0a990b"
CERT_SOURCE_DIR = "/root/.acme.sh/lang.bi_ecc"
CERT_DEST_DIR = "/uuwaf/web/conf"
CERT_FILE = "fullchain.cer"
KEY_FILE = "lang.bi.key"
DEST_CERT_FILE = "server.crt"
DEST_KEY_FILE = "server.key"
HASH_FILE = "cert_hash.json"
CERT_SCRIPT = "get_cert.sh"
def get_cert_hash(file_path):
"""Calculate SHA-256 hash of a file."""
sha256_hash = hashlib.sha256()
with open(file_path, "rb") as f:
for byte_block in iter(lambda: f.read(4096), b""):
sha256_hash.update(byte_block)
return sha256_hash.hexdigest()
def save_cert_hash(cert_hash):
"""Save certificate hash to a JSON file."""
with open(HASH_FILE, 'w') as f:
json.dump({'cert_hash': cert_hash}, f)
def load_cert_hash():
"""Load certificate hash from JSON file."""
try:
with open(HASH_FILE, 'r') as f:
data = json.load(f)
return data.get('cert_hash')
except (FileNotFoundError, json.JSONDecodeError):
return None
def run_get_cert_script():
"""Run the get_cert.sh script."""
script_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), CERT_SCRIPT)
# Check if script exists
if not os.path.exists(script_path):
print(f"Error: {CERT_SCRIPT} not found in the current directory")
print(f"Expected path: {script_path}")
return False
# Check if script is executable
if not os.access(script_path, os.X_OK):
print(f"Error: {CERT_SCRIPT} is not executable")
print("Attempting to make it executable...")
try:
os.chmod(script_path, 0o755)
print("Successfully made the script executable")
except Exception as e:
print(f"Failed to make script executable: {str(e)}")
return False
try:
# Use absolute path to the script
result = subprocess.run(['sh', script_path], capture_output=True, text=True)
if result.returncode == 0:
print("Certificate generation successful")
return True
else:
print(f"Certificate generation ignored: {result.stderr}")
return True
except Exception as e:
print(f"Error running {CERT_SCRIPT}: {str(e)}")
return False
def copy_cert_files():
"""Copy certificate files to Docker container."""
try:
# Copy certificate
subprocess.run([
'docker', 'cp',
f"{CERT_SOURCE_DIR}/{CERT_FILE}",
f"{DOCKER_CONTAINER_ID}:{CERT_DEST_DIR}/{DEST_CERT_FILE}"
], check=True)
# Copy private key
subprocess.run([
'docker', 'cp',
f"{CERT_SOURCE_DIR}/{KEY_FILE}",
f"{DOCKER_CONTAINER_ID}:{CERT_DEST_DIR}/{DEST_KEY_FILE}"
], check=True)
print("Certificate files copied successfully")
return True
except subprocess.CalledProcessError as e:
print(f"Error copying files: {str(e)}")
return False
def restart_docker_container():
"""Restart the Docker container."""
try:
subprocess.run(['docker', 'restart', DOCKER_CONTAINER_ID], check=True)
print("Docker container restarted successfully")
return True
except subprocess.CalledProcessError as e:
print(f"Error restarting container: {str(e)}")
return False
def main():
# Step 1: Run get_cert.sh script
if not run_get_cert_script():
print("Failed to generate certificates")
return
# Step 2: Check if certificate files exist
cert_path = os.path.join(CERT_SOURCE_DIR, CERT_FILE)
key_path = os.path.join(CERT_SOURCE_DIR, KEY_FILE)
if not (os.path.exists(cert_path) and os.path.exists(key_path)):
print("Certificate files not found")
return
# Step 3: Calculate new certificate hash
new_cert_hash = get_cert_hash(cert_path)
old_cert_hash = load_cert_hash()
# Step 4: Check if certificate has changed
if new_cert_hash != old_cert_hash:
print("Certificate has changed, updating...")
# Copy new certificate files
if copy_cert_files():
# Restart Docker container
if restart_docker_container():
# Save new certificate hash
save_cert_hash(new_cert_hash)
print("Certificate update completed successfully")
else:
print("Failed to restart Docker container")
else:
print("Failed to copy certificate files")
else:
print("Certificate has not changed, no update needed")
if __name__ == "__main__":
main()
除了运行sh 脚本有点问题,需要改一下,其他的基本都没啥问题。最终执行效果:
此时刷新页面,一切就都 ok 了:




33 comments
过几天我也得搞三个月的证书了。
现在基本都这个长度了
waf是部署在独立主机上的还是部署在使用主机上,我想到之前有一个雷池waf。
都可以 参考这个
https://h4ck.org.cn/2025/03/20030
昨天刚测试南墙,结果我添加网站后连证书都不知道咋绑定呜呜呜呜,又滚回雷池了,虽然内存大,但是用习惯了
证书管理直接添加,自动匹配,不需要绑定,哈哈哈
刚开始用我也是一脸懵逼,想咋没有绑定的地方。
通配符也可以吗
可以啊,为啥不行?
奇怪,那我再试试吧QAQ
看我第二篇的截图,已经实现自动化了。嘎嘎
这个是可以自动更新证书么,我从51ssl那申请的,不知道可不可以这样
我在干的事情不就是自动去更新证书?
没懂原理,是爬虫登录网站后自动续期证书么,我那51ssl登录时要求刷很难做的验证码好像,还有申请证书好多环节
啥?
没看懂,储备知识不够
,waf是防火墙,ssh证书是要申请的,只是这个申请要自动化的话有点难
自动申请直接用 acme.sh 反而没那么难,麻烦的是自动部署。
没有一年的免费证书可以领取后好麻烦,现在用1panel自带的申请用着
能自动就行了,用啥无所谓
像这种能靠不多的体力活能实现的,我通常很少去钻研技术了,还是技穷和懒惰在作祟。
嗐,算不上技术,纯粹瞎整而已
设计后台的时候我也想过不用 /admin/,写个非常规的入口防一下。然后转念一想,没改。不要问为什么。问就是自信。
要啥自行车啊
南墙 WAF和 HestiaCP 对比,如果只能二选一,我会推荐 HestiaCP。
理由如下:
易用性: HestiaCP 的界面更加简洁直观,即使是新手用户也能快速上手。相比之下,南墙 WAF 的配置相对复杂,需要一定的技术基础。对于只想快速搭建网站并进行简单管理的用户来说,HestiaCP 更友好。
功能集成: HestiaCP 集成了 Web 服务器、数据库、邮件服务器、DNS 服务器等常用功能,提供了一站式解决方案。而南墙 WAF 主要专注于 Web 应用防火墙功能,需要与其他面板或服务配合使用,增加了配置和管理的复杂性。
资源占用: HestiaCP 的资源占用相对较低,适合在 VPS 等资源有限的环境中运行。南墙 WAF 作为安全防护软件,本身也会消耗一定的系统资源。
成本: HestiaCP 是免费开源的,而南墙 WAF 是商业产品,需要付费使用。对于个人用户或小型网站来说,HestiaCP 的免费优势非常明显。
这俩不是一个东西,我不需要各种管理面板,我需要的是 waf 防火墙
确实,这两者是不同的东西,最终当然是根据自己的需求做出选择。
文章看不懂,但图都挺不错,蹦蹦跳跳的 (*^▽^*)
跳😉
我的宝塔后台登录界面就是这种提示,一直没有管过。
Cursor已经用废两回了,这次再删除账号,用同一个邮箱申请,不知道会不会被封。
证书不匹配就是这样的
这个就不知道啦
SSL证书,我都是3个月更换一次。虽然没有多大事儿,但有时候也觉得麻烦。
主要是外面卖的SSL证书都太过了,要是一年不到10块钱,我应该会去买个。
自己搞自动更新,我没研究过,写代码什么的对我来说难度太大了。
但是听说宝塔面板可以设置自动更新,等下一次的时候我试试看。
免费的麻烦 花钱买的贵
才想起我那个小鸡才1G的内存,估计够呛了。
现在用1panel里面有自动续功能,也差不多是这样子原理 acme+dns账户