Init commit

This commit is contained in:
Zhong Lufan 2019-12-31 18:44:21 +08:00
commit 40a1679bda
No known key found for this signature in database
GPG Key ID: 30191289A9BD0B15
6 changed files with 280 additions and 0 deletions

12
.editorconfig Normal file
View File

@ -0,0 +1,12 @@
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
trim_trailing_whitespace = false

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
.vscode/
.idea/
.DS_Store

87
AliyunOpenAPI.sh Normal file
View File

@ -0,0 +1,87 @@
#!/usr/bin/env bash
for c in openssl curl python3; do
if ! command -v ${c} > /dev/null; then
echo "${c}: command not found"
exit 127
fi
done
_AliAccessKeyId=$(printenv AliAccessKeyId)
_AliAccessKeySecret=$(printenv AliAccessKeySecret)
_Format=JSON
_SignatureMethod=HMAC-SHA1
_SignatureVersion=1.0
_urlencode_pycode="from sys import stdin;from urllib.parse import quote;print(quote(stdin.read(), '-_.~'))"
# aliapi_rpc <host> <http_method> <api_version> <api_action> <api_custom_key[]> <api_custom_value[]>
aliapi_rpc() {
[[ $# -lt 6 ]] && return 66
# 公共查询参数键
local _ali_common_key=(
"Format"
"AccessKeyId"
"SignatureMethod"
"Timestamp"
"SignatureVersion"
"SignatureNonce"
"Version"
"Action"
)
# 公共查询参数值
local _ali_common_value=(
"$_Format"
"$_AliAccessKeyId"
"$_SignatureMethod"
"$(_ali_sign_timestamp)"
"$_SignatureVersion"
"$(_ali_sign_nonce)"
"$3"
"$4"
)
local _ali_custom_key=() _ali_custom_value=() # 自定义查询参数键值
read -r -a _ali_custom_key <<< "$5"
read -r -a _ali_custom_value <<< "$6"
local _ali_key=() _ali_value=() # 合并查询键值
read -r -a _ali_key <<< "${_ali_common_key[*]} ${_ali_custom_key[*]}"
read -r -a _ali_value <<< "${_ali_common_value[*]} ${_ali_custom_value[*]}"
local _http_host=$1 _http_method=$2
local _query_str=""
local _key _value
for (( i = 0; i < ${#_ali_key[@]}; ++i )); do
_key=${_ali_key[$i]}
_value=${_ali_value[$i]}
[[ $(grep -E "^.+\(\)$" <<< "$_value") == "$_value" ]] && _value=$(${_value//()/}) # 参数值如果是以 () 结束,代表需要执行命令获取值。
_value=$(_urlencode "$_value")
_query_str+="$_key=$_value&"
done
local _ali_signature
_ali_signature=$(_ali_sign "$_http_method" "$_query_str")
_query_str+="Signature=$(_urlencode "$_ali_signature")"
local _curl_out _result_code _http_url="https://${_http_host}/?${_query_str}"
_curl_out=$(mktemp)
_result_code=$(curl -L -s -X "$_http_method" -o "$_curl_out" --write-out "%{http_code}" "$_http_url" || echo $?)
cat "$_curl_out" && echo
rm -f "$_curl_out"
[[ ${_result_code} -eq 200 ]] && return 0 || return 1
}
_ali_sign() {
local _http_method _str _query_str _sign_str
_http_method=$1
_str=$(echo -n "$2" | tr "&" "\n" | sort)
_query_str=$(echo -n "$_str" | tr "\n" "&")
_sign_str="${_http_method}&$(_urlencode "/")&$(_urlencode "$_query_str")"
echo -n "$_sign_str" | openssl sha1 -hmac "${_AliAccessKeySecret}&" -binary | openssl base64 -e
}
_ali_sign_timestamp() {
TZ="UTC" date "+%FT%TZ"
}
_ali_sign_nonce() {
date "+%s%N"
}
_urlencode() {
echo -n "$1" | python3 -c "$_urlencode_pycode"
}

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019-2020 Zhong Lufan
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

65
README.md Normal file
View File

@ -0,0 +1,65 @@
Aliyun OpenAPI Shell SDK
--
# 介绍
这是一个非官方的阿里云 OpenAPI Shell SDK目的是方便 Shell 脚本直接调用阿里云 OpenAPI主要是实现了自动计算签名。
虽然阿里云官方有 [AliyunCLI](https://github.com/aliyun/aliyun-cli),可以方便的在 Shell 环境下调用阿里云 OpenAPI。不过某些 API (比如 SSL 证书) 它并不支持,所以我就想写一个可能是最好用的 Shell SDK。
这个 SDK 理论上支持所有阿里云 RPC OpenAPIRESTful OpenAPI 暂不支持,因为我暂时没用到,以后可能会考虑支持。
# 依赖
SDK 主要依赖于 `curl`, `openssl`, `python3`
其中 `python3` 用于 `urlencode`,因为纯 Shell 实现的 `urlencode` 可用性较低,索性就直接调用 Python 实现,内部的 Python 代码不兼容 Python2只能运行于 Python3。
# 使用
使用起来非常简单,只需要在你的 Shell 脚本顶部导出`AliAccessKeyId` 和 `AliAccessKeySecret` 环境变量,然后引用 `AliyunOpenAPI.sh` 即可。
```bash
#!/usr/bin/env bash
# 务必使用 export 导出
export AliAccessKeyId="<AliAccessKeyId>" # 此处替换为你的阿里云 AliAccessKeyId
export AliAccessKeySecret="<AliAccessKeySecret>" # 此处替换为你的阿里云 AliAccessKeySecret
. AliyunOpenAPI.sh
# 自定义 GET 参数的键值顺序要一一对应,而且不能包含空格。
# 自定义值支持自定义函数,如果你需要包含空格或者读取文件等操作,可以声明一个自定义函数,然后按照此格式填写:函数名(),就比如下面这样。
# SDK 在处理值的时候会自动执行自定义函数,但是如果自定义函数不存在则会导致获取值失败。
get_show_size() {
echo 50
}
# 自定义 GET 参数的键
ali_custom_key=(
"CurrentPage"
"ShowSize"
)
# 自定义 GET 参数的值
ali_custom_value=(
"1"
"get_show_size()"
)
# 获取阿里云 SSL 证书列表
# aliapi_rpc 的函数签名如下
# aliapi_rpc <host> <http_method> <api_version> <api_action> <api_custom_key[]> <api_custom_value[]>
aliapi_rpc "cas.aliyuncs.com" "GET" "2018-07-13" "DescribeUserCertificateList" "${ali_custom_key[*]}" "${ali_custom_value[*]}"
# 可以通过 $? 是否等于 0 来判断是否执行成功HTTP CODE == 200
# 执行成功返回 JSON 格式的结果,执行失败返回 HTTP CODE 或 curl 的退出代码。
if [[ $? -eq 0 ]]; then
# 执行成功
else
# 执行失败
fi
```
更多使用方法可以参考 `example` 下的示例
如果你有好的示例,欢迎提交 [PR](https://github.com/Hill-98/aliyun-openapi-shell-sdk/pulls)
如果你有问题 / BUG 要反馈,也欢迎提交 [Issue](https://github.com/Hill-98/aliyun-openapi-shell-sdk/issues)

92
example/UpdateSSLCert.sh Normal file
View File

@ -0,0 +1,92 @@
#!/usr/bin/env bash
# 可用于 acme.sh 的 renewHook 脚本,可以自动更新阿里云 SSL 证书并更新对应 CDN 域名,然后删除对应域名旧的证书。
# 每次 API 的执行都会检测是否失败,如果失败,会中断脚本执行并返回自定义错误代码
# acme.sh 导出的环境变量
ENV_NAME=(
"CERT_PATH"
"CERT_KEY_PATH"
"CA_CERT_PATH"
"CERT_FULLCHAIN_PATH"
"Le_Domain"
)
# 检查环境变量是否存在
for value in "${ENV_NAME[@]}" ; do
printenv "$value" > /dev/null || exit 1
done
# 获取证书自定义函数
get_cert() {
sed -e "/^$/d" "$(printenv CERT_FULLCHAIN_PATH)" # 使用 sed 删除掉证书文件的空行
}
# 获取密钥自定义函数
get_key() {
cat "$(printenv CERT_KEY_PATH)"
}
# 导出 AliAccessKeyId 和 AliAccessKeySecret
export AliAccessKeyId="<AliAccessKeyId>"
export AliAccessKeySecret="<AliAccessKeySecret>"
DOMAIN=$(printenv Le_Domain)
CERT_NAME="${DOMAIN}-$(date +%s)" # 证书名称
# 需要更新证书的 CDN 域名列表
DOMAIN_LIST=(
"example.example.com"
)
# shellcheck disable=SC1091
. ../AliyunOpenAPI.sh
ali_custom_name=(
"CurrentPage"
"ShowSize"
)
# 获取第一页的 50 个结果,如果你的证书列表条目较多,可以考虑增加获取数量。
ali_custom_value=(
"1"
"50"
)
# 获取证书列表
result=$(aliapi_rpc "cas.aliyuncs.com" "GET" "2018-07-13" "DescribeUserCertificateList" "${ali_custom_name[*]}" "${ali_custom_value[*]}" || exit 101)
# 使用 jq 处理返回的 JSON 数据并提取出匹配当前证书域名的证书列表的 ID用于稍后的删除旧证书操作。
cert_list=$(echo "$result" | jq -cr ".CertificateList|map(select(.common == \"${DOMAIN}\"))|map(.id)|.[]")
ali_custom_name=(
"Cert"
"Key"
"Name"
)
# 使用自定义函数获取证书和密钥,保证内容可以被安全的传递过去
ali_custom_value=(
"get_cert()"
"get_key()"
"$CERT_NAME"
)
# 上传新的证书
aliapi_rpc "cas.aliyuncs.com" "GET" "2018-07-13" "CreateUserCertificate" "${ali_custom_name[*]}" "${ali_custom_value[*]}" || exit 102
# 设置 CDN 域名列表使用新的证书
for domain in "${DOMAIN_LIST[@]}"; do
ali_custom_name=(
"DomainName"
"ServerCertificateStatus"
"CertName"
"CertType"
)
ali_custom_value=(
"$domain"
"on"
"$CERT_NAME"
"cas"
)
aliapi_rpc "cdn.aliyuncs.com" "GET" "2018-05-10" "SetDomainServerCertificate" "${ali_custom_name[*]}" "${ali_custom_value[*]}" || _exit 103 "Set cdn domain cert fail: $domain"
done
# 删除旧的证书
for id in ${cert_list}; do
ali_custom_name=(
"CertId"
)
ali_custom_value=(
"$id"
)
aliapi_rpc "cas.aliyuncs.com" "GET" "2018-07-13" "DeleteUserCertificate" "${ali_custom_name[*]}" "${ali_custom_value[*]}" || _exit 104 "Delete old cert fail: $id"
done