-
-
Save anjia0532/9ebf8011322f43e3f5037bc2af3aeaa6 to your computer and use it in GitHub Desktop.
auto update apisix ssl by acme.sh
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
# author anjia0532@gmail.com | |
# blog https://anjia0532.github.io/ | |
# github https://github.com/anjia0532 | |
# this script depend on jq,check it first | |
RED='\033[0;31m' | |
NC='\033[0m' # No Color | |
if ! [ -x "$(command -v jq)" ]; then | |
echo -e "${RED}Error: jq is not installed.${NC}" >&2 | |
exit 1 | |
fi | |
if ! [ -x "$(command -v openssl)" ]; then | |
echo -e "${RED}Error: openssl is not installed.${NC}" >&2 | |
exit 1 | |
fi | |
if ! [ -x "$(command -v ~/.acme.sh/acme.sh)" ]; then | |
echo -e "${RED}Error: acme.sh is not installed.(doc https://github.com/acmesh-official/acme.sh/wiki/How-to-install)${NC}" >&2 | |
exit 1 | |
fi | |
usage () { echo "Usage : $0 -h <apisix admin host> -p <certificate pem file> -k <certificate private key file> -a <admin api key> -t <print debug info switch off/on,default off>"; } | |
# parse args | |
while getopts "h:p:k:a:t:" opts; do | |
case ${opts} in | |
h) HOST=${OPTARG} ;; | |
p) PEM=${OPTARG} ;; | |
k) KEY=${OPTARG} ;; | |
a) API_KEY=${OPTARG} ;; | |
t) DEBUG=${OPTARG} ;; | |
*) usage; exit;; | |
esac | |
done | |
# those args must be not null | |
if [ ! "$HOST" ] || [ ! "$PEM" ] || [ ! "$KEY" ] || [ ! "$API_KEY" ] | |
then | |
usage | |
exit 1 | |
fi | |
# optional args,set default value | |
[ -z "$DEBUG" ] && DEBUG=off | |
# print vars key and value when DEBUG eq on | |
[[ "on" == "$DEBUG" ]] && echo -e "HOST:${HOST} API_KEY:${API_KEY} PEM FILE:${PEM} KEY FILE:${KEY} DEBUG:${DEBUG}" | |
# get all ssl and filter this one by sni name | |
cert_content=$(curl --silent --location --request GET "${HOST}/apisix/admin/ssl/" \ | |
--header "X-API-KEY: ${API_KEY}" \ | |
--header 'Content-Type: application/json' | jq "first(.node.nodes[]| select(.value.snis[] | contains(\"$(openssl x509 -in $PEM -noout -text|grep -oP '(?<=DNS:|IP Address:)[^,]+'|sort|head -n1)\")))") | |
validity_start=$(date --date="$(openssl x509 -startdate -noout -in $PEM|cut -d= -f 2)" +"%s") | |
validity_end=$(date --date="$(openssl x509 -enddate -noout -in $PEM|cut -d= -f 2)" +"%s") | |
# create a new ssl when it not exist | |
if [ -z "$cert_content" ] | |
then | |
cert_content="{\"snis\":[],\"status\": 1}" | |
# read domains from pem file by openssl | |
snis=$(openssl x509 -in $PEM -noout -text|grep -oP '(?<=DNS:|IP Address:)[^,]+'|sort) | |
for sni in ${snis[@]} ; do | |
cert_content=$(echo $cert_content | jq ".snis += [\"$sni\"]") | |
done | |
cert_content=$(echo $cert_content | jq ".|.cert = \"$(cat $PEM)\"|.key = \"$(cat $KEY)\"|.validity_start=${validity_start}|.validity_end=${validity_end}") | |
cert_update_result=$(curl --silent --location --request POST "${HOST}/apisix/admin/ssl/" \ | |
--header "X-API-KEY: ${API_KEY}" \ | |
--header 'Content-Type: application/json' \ | |
--data "$cert_content" ) | |
[[ "on" == "$DEBUG" ]] && echo -e "cert_content: \n${cert_content}\n\ncreate result json:\n\n${cert_update_result}" | |
else | |
# get exist ssl id | |
URI=$(echo $cert_content | jq -r ".key") | |
ID=$(echo ${URI##*/}) | |
# get exist ssl certificate json , modify cert and key value | |
cert_content=$(echo $cert_content | jq ".value|.cert = \"$(cat $PEM)\"|.key = \"$(cat $KEY)\"|.id=\"${ID}\"|.update_time=$(date +'%s')|.validity_start=${validity_start}|.validity_end=${validity_end}") | |
# update apisix ssl | |
cert_update_result=$(curl --silent --location --request PUT "${HOST}/apisix/admin/ssl/${ID}" \ | |
--header "X-API-KEY: ${API_KEY}" \ | |
--header 'Content-Type: application/json' \ | |
--data "$cert_content" ) | |
[[ "on" == "$DEBUG" ]] && echo -e "cert_content: \n${cert_content}\n\nupdate result json:\n\n${cert_update_result}" | |
fi | |
exit 0 |
在 docker的neilpang/acme.sh中使用出现问题 neilpang/acme.sh 是基于Alpine Linux打包的
问题一 grep无法使用-P
grep -oP '(?<=DNS:|IP Address:)[^,]+'
修改为
grep -oE '(DNS:|IP Address:)[^,]+' | sed -E 's/DNS:|IP Address://g'
问题二 71行的for无法直接读取字符串变量
修改为使用临时文件的方式
cert_content="{\"snis\":[],\"status\": 1}"
# read domains from pem file by openssl
tmp_file_path="/tmp/`cat /proc/sys/kernel/random/uuid`"
openssl x509 -in $PEM -noout -text|grep -oE '(DNS:|IP Address:)[^,]+' | sed -E 's/DNS:|IP Address://g' | sort > $tmp_file_path
while read -r sni ; do
cert_content=$(echo $cert_content | jq ".snis += [\"$sni\"]")
done < $tmp_file_path
rm -f $tmp_file_path
cert_content=$(echo $cert_content | jq ".|.cert = \"$(cat $PEM)\"|.key = \"$(cat $KEY)\"|.validity_start=${validity_start}|.validity_end=${validity_end}")
cert_update_result=$(curl --silent --location --request POST "${HOST}/apisix/admin/ssl/" \
--header "X-API-KEY: ${API_KEY}" \
--header 'Content-Type: application/json' \
--data "$cert_content" )
[[ "on" == "$DEBUG" ]] && echo -e "cert_content: \n${cert_content}\n\ncreate result json:\n\n${cert_update_result}"
我这个脚本是在Ubuntu写的,测试的。Alpine因为自身的gcc或者glibc一类的编译器有问题,经常会导致一些诡异问题(比如 jdk 8某个小版本之后明确不再发布 alpine 版本)。如果不是对容器镜像大小有强迫症的追求,可以试试用 -slim 镜像,兼顾体积和兼容性。(PS我自己用golang写了个基于 acme.sh 的 hook 更新阿里云(证书中心,cdn,dcdn,slb,oss,waf),网关(apisix,kong),腾讯云(ssl,clb,cdn,waf),k8s ingress secret 的小程序,目前没有开源计划,用bash脚本不利于阅读和维护)
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
我的脚本,支持检索已有ssl列表是否存在该证书,如果存在则更新,不存在则创建,snis使用OpenSSL读的pem,有效起止时间也是用OpenSSL读的pem
详细步骤,参考我的博客 https://juejin.cn/post/6965778290619449351