Tự động hóa quản lý chứng chỉ SSL trên nhiều máy chủ với acme.sh và Rsync

Việc duy trì chứng chỉ SSL đồng bộ trên hệ thống máy chủ phân tán thường gây ra nhiều phiền toái: quên cập nhật, lỗi cấu hình, hoặc thậm chí downtime do hết hạn. Giải pháp kết hợp acme.shRsync dưới đây sẽ giúp bạn tự động hóa toàn bộ quy trình — từ cấp phát, gia hạn đến đồng bộ và khởi động lại dịch vụ.

Nguyên lý hoạt động

  • acme.sh: Tự động yêu cầu và gia hạn chứng chỉ thông qua giao thức ACME (Let’s Encrypt, ZeroSSL...).
  • Rsync: Đồng bộ tập tin chứng chỉ mới đến các máy chủ đích một cách hiệu quả.
  • Cron + Hook: Kích hoạt đồng bộ ngay sau khi chứng chỉ được làm mới.
  • SSH key: Đảm bảo kết nối an toàn và không cần mật khẩu giữa các máy chủ.

Chuẩn bị môi trường

Đảm bảo tất cả máy chủ đều cài đặt:

# Ubuntu/Debian
sudo apt update && sudo apt install -y rsync openssl

# CentOS/RHEL
sudo yum install -y rsync openssl

Thiết lập SSH key để đăng nhập không cần mật khẩu từ máy chủ chính sang các máy chủ đích:

ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519 -N ""
ssh-copy-id deploy@server-web-01
ssh-copy-id deploy@server-web-02

Cài đặt và cấu hình acme.sh

Trên máy chủ trung tâm (nơi cấp chứng chỉ):

curl https://get.acme.sh | sh -s email=your@email.com
source ~/.bashrc

Ví dụ cấp chứng chỉ cho tên miền myapp.dev bằng phương thức Webroot:

acme.sh --issue -d myapp.dev -w /var/www/html --renew-hook "/home/deploy/scripts/sync-cert.sh"

Script đồng bộ chứng chỉ

Tạo thư mục cấu hình:

mkdir -p ~/scripts/certs

Tạo file cấu hình ~/scripts/certs/config:

CERT_DOMAIN="myapp.dev"
CERT_PATH="$HOME/.acme.sh/$CERT_DOMAIN"
TARGETS=(
    "deploy@server-web-01:/etc/ssl/myapp"
    "deploy@server-web-02:/etc/ssl/myapp"
)
FILES=("fullchain.cer" "$CERT_DOMAIN.key")
SERVICE_CMD="sudo systemctl reload nginx"

Tạo script đồng bộ ~/scripts/sync-cert.sh:

#!/bin/bash

CONFIG="$HOME/scripts/certs/config"
[ ! -f "$CONFIG" ] && { echo "❌ Config not found"; exit 1; }
source "$CONFIG"

for target in "${TARGETS[@]}"; do
    host=$(echo "$target" | cut -d: -f1)
    path=$(echo "$target" | cut -d: -f2-)

    ssh "$host" "mkdir -p $path" >/dev/null

    for cert_file in "${FILES[@]}"; do
        local_file="$CERT_PATH/$cert_file"
        [ ! -f "$local_file" ] && { echo "❌ Missing $cert_file"; continue; }
        rsync -az -e ssh "$local_file" "$host:$path/"
    done

    ssh "$host" "$SERVICE_CMD" >/dev/null && \
        echo "✅ Synced and reloaded on $host" || \
        echo "⚠️  Failed to reload service on $host"
done

Cấp quyền thực thi:

chmod +x ~/scripts/sync-cert.sh

Kiểm tra và tự động hóa

Thử chạy thủ công để kiểm tra:

~/scripts/sync-cert.sh

acme.sh đã tự động thêm hook vào cron, nhưng bạn có thể kiểm tra lại:

crontab -l | grep acme.sh

Giám sát và cảnh báo

Tạo script kiểm tra thời hạn chứng chỉ ~/scripts/check-expiry.sh:

#!/bin/bash

CERT_FILE="$HOME/.acme.sh/myapp.dev/fullchain.cer"
[ ! -f "$CERT_FILE" ] && exit 1

DAYS_LEFT=$(openssl x509 -checkend $((30*86400)) -noout -in "$CERT_FILE" 2>&1 | grep -c "not after")

if [ "$DAYS_LEFT" -eq 0 ]; then
    echo "🚨 Certificate expires in less than 30 days!"
    # Gửi cảnh báo qua mail, Slack, Telegram...
fi

Lập lịch kiểm tra hàng ngày:

crontab -e
# Thêm dòng:
0 8 * * * /home/deploy/scripts/check-expiry.sh

Xử lý tình huống nâng cao

  • SSH cổng khác: Thêm cổng vào định dạng user@host:port:path và chỉnh sửa script dùng ssh -p PORT.
  • Qua jump server: Dùng tùy chọn -e "ssh -J jump-user@jump-host" trong lệnh rsync.
  • Nhiều domain: Viết vòng lặp đọc danh sách domain từ file hoặc dùng mảng cấu hình riêng.

Gỡ lỗi thường gặp

  • Không tìm thấy chứng chỉ: Kiểm tra đường dẫn và tên domain trong cấu hình.
  • Không reload được service: Cấp quyền sudo không cần mật khẩu cho user deploy: deploy ALL=(ALL) NOPASSWD: /bin/systemctl reload nginx
  • Gia hạn thất bại: Xem log tại ~/.acme.sh/acme.sh.log, kiểm tra quyền truy cập webroot hoặc DNS.

Thẻ: acme.sh rsync ssl-certificate letsencrypt nginx

Đăng vào ngày 25 tháng 7 lúc 07:30