在共享主机上自动生成和更新 SSL

Posted

技术标签:

【中文标题】在共享主机上自动生成和更新 SSL【英文标题】:Auto Generate and Update SSL on Shared Hosting 【发布时间】:2021-02-03 23:33:25 【问题描述】:

我正在尝试自动生成 SSL 证书并将它们上传到我在 namecheap.com 上的共享软管

主机不提供任何无需支付大量费用即可自动管理 ssl 证书的方法。

我正在尝试让这个脚本工作

https://catelin.net/2018/03/24/fully-automate-ssl-tls-certificate-renewal-with-cpanel/

这个想法是在你自己的 linux 机器上运行 bash 脚本,它将通过 cpanel 更新 ssl 证书。

我在这段代码中遇到了困难。在我的实际代码中,我更新了服务器名称信息。主要问题是我几乎没有使用 bash 脚本编写的经验。

certificate=$(echo |openssl s_client -servername yourserver.com -connect yourserver.com:443 2>/tmp/cert.tmp|openssl x509 -checkend $[86400 * $RENEW] -enddate)
if [ "$certificate" == "" ]; then
  echo "Error: unable to check certificate"
else
  if [[ $certificate =~ (.*)Certificate will expire ]]; then
    echo $certificate
    ...

我在这里遇到了一个错误(我确信这是我的第一个错误......)

./certupdate.sh: line 19: syntax error in conditional expression
./certupdate.sh: line 19: syntax error near `will'
./certupdate.sh: line 19: `    if [[ $certificate =~ (.*)Certificate will expire ]]; then'

任何帮助都会很棒。

或者,如果有人对如何更新 ssl 证书有更好的想法,那就更好了。 php 中的某些东西会很棒,因为我对此更熟悉。

【问题讨论】:

【参考方案1】:

shell 通过在空格上拆分将每一行解析为标记。带有=~ 的内置[[ 的语法要求每一侧都有一个标记。您可以通过在每个不是标记分隔符的空白字符前面放置反斜杠或引用应该是单个标记的序列来防止在空格上拆分。

  if [[ $certificate =~ (.*)"Certificate will expire" ]]; then

除此之外,您在这里真的不需要正则表达式。 (如果你确实使用了一个,.* 周围的括号是多余的。实际上整个 .* 是多余的。)

  if [[ $certificate = *"Certificate will expire"* ]]; then

除此之外,您尝试复制的脚本还有许多其他问题,尽管问题更小。可能只是尝试找到一个更好的博客来复制/粘贴。

这是一个快速重构,希望能让脚本更惯用,但我可能遗漏了一些问题,并且没有任何方法来测试它。

#!/bin/bash

# Don't use uppercase for private variables
renew=22

# For diagnostic messages
me=$0##*/

# Parametrize domain name
dom="yourserver.com"
email="email@example.org"

# Don't echo so much junk
# (I will silently drop the other junk output without comment)
# echo "======================================="
# Fix date formatting, print diagnostic to stderr
date "+$me: %c START" >&2
# Use a unique temp file to avoid symlink attacks and concurrency problems
t=$(mktemp -t letsencryptCA.XXXXXXXXXX.crt) || exit
# Clean it up when we are done
trap 'rm -f "$t"' ERR EXIT
wget -O "$t" https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem.txt

# Avoid obsolete $[ ... ] math syntax
# Avoid superfluous echo |
# Don't write stderr to a junk file
# Maybe add 2>/dev/null on the first openssl if this is too noisy
certificate=$(openssl s_client -servername "$dom" -connect "$dom":443 </dev/null | 
              openssl x509 -checkend $((86400 * $renew)) -enddate)
# Indentation fixes
if [ "$certificate" == "" ]; then
    # Error messages indicate script's name, and go to standard error
    # Include domain name in error message
    # Fixed throughout below
    echo "$me: Error: unable to check $dom certificate" >&2
    # Exit with an error, too
    exit 123
else
    # Quote string
    # Move this outside the conditional, to avoid repeated code
    echo "$certificate"

    # Fix comparison
    if [[ $certificate = *"Certificate will expire"* ]]; then

        echo "$me: $dom certificate needs to be renewed" >&2
    
        # No idea here, assume this is okay
        # Wrap horribly long command though
        certbot certonly --non-interactive --staple-ocsp \
            --email "$email" -d "$dom" -d "www.$dom" \
            --agree-tos --manual \
            --manual-auth-hook /path/toyour/scripts/letsencryptauth.sh \
            --manual-cleanup-hook /path/toyour/scripts/letsencryptclean.sh
        echo "$me: $dom cert process completed, now uploading it to CPanel" >&2
    
        # Weird indentation fixed again
        USER='cpanel username' PASS='cpanelpassword' EMAIL="$email" \
        /usr/bin/php /path/toyour/scripts/sslic.php "$dom" \
            /etc/letsencrypt/live/"$dom"/cert.pem \
            /etc/letsencrypt/live/"$dom"/privkey.pem "$t"
        echo "$me: $dom upload to cpanel process complete" >&2
    
    else
        echo "$me: $dom cert does not need to be renewed" >&2
    fi
fi
# Fix date formatting, print diagnostic to stderr
date "+$me: %c END" >&2

date%c 格式说明符包括年份,原始代码省略了它。我认为此更改是一项功能而不是错误。

还有很多硬编码路径等可能应该更好地参数化。

openssl 的 stderr 输出足够适中,我认为我们绝对不需要丢弃它;另一方面,将其转储到临时文件中几乎肯定会在实际出现问题(网络故障或其他原因)时隐藏有用的诊断信息。

tripleee$ openssl s_client -servername www.***.com \
>   -connect www.***.com:443 </dev/null >/dev/null
depth=2 O = Digital Signature Trust Co., CN = DST Root CA X3
verify return:1
depth=1 C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
verify return:1
depth=0 CN = *.stackexchange.com
verify return:1

【讨论】:

以上是关于在共享主机上自动生成和更新 SSL的主要内容,如果未能解决你的问题,请参考以下文章

使用 certbot 自动更新 ssl 证书

.htaccess 使用共享 ssl 证书

在服务于不同域的 apache 虚拟主机之间共享 SSL 配置

nginx实现虚拟主机ssl加密,注意此方法只能在公司内部使用

SSL虚拟主机

所有 Nginx 虚拟主机都可以共享相同的 ssl_session_cache 吗?