原创内容,转载请注明出处:https://www.myzhenai.com.cn/post/4869.html
我们博客的服务器是阿里云的,所以我以前一直都使用阿里云的个人免费证书,一年有20个证书
原本个人免费证书是一年的,后面改成90天,
但是我今天发现我的证书过期了,并且我一年20个证书的额度也用完了
怎么办?
我知道有acme.sh和certbot可以自动安装证书
在配置脚本之前,我们先要确定自己需要以哪种方式进行验证,
我们知道,在验证证书的时候有三种方式进行验证,分别是dns验证、文件验证和网站代码验证
我们经常使用的是dns验证,就是你要在你的dns记录中添加验证信息,文件验证是打开一个文件来验证你是网站的实际控制者
因为有些朋友不一定懂得怎样设置dns记录,所以我们本文分别是两种验证方式
如果你选择dns记录验证,那么就要做一些设置,例如阿里云,你需要一个可以检测dns的AccessKey ID和AccessKey Secret
怎样获得这两个参数呢?
1、登录阿里云官网(https://www.aliyun.com/),使用你域名的管理账号
2、创建一个仅拥有「DNS 管理权限」的子账号,避免泄露主账号全部权限
3、点击右上角用户头像那里,点击“访问控制”
4、在RAM访问控制面板左侧,点击用户,创建用户,勾选 “使用永久 AccessKey 访问创建 AccessKey ID 和 AccessKey Secret,支持通过 API 或其他开发工具访问” 选项
5、获取 AccessKey ID 和 Secret(最重要!)
创建成功后,会弹出「AccessKey 创建成功」提示框,显示:
AccessKey ID:一串字母 + 数字(如 LTAI5txxxxxxxxxxxxxxx)
AccessKey Secret:一串随机字符(如 8txxxxxxxxxxxxxxxxxxxxxxxx)
务必:
复制这两个值保存到本地(Secret 仅显示这一次,后续无法查看)
不要泄露给他人,泄露后立即在 IAM 控制台禁用该 AccessKey
6、权限策略搜索框输入「AliyunDNSFullAccess」,勾选该策略(仅这一个即可,无需其他权限)
那么现在我们就可以来实现这两个脚本的安装证书实现方法,两个脚本使用一个就可以
acme.sh
#!/bin/bash
# 功能:申请Let's Encrypt 90天免费证书,支持HTTP/DNS验证,自动更新&复制证书
# 依赖:acme.sh(自动检测安装)、curl/wget(acme.sh安装依赖)
##############################################################################
# 1. 检查并安装acme.sh(若未安装)
##############################################################################
if ! command -v acme.sh &> /dev/null; then
echo "未检测到acme.sh,开始自动安装..."
# 官方安装命令(默认安装到 ~/.acme.sh/)
curl https://get.acme.sh | sh -s email=your-email@example.com
# 加载acme.sh环境变量(避免重启终端)
source ~/.bashrc
echo "acme.sh安装完成!"
fi
##############################################################################
# 2. 读取用户输入(域名、验证方式、网站路径、目标目录)
##############################################################################
# 输入域名
read -p "请输入需要申请证书的域名(例如:example.com 或 www.example.com):" DOMAIN
# 选择验证方式
echo -e "\n请选择证书验证方式:"
echo "1. HTTP检测(需网站可访问,无需修改DNS)"
echo "2. DNS检测(无需网站上线,需手动添加DNS TXT记录)"
read -p "输入选择(1/2):" VALIDATION_METHOD
# 若选择HTTP检测,读取网站根目录
if [ "$VALIDATION_METHOD" = "1" ]; then
read -p "请输入网站根目录路径(例如:/var/www/html 或 /usr/share/nginx/html):" WEBROOT
# 验证目录是否存在
if [ ! -d "$WEBROOT" ]; then
echo "错误:网站目录 $WEBROOT 不存在!"
exit 1
fi
fi
# 输入证书目标目录(复制证书用)
read -p "请输入证书目标存储目录(例如:/etc/nginx/ssl):" TARGET_DIR
# 自动创建目标目录(若不存在)
mkdir -p "$TARGET_DIR" || { echo "错误:无法创建目标目录 $TARGET_DIR!"; exit 1; }
##############################################################################
# 3. 申请Let's Encrypt证书
##############################################################################
echo -e "\n开始申请 $DOMAIN 的证书..."
if [ "$VALIDATION_METHOD" = "1" ]; then
# HTTP验证方式(--webroot 模式,acme.sh会在网站目录下创建.well-known目录)
acme.sh --issue -d "$DOMAIN" --webroot -w "$WEBROOT" --server letsencrypt
elif [ "$VALIDATION_METHOD" = "2" ]; then
# DNS验证方式(--dns 模式,需手动添加TXT记录,按提示操作)
acme.sh --issue -d "$DOMAIN" --dns --server letsencrypt
else
echo "错误:无效的验证方式选择!"
exit 1
fi
# 验证证书申请是否成功
if [ -f "$HOME/.acme.sh/$DOMAIN/$DOMAIN.key" ] && [ -f "$HOME/.acme.sh/$DOMAIN/fullchain.cer" ]; then
echo "证书申请成功!"
else
echo "错误:证书申请失败,请检查操作步骤或网络环境!"
exit 1
fi
##############################################################################
# 4. 复制证书到目标目录(覆盖旧证书)
##############################################################################
echo -e "\n开始复制证书到目标目录 $TARGET_DIR..."
cp -f "$HOME/.acme.sh/$DOMAIN/$DOMAIN.key" "$TARGET_DIR/$DOMAIN.key"
cp -f "$HOME/.acme.sh/$DOMAIN/fullchain.cer" "$TARGET_DIR/$DOMAIN.crt"
echo "证书复制完成!"
echo "私钥路径:$TARGET_DIR/$DOMAIN.key"
echo "证书链路径:$TARGET_DIR/$DOMAIN.crt"
##############################################################################
# 5. 设置自动更新(每周日凌晨3点检测,有效期<10天则更新)
##############################################################################
# 检查是否已存在定时任务
CRON_JOB="0 3 * * 0 $HOME/.acme.sh/acme.sh --cron --home $HOME/.acme.sh/ --renew-hook \"cp -f $HOME/.acme.sh/$DOMAIN/$DOMAIN.key $TARGET_DIR/ && cp -f $HOME/.acme.sh/$DOMAIN/fullchain.cer $TARGET_DIR/ && echo '$(date +%Y-%m-%d_%H:%M:%S) - $DOMAIN 证书更新成功' >> /var/log/acme_cert_renew.log\""
if ! crontab -l 2>/dev/null | grep -q "$DOMAIN"; then
# 添加定时任务到crontab
(crontab -l 2>/dev/null; echo "$CRON_JOB") | crontab -
echo -e "\n自动更新任务已设置:每周日凌晨3点检测证书有效期,少于10天自动更新并同步到目标目录"
echo "更新日志路径:/var/log/acme_cert_renew.log"
else
echo -e "\n该域名的自动更新任务已存在,无需重复设置"
fi
##############################################################################
# 6. 额外完善功能:证书备份(备份到目标目录的backup子目录)
##############################################################################
BACKUP_DIR="$TARGET_DIR/backup"
mkdir -p "$BACKUP_DIR"
cp -f "$TARGET_DIR/$DOMAIN.key" "$BACKUP_DIR/$(date +%Y%m%d)_$DOMAIN.key"
cp -f "$TARGET_DIR/$DOMAIN.crt" "$BACKUP_DIR/$(date +%Y%m%d)_$DOMAIN.crt"
echo -e "\n证书备份完成:$BACKUP_DIR/$(date +%Y%m%d)_$DOMAIN.*"
#!/bin/bash
# ============================================
# acme.sh SSL证书管理脚本
# 功能:申请、检测、更新和部署SSL证书
# ============================================
# 配置常量
CERT_CHECK_INTERVAL="7" # 检查间隔(天)
CERT_RENEW_THRESHOLD="10" # 续期阈值(天)
CRON_SCHEDULE="0 0 * * 0" # 每周日0点执行
# 颜色输出函数
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
print_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
print_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
print_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# 检查acme.sh是否安装
check_acme_installed() {
if command -v acme.sh &> /dev/null; then
print_info "acme.sh 已安装"
return 0
else
print_warning "acme.sh 未安装,尝试自动安装..."
# 尝试安装acme.sh
if curl https://get.acme.sh | sh; then
print_success "acme.sh 安装成功"
# 重新加载bash配置
source ~/.bashrc 2>/dev/null || source ~/.profile 2>/dev/null
return 0
else
print_error "acme.sh 安装失败,请手动安装"
print_info "手动安装命令: curl https://get.acme.sh | sh"
exit 1
fi
fi
}
# 获取用户输入
get_user_input() {
echo "=================================="
echo " SSL证书管理工具 (acme.sh) "
echo "=================================="
# 输入域名
while true; do
read -p "请输入域名 (多个域名用空格分隔): " domain_input
if [ -z "$domain_input" ]; then
print_error "域名不能为空"
else
DOMAINS=($domain_input)
# 验证域名格式
invalid_domains=()
for domain in "${DOMAINS[@]}"; do
if ! [[ "$domain" =~ ^[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z]{2,})+$ ]]; then
invalid_domains+=("$domain")
fi
done
if [ ${#invalid_domains[@]} -eq 0 ]; then
break
else
print_error "以下域名格式无效: ${invalid_domains[*]}"
fi
fi
done
# 选择验证方式
echo ""
echo "请选择验证方式:"
echo "1) HTTP验证 (需要网站可访问)"
echo "2) DNS验证 (需要修改DNS记录)"
echo "3) DNS API验证 (支持自动DNS验证)"
while true; do
read -p "请选择 [1-3]: " verify_method
case $verify_method in
1)
VERIFY_METHOD="http"
read -p "请输入网站根目录路径 [默认: /var/www/html]: " web_root
WEB_ROOT=${web_root:-/var/www/html}
# 检查目录是否存在
if [ ! -d "$WEB_ROOT" ]; then
print_warning "目录不存在,将尝试创建: $WEB_ROOT"
sudo mkdir -p "$WEB_ROOT"
fi
break
;;
2)
VERIFY_METHOD="dns"
print_info "请稍后在DNS管理界面添加TXT记录"
break
;;
3)
VERIFY_METHOD="dns-api"
echo ""
echo "支持的DNS提供商:"
echo "1) Cloudflare"
echo "2) Aliyun"
echo "3) DNSPod"
echo "4) GoDaddy"
echo "5) CloudXNS"
read -p "选择DNS提供商 [1-5]: " dns_provider_num
case $dns_provider_num in
1) DNS_PROVIDER="cloudflare" ;;
2) DNS_PROVIDER="dns_ali" ;;
3) DNS_PROVIDER="dns_dp" ;;
4) DNS_PROVIDER="dns_gd" ;;
5) DNS_PROVIDER="cloudxns" ;;
*) DNS_PROVIDER="cloudflare" ;;
esac
print_info "请设置DNS API凭证:"
case $DNS_PROVIDER in
cloudflare)
read -p "Cloudflare Email: " CF_Email
read -p "Cloudflare API Key: " CF_Key
export CF_Email="$CF_Email"
export CF_Key="$CF_Key"
;;
dns_ali)
read -p "Aliyun AccessKey ID: " Ali_Key
read -p "Aliyun AccessKey Secret: " Ali_Secret
export Ali_Key="$Ali_Key"
export Ali_Secret="$Ali_Secret"
;;
esac
break
;;
*)
print_error "无效选择"
;;
esac
done
# 询问证书部署路径
echo ""
read -p "请输入证书部署目录 [默认: /etc/ssl/certs]: " deploy_dir
DEPLOY_DIR=${deploy_dir:-/etc/ssl/certs}
# 创建部署目录
if [ ! -d "$DEPLOY_DIR" ]; then
print_info "创建部署目录: $DEPLOY_DIR"
sudo mkdir -p "$DEPLOY_DIR"
fi
# 询问是否安装到Nginx/Apache
read -p "是否自动配置Web服务器? [y/N]: " config_web
if [[ $config_web =~ ^[Yy]$ ]]; then
echo "选择Web服务器:"
echo "1) Nginx"
echo "2) Apache"
read -p "请选择 [1-2]: " web_server
fi
}
# 申请证书
issue_certificate() {
print_info "开始申请SSL证书..."
# 构建域名参数
domain_args=""
for domain in "${DOMAINS[@]}"; do
if [ "$domain" == "${DOMAINS[0]}" ]; then
domain_args="-d $domain"
else
domain_args="$domain_args -d $domain"
fi
done
# 根据验证方式执行不同命令
case $VERIFY_METHOD in
http)
print_info "使用HTTP验证方式"
acme.sh --issue $domain_args --webroot "$WEB_ROOT" --force
;;
dns)
print_info "使用DNS验证方式"
print_info "请按照提示添加DNS TXT记录"
acme.sh --issue $domain_args --dns --yes-I-know-dns-manual-mode-enough-go-ahead-please --force
;;
dns-api)
print_info "使用DNS API验证方式 (${DNS_PROVIDER})"
acme.sh --issue $domain_args --dns "$DNS_PROVIDER" --force
;;
esac
if [ $? -eq 0 ]; then
print_success "证书申请成功"
return 0
else
print_error "证书申请失败"
return 1
fi
}
# 部署证书
deploy_certificate() {
local main_domain="${DOMAINS[0]}"
print_info "部署证书到: $DEPLOY_DIR"
# 证书源路径
CERT_SOURCE="$HOME/.acme.sh/$main_domain"
if [ ! -d "$CERT_SOURCE" ]; then
print_error "证书目录不存在: $CERT_SOURCE"
return 1
fi
# 创建域名子目录
CERT_DEPLOY_DIR="$DEPLOY_DIR/$main_domain"
sudo mkdir -p "$CERT_DEPLOY_DIR"
# 复制证书文件
sudo cp "$CERT_SOURCE/fullchain.cer" "$CERT_DEPLOY_DIR/fullchain.pem"
sudo cp "$CERT_SOURCE/$main_domain.key" "$CERT_DEPLOY_DIR/privkey.pem"
sudo cp "$CERT_SOURCE/ca.cer" "$CERT_DEPLOY_DIR/chain.pem"
# 设置权限
sudo chmod 600 "$CERT_DEPLOY_DIR/privkey.pem"
sudo chmod 644 "$CERT_DEPLOY_DIR/fullchain.pem"
sudo chmod 644 "$CERT_DEPLOY_DIR/chain.pem"
print_success "证书已部署到: $CERT_DEPLOY_DIR"
# 生成合并证书(部分服务器需要)
sudo cat "$CERT_DEPLOY_DIR/fullchain.pem" "$CERT_DEPLOY_DIR/privkey.pem" | sudo tee "$CERT_DEPLOY_DIR/combined.pem" > /dev/null
# 配置Web服务器
if [[ $config_web =~ ^[Yy]$ ]]; then
configure_webserver "$main_domain" "$CERT_DEPLOY_DIR"
fi
return 0
}
# 配置Web服务器
configure_webserver() {
local domain="$1"
local cert_path="$2"
case $web_server in
1) # Nginx
nginx_conf="/etc/nginx/sites-available/$domain"
if [ -d "/etc/nginx/sites-available" ]; then
sudo tee "$nginx_conf" > /dev/null <<EOF
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name $domain;
ssl_certificate $cert_path/fullchain.pem;
ssl_certificate_key $cert_path/privkey.pem;
ssl_trusted_certificate $cert_path/chain.pem;
# SSL优化配置
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
# 其他配置...
location / {
root /var/www/html;
index index.html;
}
}
EOF
if [ -f "$nginx_conf" ]; then
sudo ln -sf "$nginx_conf" "/etc/nginx/sites-enabled/"
print_info "Nginx配置已创建,请重启Nginx生效"
fi
fi
;;
2) # Apache
apache_conf="/etc/apache2/sites-available/$domain.conf"
if [ -d "/etc/apache2/sites-available" ]; then
sudo tee "$apache_conf" > /dev/null <<EOF
<VirtualHost *:443>
ServerName $domain
SSLEngine on
SSLCertificateFile $cert_path/fullchain.pem
SSLCertificateKeyFile $cert_path/privkey.pem
SSLCertificateChainFile $cert_path/chain.pem
# 其他配置...
DocumentRoot /var/www/html
</VirtualHost>
EOF
if [ -f "$apache_conf" ]; then
sudo a2ensite "$domain.conf" > /dev/null 2>&1
print_info "Apache配置已创建,请重启Apache生效"
fi
fi
;;
esac
}
# 检查证书有效期
check_cert_expiry() {
local domain="${DOMAINS[0]}"
local cert_file="$HOME/.acme.sh/$domain/fullchain.cer"
if [ ! -f "$cert_file" ]; then
print_error "证书文件不存在: $cert_file"
return 1
fi
# 获取证书过期时间
expiry_date=$(openssl x509 -enddate -noout -in "$cert_file" | cut -d= -f2)
expiry_epoch=$(date -d "$expiry_date" +%s)
current_epoch=$(date +%s)
# 计算剩余天数
days_remaining=$(( ($expiry_epoch - $current_epoch) / 86400 ))
echo "=================================="
echo "证书有效期检查:"
echo "域名: $domain"
echo "过期时间: $expiry_date"
echo "剩余天数: $days_remaining 天"
if [ $days_remaining -le $CERT_RENEW_THRESHOLD ]; then
print_warning "证书即将过期 (少于 ${CERT_RENEW_THRESHOLD} 天)"
return 1
else
print_success "证书有效期正常"
return 0
fi
}
# 更新证书
renew_certificate() {
local main_domain="${DOMAINS[0]}"
print_info "开始更新证书..."
# 尝试更新
acme.sh --renew -d "$main_domain" --force
if [ $? -eq 0 ]; then
print_success "证书更新成功"
# 重新部署证书
deploy_certificate
# 重启Web服务器
restart_webserver
return 0
else
print_error "证书更新失败"
return 1
fi
}
# 重启Web服务器
restart_webserver() {
if [[ $config_web =~ ^[Yy]$ ]]; then
case $web_server in
1) # Nginx
if systemctl is-active --quiet nginx; then
sudo systemctl reload nginx
print_info "Nginx已重新加载配置"
fi
;;
2) # Apache
if systemctl is-active --quiet apache2; then
sudo systemctl reload apache2
print_info "Apache已重新加载配置"
fi
;;
esac
fi
}
# 设置定时任务
setup_cron_job() {
local script_path="$(realpath "$0")"
local cron_cmd="$script_path --cron"
# 添加定时任务
(crontab -l 2>/dev/null | grep -v "$script_path"; echo "$CRON_SCHEDULE $cron_cmd") | crontab -
if [ $? -eq 0 ]; then
print_success "定时任务设置成功 (计划: $CRON_SCHEDULE)"
print_info "将自动检查并更新证书"
else
print_error "定时任务设置失败"
fi
}
# 主函数
main() {
# 检查参数
if [ "$1" == "--cron" ]; then
# 定时任务模式
check_cert_expiry
if [ $? -eq 1 ]; then
renew_certificate
fi
exit 0
fi
# 交互模式
check_acme_installed
get_user_input
# 申请证书
issue_certificate
if [ $? -eq 0 ]; then
# 部署证书
deploy_certificate
# 检查有效期
check_cert_expiry
# 设置定时任务
echo ""
read -p "是否设置自动更新定时任务? [Y/n]: " setup_cron
if [[ ! $setup_cron =~ ^[Nn]$ ]]; then
setup_cron_job
fi
# 生成报告
generate_report
fi
}
# 生成报告
generate_report() {
echo ""
echo "=================================="
echo " SSL证书部署报告 "
echo "=================================="
echo "主域名: ${DOMAINS[0]}"
echo "所有域名: ${DOMAINS[*]}"
echo "证书位置: $HOME/.acme.sh/${DOMAINS[0]}/"
echo "部署位置: $DEPLOY_DIR/${DOMAINS[0]}/"
echo "验证方式: $VERIFY_METHOD"
if [ "$VERIFY_METHOD" == "http" ]; then
echo "网站目录: $WEB_ROOT"
fi
echo ""
echo "文件列表:"
echo " - fullchain.pem: 完整证书链"
echo " - privkey.pem: 私钥文件"
echo " - chain.pem: 中间证书"
echo " - combined.pem: 合并证书"
echo ""
echo "自动更新: 已设置每周检查,少于${CERT_RENEW_THRESHOLD}天自动更新"
echo "=================================="
}
# 脚本异常处理
trap 'print_error "脚本执行被中断"; exit 1' INT TERM
# 执行主函数
main "$@"
Certbot
#!/bin/bash
# 功能:申请Let's Encrypt 90天免费证书,支持HTTP/DNS验证(含自动化DNS插件),自动更新&复制证书
# 依赖:certbot + 对应模块(脚本自动检测并提供安装选项)、Python3
##############################################################################
# 1. 检查并安装Certbot及所需模块
##############################################################################
# 检查Certbot主程序
if ! command -v certbot &> /dev/null; then
echo "未检测到Certbot,开始安装主程序..."
if [ -f /etc/os-release ]; then
. /etc/os-release
case $ID in
ubuntu|debian)
sudo apt update && sudo apt install -y certbot python3-certbot-nginx
;;
centos|rhel)
sudo yum install -y epel-release && sudo yum install -y certbot python3-certbot-nginx
;;
*)
echo "未支持的系统,请手动安装Certbot:https://certbot.eff.org/instructions"
exit 1
;;
esac
fi
echo "Certbot主程序安装完成!"
fi
# 选择验证方式(关联模块安装)
echo -e "\n请选择证书验证方式:"
echo "1. HTTP检测(http-01,需网站可访问,依赖 python3-certbot-nginx 模块,已默认安装)"
echo "2. 手动DNS检测(dns-01,无需网站上线,手动添加TXT记录,无需额外模块)"
echo "3. 自动化DNS检测(dns-01,自动添加TXT记录,需安装对应云服务商插件)"
read -p "输入选择(1/2/3):" VALIDATION_METHOD
# 若选择自动化DNS验证,让用户选择云服务商并安装对应模块
DNS_PLUGIN=""
if [ "$VALIDATION_METHOD" = "3" ]; then
echo -e "\n请选择你的DNS服务商(支持常见云厂商):"
echo "1. 阿里云(aliyun)"
echo "2. Cloudflare"
echo "3. 腾讯云(dnspod)"
echo "4. AWS Route53"
read -p "输入选择(1/2/3/4):" DNS_VENDOR
# 安装对应DNS插件(按系统区分)
if [ -f /etc/os-release ]; then
. /etc/os-release
case $DNS_VENDOR in
1)
DNS_PLUGIN="dns-aliyun"
MODULE_NAME="python3-certbot-dns-aliyun"
;;
2)
DNS_PLUGIN="dns-cloudflare"
MODULE_NAME="python3-certbot-dns-cloudflare"
;;
3)
DNS_PLUGIN="dns-dnspod"
MODULE_NAME="python3-certbot-dns-dnspod"
;;
4)
DNS_PLUGIN="dns-route53"
MODULE_NAME="python3-certbot-dns-route53"
;;
*)
echo "无效的DNS服务商选择!"
exit 1
;;
esac
# 安装插件模块
if ! dpkg -l | grep -q "$MODULE_NAME" > /dev/null 2>&1; then
echo "开始安装 $MODULE_NAME 插件..."
case $ID in
ubuntu|debian)
sudo apt update && sudo apt install -y "$MODULE_NAME"
;;
centos|rhel)
sudo yum install -y "$MODULE_NAME"
;;
esac
echo "$MODULE_NAME 插件安装完成!"
fi
fi
fi
##############################################################################
# 2. 读取用户输入(域名、网站路径、目标目录、DNS密钥)
##############################################################################
# 输入域名
read -p "请输入需要申请证书的域名(例如:example.com 或 www.example.com):" DOMAIN
# 若选择HTTP检测,读取网站根目录
if [ "$VALIDATION_METHOD" = "1" ]; then
read -p "请输入网站根目录路径(例如:/var/www/html 或 /usr/share/nginx/html):" WEBROOT
if [ ! -d "$WEBROOT" ]; then
echo "错误:网站目录 $WEBROOT 不存在!"
exit 1
fi
fi
# 若选择自动化DNS检测,读取云服务商密钥(以阿里云为例,其他厂商类似)
if [ "$VALIDATION_METHOD" = "3" ]; then
case $DNS_VENDOR in
1)
# 阿里云:需要AccessKey ID和Secret(需提前创建,权限:管理DNS解析)
read -p "请输入阿里云AccessKey ID:" ALIYUN_ACCESS_KEY
read -p "请输入阿里云AccessKey Secret:" ALIYUN_ACCESS_SECRET
# 生成阿里云配置文件(Certbot插件需要)
ALIYUN_CONFIG="$HOME/.certbot-aliyun.ini"
echo "dns_aliyun_access_key = $ALIYUN_ACCESS_KEY" > "$ALIYUN_CONFIG"
echo "dns_aliyun_access_secret = $ALIYUN_ACCESS_SECRET" >> "$ALIYUN_CONFIG"
chmod 600 "$ALIYUN_CONFIG" # 限制权限,避免密钥泄露
;;
2)
# Cloudflare:需要API Token(权限:Zone.DNS)
read -p "请输入Cloudflare API Token:" CF_API_TOKEN
CF_CONFIG="$HOME/.certbot-cloudflare.ini"
echo "dns_cloudflare_api_token = $CF_API_TOKEN" > "$CF_CONFIG"
chmod 600 "$CF_CONFIG"
;;
# 其他厂商密钥配置逻辑类似,已简化示例
esac
fi
# 输入证书目标目录
read -p "请输入证书目标存储目录(例如:/etc/nginx/ssl):" TARGET_DIR
sudo mkdir -p "$TARGET_DIR" || { echo "错误:无法创建目标目录 $TARGET_DIR!"; exit 1; }
sudo chmod 755 "$TARGET_DIR"
##############################################################################
# 3. 申请Let's Encrypt证书(按验证方式执行)
##############################################################################
echo -e "\n开始申请 $DOMAIN 的证书..."
CERT_PATH="/etc/letsencrypt/live/$DOMAIN"
case $VALIDATION_METHOD in
1)
# HTTP验证(--webroot模式)
sudo certbot certonly --webroot -w "$WEBROOT" -d "$DOMAIN" --agree-tos --email your-email@example.com --quiet
;;
2)
# 手动DNS验证(--manual模式,需按提示添加TXT记录)
sudo certbot certonly --manual -d "$DOMAIN" --preferred-challenges dns --agree-tos --email your-email@example.com
;;
3)
# 自动化DNS验证(使用对应插件,无需手动添加记录)
case $DNS_VENDOR in
1)
sudo certbot certonly --$DNS_PLUGIN --$DNS_PLUGIN-credentials "$ALIYUN_CONFIG" -d "$DOMAIN" --agree-tos --email your-email@example.com --quiet
;;
2)
sudo certbot certonly --$DNS_PLUGIN --$DNS_PLUGIN-credentials "$CF_CONFIG" -d "$DOMAIN" --agree-tos --email your-email@example.com --quiet
;;
esac
;;
*)
echo "错误:无效的验证方式选择!"
exit 1
;;
esac
# 验证证书申请结果
if [ -f "$CERT_PATH/privkey.pem" ] && [ -f "$CERT_PATH/fullchain.pem" ]; then
echo "证书申请成功!"
else
echo "错误:证书申请失败,请检查密钥配置或网络环境!"
exit 1
fi
##############################################################################
# 4. 复制证书到目标目录+权限修复(同之前逻辑)
##############################################################################
echo -e "\n开始复制证书到目标目录 $TARGET_DIR..."
sudo cp -f "$CERT_PATH/privkey.pem" "$TARGET_DIR/$DOMAIN.key"
sudo cp -f "$CERT_PATH/fullchain.pem" "$TARGET_DIR/$DOMAIN.crt"
sudo chmod 644 "$TARGET_DIR/$DOMAIN.key" "$TARGET_DIR/$DOMAIN.crt"
echo "证书复制完成!"
echo "私钥路径:$TARGET_DIR/$DOMAIN.key"
echo "证书链路径:$TARGET_DIR/$DOMAIN.crt"
##############################################################################
# 5. 自动更新+日志(同之前逻辑,已兼容自动化DNS验证)
##############################################################################
CRON_JOB="0 4 * * 0 sudo certbot renew --quiet --renew-hook \"cp -f $CERT_PATH/privkey.pem $TARGET_DIR/ && cp -f $CERT_PATH/fullchain.pem $TARGET_DIR/ && echo '$(date +%Y-%m-%d_%H:%M:%S) - $DOMAIN 证书更新成功' >> /var/log/certbot_cert_renew.log\""
if ! crontab -l 2>/dev/null | grep -q "$DOMAIN"; then
(sudo crontab -l 2>/dev/null; echo "$CRON_JOB") | sudo crontab -
echo -e "\n自动更新任务已设置:每周日凌晨4点检测证书有效期,少于10天自动更新并同步到目标目录"
fi
##############################################################################
# 6. 证书备份(同之前逻辑)
##############################################################################
BACKUP_DIR="$TARGET_DIR/backup"
sudo mkdir -p "$BACKUP_DIR"
sudo cp -f "$TARGET_DIR/$DOMAIN.key" "$BACKUP_DIR/$(date +%Y%m%d)_$DOMAIN.key"
sudo cp -f "$TARGET_DIR/$DOMAIN.crt" "$BACKUP_DIR/$(date +%Y%m%d)_$DOMAIN.crt"
echo -e "\n证书备份完成:$BACKUP_DIR/$(date +%Y%m%d)_$DOMAIN.*"
#!/bin/bash
# ============================================
# certbot SSL证书管理脚本
# 功能:申请、检测、更新和部署SSL证书
# 自动安装所需模块和依赖
# ============================================
# 配置常量
CERT_CHECK_INTERVAL="7" # 检查间隔(天)
CERT_RENEW_THRESHOLD="10" # 续期阈值(天)
CRON_SCHEDULE="0 0 * * 0" # 每周日0点执行
# 颜色输出函数
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
print_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
print_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
print_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# 检查是否以root运行
check_root() {
if [ "$EUID" -ne 0 ]; then
print_error "请使用sudo或以root用户运行此脚本"
exit 1
fi
}
# 检测系统类型
detect_os() {
if [ -f /etc/os-release ]; then
. /etc/os-release
OS=$ID
VERSION=$VERSION_ID
elif [ -f /etc/redhat-release ]; then
OS="rhel"
elif [ -f /etc/debian_version ]; then
OS="debian"
else
OS=$(uname -s | tr '[:upper:]' '[:lower:]')
fi
echo $OS
}
# 安装certbot基础包
install_certbot_base() {
local os=$(detect_os)
print_info "检测到系统: $os"
print_info "正在安装certbot基础包..."
case $os in
ubuntu|debian)
apt-get update
apt-get install -y certbot
;;
centos|rhel|fedora)
if command -v dnf &> /dev/null; then
dnf install -y certbot
else
yum install -y epel-release
yum install -y certbot
fi
;;
arch)
pacman -Syu --noconfirm certbot
;;
*)
print_error "不支持的操作系统,请手动安装certbot"
print_info "参考: https://certbot.eff.org/instructions"
exit 1
;;
esac
if command -v certbot &> /dev/null; then
print_success "certbot基础包安装成功"
return 0
else
print_error "certbot安装失败"
return 1
fi
}
# 安装Web服务器插件
install_web_server_plugin() {
local web_server=$1
local os=$(detect_os)
case $web_server in
nginx)
print_info "安装certbot-nginx插件..."
case $os in
ubuntu|debian)
apt-get install -y python3-certbot-nginx
;;
centos|rhel|fedora)
if command -v dnf &> /dev/null; then
dnf install -y python3-certbot-nginx
else
yum install -y python3-certbot-nginx
fi
;;
arch)
pacman -Syu --noconfirm certbot-nginx
;;
esac
;;
apache)
print_info "安装certbot-apache插件..."
case $os in
ubuntu|debian)
apt-get install -y python3-certbot-apache
;;
centos|rhel|fedora)
if command -v dnf &> /dev/null; then
dnf install -y python3-certbot-apache
else
yum install -y python3-certbot-apache
fi
;;
arch)
pacman -Syu --noconfirm certbot-apache
;;
esac
;;
esac
}
# 安装DNS插件
install_dns_plugin() {
local dns_provider=$1
local os=$(detect_os)
print_info "安装certbot DNS插件: $dns_provider"
case $dns_provider in
cloudflare)
case $os in
ubuntu|debian)
apt-get install -y python3-certbot-dns-cloudflare
;;
centos|rhel|fedora)
if command -v dnf &> /dev/null; then
dnf install -y python3-certbot-dns-cloudflare
else
yum install -y python3-certbot-dns-cloudflare
fi
;;
arch)
pacman -Syu --noconfirm certbot-dns-cloudflare
;;
esac
# 配置Cloudflare API凭证
configure_cloudflare_credentials
;;
aliyun|alidns)
case $os in
ubuntu|debian)
apt-get install -y python3-certbot-dns-aliyun
;;
centos|rhel|fedora)
if command -v dnf &> /dev/null; then
dnf install -y python3-certbot-dns-aliyun
else
# 可能需要从pip安装
pip3 install certbot-dns-aliyun
fi
;;
arch)
pacman -Syu --noconfirm certbot-dns-aliyun || pip3 install certbot-dns-aliyun
;;
esac
# 配置阿里云API凭证
configure_aliyun_credentials
;;
dnspod)
# DNSPod插件通常需要从pip安装
print_info "通过pip安装certbot-dns-dnspod"
pip3 install certbot-dns-dnspod
configure_dnspod_credentials
;;
route53)
case $os in
ubuntu|debian)
apt-get install -y python3-certbot-dns-route53
;;
centos|rhel|fedora)
if command -v dnf &> /dev/null; then
dnf install -y python3-certbot-dns-route53
else
yum install -y python3-certbot-dns-route53
fi
;;
arch)
pacman -Syu --noconfirm certbot-dns-route53
;;
esac
configure_route53_credentials
;;
godaddy)
# GoDaddy插件通常需要从pip安装
pip3 install certbot-dns-godaddy
configure_godaddy_credentials
;;
esac
}
# 配置Cloudflare API凭证
configure_cloudflare_credentials() {
print_info "配置Cloudflare API凭证"
# 创建凭证目录
mkdir -p /etc/letsencrypt
mkdir -p ~/.secrets/certbot
# 询问Cloudflare API凭证
read -p "请输入Cloudflare邮箱: " cf_email
read -p "请输入Cloudflare Global API Key: " cf_api_key
# 创建凭证文件
cat > ~/.secrets/certbot/cloudflare.ini <<EOF
# Cloudflare API credentials used by Certbot
dns_cloudflare_email = ${cf_email}
dns_cloudflare_api_key = ${cf_api_key}
EOF
# 设置权限
chmod 600 ~/.secrets/certbot/cloudflare.ini
print_success "Cloudflare凭证已保存到 ~/.secrets/certbot/cloudflare.ini"
}
# 配置阿里云API凭证
configure_aliyun_credentials() {
print_info "配置阿里云API凭证"
mkdir -p ~/.secrets/certbot
read -p "请输入阿里云AccessKey ID: " ali_key
read -p "请输入阿里云AccessKey Secret: " ali_secret
cat > ~/.secrets/certbot/aliyun.ini <<EOF
# Aliyun DNS API credentials
dns_aliyun_access_key = ${ali_key}
dns_aliyun_access_key_secret = ${ali_secret}
EOF
chmod 600 ~/.secrets/certbot/aliyun.ini
print_success "阿里云凭证已保存"
}
# 配置DNSPod API凭证
configure_dnspod_credentials() {
print_info "配置DNSPod API凭证"
mkdir -p ~/.secrets/certbot
read -p "请输入DNSPod API ID: " dp_id
read -p "请输入DNSPod API Token: " dp_token
cat > ~/.secrets/certbot/dnspod.ini <<EOF
# DNSPod API credentials
dns_dnspod_api_id = ${dp_id}
dns_dnspod_api_token = ${dp_token}
EOF
chmod 600 ~/.secrets/certbot/dnspod.ini
print_success "DNSPod凭证已保存"
}
# 检查并安装所需模块
install_required_modules() {
local verify_method=$1
local dns_provider=$2
local web_server=$3
# 检查certbot是否已安装
if ! command -v certbot &> /dev/null; then
install_certbot_base
if [ $? -ne 0 ]; then
return 1
fi
fi
# 安装Web服务器插件(如果选择了自动配置)
if [ -n "$web_server" ]; then
install_web_server_plugin "$web_server"
fi
# 安装DNS插件(如果选择了DNS验证)
if [ "$verify_method" == "dns" ] && [ -n "$dns_provider" ]; then
install_dns_plugin "$dns_provider"
fi
return 0
}
# 获取用户输入
get_user_input() {
echo "=================================="
echo " SSL证书管理工具 (certbot) "
echo "=================================="
# 输入域名
while true; do
read -p "请输入主域名: " main_domain
if [ -z "$main_domain" ]; then
print_error "域名不能为空"
elif ! [[ "$main_domain" =~ ^[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z]{2,})+$ ]]; then
print_error "域名格式无效"
else
break
fi
done
read -p "请输入其他域名 (用空格分隔,直接回车跳过): " other_domains_input
# 构建域名列表
DOMAINS=("$main_domain")
if [ -n "$other_domains_input" ]; then
DOMAINS+=($other_domains_input)
fi
# 选择验证方式
echo ""
echo "请选择验证方式:"
echo "1) Webroot验证 (需要网站可访问)"
echo "2) Standalone验证 (临时占用80/443端口)"
echo "3) DNS验证 (自动API验证,推荐)"
while true; do
read -p "请选择 [1-3]: " verify_method
case $verify_method in
1)
VERIFY_METHOD="webroot"
read -p "请输入网站根目录路径 [默认: /var/www/html]: " web_root
WEB_ROOT=${web_root:-/var/www/html}
# 检查目录是否存在
if [ ! -d "$WEB_ROOT" ]; then
print_warning "目录不存在,将尝试创建: $WEB_ROOT"
mkdir -p "$WEB_ROOT"
fi
break
;;
2)
VERIFY_METHOD="standalone"
print_info "将使用standalone模式验证,请确保80/443端口空闲"
break
;;
3)
VERIFY_METHOD="dns"
echo ""
echo "支持的DNS提供商:"
echo "1) Cloudflare (推荐)"
echo "2) Aliyun (阿里云)"
echo "3) DNSPod"
echo "4) Route53 (AWS)"
echo "5) GoDaddy"
read -p "选择DNS提供商 [1-5]: " dns_provider_num
case $dns_provider_num in
1) DNS_PROVIDER="cloudflare" ;;
2) DNS_PROVIDER="aliyun" ;;
3) DNS_PROVIDER="dnspod" ;;
4) DNS_PROVIDER="route53" ;;
5) DNS_PROVIDER="godaddy" ;;
*) DNS_PROVIDER="cloudflare" ;;
esac
print_info "将安装 ${DNS_PROVIDER} DNS插件..."
break
;;
*)
print_error "无效选择"
;;
esac
done
# 询问证书部署路径
echo ""
read -p "请输入证书部署目录 [默认: /etc/ssl/certs]: " deploy_dir
DEPLOY_DIR=${deploy_dir:-/etc/ssl/certs}
# 创建部署目录
if [ ! -d "$DEPLOY_DIR" ]; then
print_info "创建部署目录: $DEPLOY_DIR"
mkdir -p "$DEPLOY_DIR"
fi
# 询问邮箱(用于通知)
read -p "请输入邮箱地址 (用于证书到期提醒): " email
EMAIL=${email:-admin@$main_domain}
# 询问是否安装到Nginx/Apache
read -p "是否自动配置Web服务器? [y/N]: " config_web
if [[ $config_web =~ ^[Yy]$ ]]; then
# 检测已安装的Web服务器
if command -v nginx &> /dev/null; then
WEB_SERVER="nginx"
print_info "检测到Nginx,将自动配置"
elif command -v apache2 &> /dev/null; then
WEB_SERVER="apache"
print_info "检测到Apache,将自动配置"
else
echo "请选择Web服务器:"
echo "1) Nginx"
echo "2) Apache"
read -p "请选择 [1-2]: " web_server_choice
case $web_server_choice in
1) WEB_SERVER="nginx" ;;
2) WEB_SERVER="apache" ;;
*) WEB_SERVER="nginx" ;;
esac
fi
fi
# 询问是否测试证书申请
read -p "是否使用测试环境(避免达到Let's Encrypt限制)? [Y/n]: " test_cert
if [[ $test_cert =~ ^[Nn]$ ]]; then
STAGING=""
else
STAGING="--test-cert"
print_info "将使用Let's Encrypt测试环境"
fi
}
# 申请证书
issue_certificate() {
print_info "开始申请SSL证书..."
# 构建域名参数
domain_args="-d ${DOMAINS[0]}"
for (( i=1; i<${#DOMAINS[@]}; i++ )); do
domain_args="$domain_args -d ${DOMAINS[$i]}"
done
# 根据验证方式执行不同命令
case $VERIFY_METHOD in
webroot)
print_info "使用Webroot验证方式"
certbot certonly --webroot -w "$WEB_ROOT" \
$domain_args \
--email "$EMAIL" \
--agree-tos \
--non-interactive \
--force-renewal \
$STAGING
;;
standalone)
print_info "使用Standalone验证方式"
# 检查端口占用
if netstat -tuln | grep -q ":80 "; then
print_warning "80端口已被占用,尝试停止相关服务"
# 尝试停止Nginx或Apache
if systemctl is-active --quiet nginx; then
systemctl stop nginx
NGINX_WAS_RUNNING=true
fi
if systemctl is-active --quiet apache2; then
systemctl stop apache2
APACHE_WAS_RUNNING=true
fi
fi
certbot certonly --standalone \
$domain_args \
--email "$EMAIL" \
--agree-tos \
--non-interactive \
--force-renewal \
--preferred-challenges http \
$STAGING
# 恢复服务
if [ "$NGINX_WAS_RUNNING" = true ]; then
systemctl start nginx
fi
if [ "$APACHE_WAS_RUNNING" = true ]; then
systemctl start apache2
fi
;;
dns)
print_info "使用DNS验证方式 (${DNS_PROVIDER})"
# 构建DNS插件参数
dns_plugin_args=""
case $DNS_PROVIDER in
cloudflare)
dns_plugin_args="--dns-cloudflare --dns-cloudflare-credentials ~/.secrets/certbot/cloudflare.ini"
;;
aliyun)
dns_plugin_args="--dns-aliyun --dns-aliyun-credentials ~/.secrets/certbot/aliyun.ini"
;;
dnspod)
dns_plugin_args="--dns-dnspod --dns-dnspod-credentials ~/.secrets/certbot/dnspod.ini"
;;
route53)
dns_plugin_args="--dns-route53"
;;
godaddy)
dns_plugin_args="--dns-godaddy --dns-godaddy-credentials ~/.secrets/certbot/godaddy.ini"
;;
esac
certbot certonly $dns_plugin_args \
$domain_args \
--email "$EMAIL" \
--agree-tos \
--non-interactive \
--force-renewal \
$STAGING
;;
esac
local exit_code=$?
if [ $exit_code -eq 0 ]; then
print_success "证书申请成功"
return 0
elif [ $exit_code -eq 1 ]; then
print_error "证书申请失败: 输入参数错误"
return 1
elif [ $exit_code -eq 2 ]; then
print_error "证书申请失败: 网络问题"
return 1
else
print_error "证书申请失败 (错误码: $exit_code)"
return 1
fi
}
# 部署证书
deploy_certificate() {
local main_domain="${DOMAINS[0]}"
print_info "部署证书到: $DEPLOY_DIR"
# certbot证书路径
if [ -n "$STAGING" ]; then
CERT_SOURCE="/etc/letsencrypt/staging/live/$main_domain"
else
CERT_SOURCE="/etc/letsencrypt/live/$main_domain"
fi
if [ ! -d "$CERT_SOURCE" ]; then
print_error "证书目录不存在: $CERT_SOURCE"
print_info "请检查证书申请是否成功"
return 1
fi
# 创建部署目录
CERT_DEPLOY_DIR="$DEPLOY_DIR/$main_domain"
mkdir -p "$CERT_DEPLOY_DIR"
# 复制证书文件(使用符号链接或实际文件)
if [ -f "$CERT_SOURCE/fullchain.pem" ]; then
cp -L "$CERT_SOURCE/fullchain.pem" "$CERT_DEPLOY_DIR/" 2>/dev/null || cp "$CERT_SOURCE/fullchain.pem" "$CERT_DEPLOY_DIR/"
cp -L "$CERT_SOURCE/privkey.pem" "$CERT_DEPLOY_DIR/" 2>/dev/null || cp "$CERT_SOURCE/privkey.pem" "$CERT_DEPLOY_DIR/"
cp -L "$CERT_SOURCE/chain.pem" "$CERT_DEPLOY_DIR/" 2>/dev/null || cp "$CERT_SOURCE/chain.pem" "$CERT_DEPLOY_DIR/"
cp -L "$CERT_SOURCE/cert.pem" "$CERT_DEPLOY_DIR/" 2>/dev/null || cp "$CERT_SOURCE/cert.pem" "$CERT_DEPLOY_DIR/"
else
print_error "证书文件不存在"
return 1
fi
# 设置权限
chmod 600 "$CERT_DEPLOY_DIR/privkey.pem"
chmod 644 "$CERT_DEPLOY_DIR/fullchain.pem"
chmod 644 "$CERT_DEPLOY_DIR/chain.pem"
chmod 644 "$CERT_DEPLOY_DIR/cert.pem"
# 创建符号链接(保持最新证书)
ln -sf "$CERT_SOURCE/fullchain.pem" "$CERT_DEPLOY_DIR/latest-fullchain.pem" 2>/dev/null || true
ln -sf "$CERT_SOURCE/privkey.pem" "$CERT_DEPLOY_DIR/latest-privkey.pem" 2>/dev/null || true
print_success "证书已部署到: $CERT_DEPLOY_DIR"
# 创建合并证书(某些应用需要)
cat "$CERT_DEPLOY_DIR/fullchain.pem" "$CERT_DEPLOY_DIR/privkey.pem" > "$CERT_DEPLOY_DIR/combined.pem"
chmod 600 "$CERT_DEPLOY_DIR/combined.pem"
# 配置Web服务器
if [[ $config_web =~ ^[Yy]$ ]]; then
configure_webserver "$main_domain" "$CERT_DEPLOY_DIR"
fi
return 0
}
# 配置Web服务器
configure_webserver() {
local domain="$1"
local cert_path="$2"
case $WEB_SERVER in
nginx)
print_info "配置Nginx SSL证书..."
# 使用certbot自动配置Nginx
if command -v certbot &> /dev/null && [ -z "$STAGING" ]; then
certbot --nginx -d "$domain" --non-interactive --agree-tos --email "$EMAIL" --redirect
if [ $? -eq 0 ]; then
print_success "Nginx SSL配置完成"
return
fi
fi
# 手动配置备用
nginx_conf="/etc/nginx/sites-available/$domain"
if [ -d "/etc/nginx/sites-available" ]; then
# 备份原配置
if [ -f "$nginx_conf" ]; then
cp "$nginx_conf" "${nginx_conf}.backup.$(date +%Y%m%d_%H%M%S)"
fi
tee "$nginx_conf" > /dev/null <<EOF
server {
listen 80;
server_name $domain;
return 301 https://\$server_name\$request_uri;
}
server {
listen 443 ssl http2;
server_name $domain;
ssl_certificate $cert_path/fullchain.pem;
ssl_certificate_key $cert_path/privkey.pem;
ssl_trusted_certificate $cert_path/chain.pem;
# SSL优化
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
# HSTS
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
location / {
root /var/www/html;
index index.html;
}
}
EOF
if [ -f "$nginx_conf" ]; then
ln -sf "$nginx_conf" "/etc/nginx/sites-enabled/" 2>/dev/null || true
print_info "Nginx配置已创建,请重启Nginx生效"
fi
fi
;;
apache)
print_info "配置Apache SSL证书..."
# 使用certbot自动配置Apache
if command -v certbot &> /dev/null && [ -z "$STAGING" ]; then
certbot --apache -d "$domain" --non-interactive --agree-tos --email "$EMAIL" --redirect
if [ $? -eq 0 ]; then
print_success "Apache SSL配置完成"
return
fi
fi
# 手动配置备用
apache_conf="/etc/apache2/sites-available/$domain.conf"
if [ -d "/etc/apache2/sites-available" ]; then
tee "$apache_conf" > /dev/null <<EOF
<VirtualHost *:80>
ServerName $domain
Redirect permanent / https://$domain/
</VirtualHost>
<VirtualHost *:443>
ServerName $domain
SSLEngine on
SSLCertificateFile $cert_path/fullchain.pem
SSLCertificateKeyFile $cert_path/privkey.pem
SSLCertificateChainFile $cert_path/chain.pem
# SSL优化
SSLProtocol all -SSLv2 -SSLv3 -TLSv1 -TLSv1.1
SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256
# HSTS
Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
DocumentRoot /var/www/html
<Directory /var/www/html>
Options Indexes FollowSymLinks
AllowOverride All
Require all granted
</Directory>
</VirtualHost>
EOF
if [ -f "$apache_conf" ]; then
a2ensite "$domain.conf" > /dev/null 2>&1
a2enmod ssl > /dev/null 2>&1
a2enmod headers > /dev/null 2>&1
print_info "Apache配置已创建,请重启Apache生效"
fi
fi
;;
esac
}
# 检查证书有效期
check_cert_expiry() {
local main_domain="${DOMAINS[0]}"
# 确定证书路径
if [ -n "$STAGING" ]; then
cert_file="/etc/letsencrypt/staging/live/$main_domain/fullchain.pem"
else
cert_file="/etc/letsencrypt/live/$main_domain/fullchain.pem"
fi
if [ ! -f "$cert_file" ]; then
print_error "证书文件不存在: $cert_file"
return 1
fi
# 使用openssl检查证书过期时间
expiry_date=$(openssl x509 -enddate -noout -in "$cert_file" 2>/dev/null | cut -d= -f2)
if [ -z "$expiry_date" ]; then
print_error "无法读取证书信息"
return 1
fi
# 转换为时间戳(处理不同系统date命令的差异)
if date --version 2>/dev/null | grep -q GNU; then
# GNU date (Linux)
expiry_epoch=$(date -d "$expiry_date" +%s 2>/dev/null)
else
# BSD date (macOS)
expiry_epoch=$(date -j -f "%b %d %H:%M:%S %Y" "$expiry_date" +%s 2>/dev/null)
fi
if [ -z "$expiry_epoch" ]; then
print_error "无法解析证书过期时间"
return 1
fi
current_epoch=$(date +%s)
# 计算剩余天数
days_remaining=$(( ($expiry_epoch - $current_epoch) / 86400 ))
echo "=================================="
echo "证书有效期检查:"
echo "域名: $main_domain"
echo "过期时间: $expiry_date"
echo "剩余天数: $days_remaining 天"
if [ $days_remaining -le $CERT_RENEW_THRESHOLD ]; then
print_warning "证书即将过期 (少于 ${CERT_RENEW_THRESHOLD} 天)"
return 1
elif [ $days_remaining -le 30 ]; then
print_warning "证书将在30天内过期"
return 0
else
print_success "证书有效期正常"
return 0
fi
}
# 更新证书
renew_certificate() {
print_info "开始更新证书..."
# 使用certbot renew命令
certbot renew --quiet --post-hook "systemctl reload nginx apache2 2>/dev/null || true"
if [ $? -eq 0 ]; then
print_success "证书更新成功"
# 重新部署证书
deploy_certificate
# 重启Web服务器
if [ -n "$WEB_SERVER" ]; then
case $WEB_SERVER in
nginx)
systemctl reload nginx 2>/dev/null || systemctl restart nginx 2>/dev/null || true
;;
apache)
systemctl reload apache2 2>/dev/null || systemctl restart apache2 2>/dev/null || true
;;
esac
fi
return 0
else
print_error "证书更新失败"
return 1
fi
}
# 设置定时任务
setup_cron_job() {
local script_path="$(realpath "$0")"
local cron_cmd="$script_path --cron"
# 添加定时任务
(crontab -l 2>/dev/null | grep -v "$script_path"; echo "$CRON_SCHEDULE $cron_cmd") | crontab -
if [ $? -eq 0 ]; then
print_success "定时任务设置成功 (计划: $CRON_SCHEDULE)"
# 也添加certbot的自动更新
(crontab -l 2>/dev/null | grep -v "certbot renew"; echo "0 3 * * * certbot renew --quiet --post-hook 'systemctl reload nginx apache2 2>/dev/null || true' 2>/dev/null") | crontab -
print_info "已设置certbot自动更新 (每天3点执行)"
else
print_error "定时任务设置失败"
fi
}
# 显示证书信息
show_certificate_info() {
local main_domain="${DOMAINS[0]}"
echo ""
echo "=================================="
echo " 证书信息 "
echo "=================================="
# 使用certbot certificates命令
if certbot certificates 2>/dev/null | grep -q "$main_domain"; then
certbot certificates 2>/dev/null | grep -A 10 "$main_domain"
else
print_warning "未找到证书信息,尝试直接读取文件"
if [ -n "$STAGING" ]; then
cert_dir="/etc/letsencrypt/staging/live/$main_domain"
else
cert_dir="/etc/letsencrypt/live/$main_domain"
fi
if [ -d "$cert_dir" ]; then
echo "证书位置: $cert_dir"
ls -la "$cert_dir/"
fi
fi
}
# 主函数
main() {
# 检查参数
if [ "$1" == "--cron" ]; then
# 定时任务模式
print_info "执行定时证书检查..."
check_cert_expiry
if [ $? -eq 1 ]; then
renew_certificate
fi
exit 0
elif [ "$1" == "--renew" ]; then
# 手动更新模式
renew_certificate
exit $?
elif [ "$1" == "--check" ]; then
# 只检查模式
check_cert_expiry
exit $?
fi
# 交互模式
check_root
get_user_input
# 安装所需模块
print_info "检查并安装所需模块..."
install_required_modules "$VERIFY_METHOD" "$DNS_PROVIDER" "$WEB_SERVER"
if [ $? -ne 0 ]; then
print_error "模块安装失败"
exit 1
fi
# 申请证书
issue_certificate
if [ $? -eq 0 ]; then
# 部署证书
deploy_certificate
# 检查有效期
check_cert_expiry
# 显示证书信息
show_certificate_info
# 设置定时任务
echo ""
read -p "是否设置自动更新定时任务? [Y/n]: " setup_cron
if [[ ! $setup_cron =~ ^[Nn]$ ]]; then
setup_cron_job
fi
# 生成报告
generate_report
else
print_error "证书申请过程失败"
exit 1
fi
}
# 生成报告
generate_report() {
local main_domain="${DOMAINS[0]}"
echo ""
echo "=================================="
echo " SSL证书部署报告 "
echo "=================================="
echo "主域名: $main_domain"
echo "所有域名: ${DOMAINS[*]}"
if [ -n "$STAGING" ]; then
echo "证书位置: /etc/letsencrypt/staging/live/$main_domain/"
echo "模式: 测试环境 (STAGING)"
else
echo "证书位置: /etc/letsencrypt/live/$main_domain/"
echo "模式: 生产环境"
fi
echo "部署位置: $DEPLOY_DIR/$main_domain/"
echo "验证方式: $VERIFY_METHOD"
if [ "$VERIFY_METHOD" == "webroot" ]; then
echo "网站目录: $WEB_ROOT"
elif [ "$VERIFY_METHOD" == "dns" ]; then
echo "DNS提供商: $DNS_PROVIDER"
fi
echo "通知邮箱: $EMAIL"
if [ -n "$WEB_SERVER" ]; then
echo "Web服务器: $WEB_SERVER"
fi
echo ""
echo "管理命令:"
echo " - 手动更新: $0 --renew"
echo " - 检查证书: $0 --check"
echo " - 查看证书: certbot certificates"
echo " - 撤销证书: certbot revoke --cert-name $main_domain"
echo ""
echo "证书文件:"
echo " - fullchain.pem: 完整证书链"
echo " - privkey.pem: 私钥文件"
echo " - chain.pem: 中间证书"
echo " - cert.pem: 证书文件"
echo " - combined.pem: 合并证书(证书+私钥)"
echo ""
echo "自动更新:"
echo " - 脚本检查: 每周日0点 (少于${CERT_RENEW_THRESHOLD}天自动更新)"
echo " - certbot自动更新: 每天3点"
echo "=================================="
}
# 脚本异常处理
trap 'print_error "脚本执行被中断"; exit 1' INT TERM
# 执行主函数
main "$@"
sicnature ---------------------------------------------------------------------
I P 地 址: 216.73.216.130
区 域 位 置: 美国加利福尼亚洛杉矶
系 统 信 息:
Original content, please indicate the source:
同福客栈论坛 | 蟒蛇科普 | 海南乡情论坛 | JiaYu Blog
sicnature ---------------------------------------------------------------------




没有评论