bash - 在共享主机上自动生成和更新 SSL
问题描述
我正在尝试自动生成 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 中的某些东西会很棒,因为我对此更熟悉。
解决方案
shell 通过在空格上拆分将每一行解析为标记。[[
内置 with的语法=~
要求每一侧都有一个标记。您可以通过在每个不是标记分隔符的空白字符前面放置反斜杠或引用应该是单个标记的序列来防止在空格上拆分。
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
的%c
格式说明符date
包括年份,原始代码省略了它。我认为这个改变是一个特性而不是一个错误。
仍然有很多硬编码路径等可能应该更好地参数化。
来自的 stderr 输出openssl
足够适中,我认为我们绝对不需要丢弃它;另一方面,将其转储到临时文件中几乎肯定会在实际出现问题时隐藏有用的诊断信息(网络关闭或其他)。
tripleee$ openssl s_client -servername www.stackoverflow.com \
> -connect www.stackoverflow.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
推荐阅读
- asn.1 - ASN.1 中隐式和显式标签的编码
- r - 将列格式化为 r 中的时间
- mysql - 有什么更好的方法来查找表 Y 中与表 X 的 PK 相关的所有 id 是否都存在于 mysql 的表 Z 中?
- sql - SQL 从一个表更新到另一个 ID 匹配的表
- python - pygame.sprite.groupcollide() 尝试在 pygame 中实现碰撞时不起作用
- c++ - 为什么我的重载函数调用不明确?
- twilio - Whatsapp - 客户解决方案查询
- javascript - 避免浏览器弹出
- rxjs - 什么是 rxjs 等价于 Array.some()?
- javascript - 在 React 中将 setStateof 嵌套对象传递给子对象