Merge branch 'master' of https://github.com/Neilpang/acme.sh into ssh-deploy

This commit is contained in:
David Kerr 2017-10-29 21:03:05 -04:00
commit c809b33161
14 changed files with 414 additions and 177 deletions

View File

@ -18,7 +18,7 @@ addons:
install: install:
- if [ "$TRAVIS_OS_NAME" = 'osx' ]; then - if [ "$TRAVIS_OS_NAME" = 'osx' ]; then
brew update && brew install openssl; brew update && brew install openssl socat;
brew info openssl; brew info openssl;
ln -s /usr/local/opt/openssl/lib/libcrypto.1.0.0.dylib /usr/local/lib/; ln -s /usr/local/opt/openssl/lib/libcrypto.1.0.0.dylib /usr/local/lib/;
ln -s /usr/local/opt/openssl/lib/libssl.1.0.0.dylib /usr/local/lib/; ln -s /usr/local/opt/openssl/lib/libssl.1.0.0.dylib /usr/local/lib/;
@ -30,6 +30,7 @@ install:
openssl version 2>&1 || true; openssl version 2>&1 || true;
$ACME_OPENSSL_BIN version 2>&1 || true; $ACME_OPENSSL_BIN version 2>&1 || true;
export PATH="$_old_path"; export PATH="$_old_path";
else sudo apt-get install socat;
fi fi
script: script:

View File

@ -4,7 +4,7 @@ RUN apk update -f \
&& apk --no-cache add -f \ && apk --no-cache add -f \
openssl \ openssl \
curl \ curl \
netcat-openbsd \ socat \
&& rm -rf /var/cache/apk/* && rm -rf /var/cache/apk/*
ENV LE_CONFIG_HOME /acme.sh ENV LE_CONFIG_HOME /acme.sh
@ -16,7 +16,7 @@ ADD ./ /install_acme.sh/
RUN cd /install_acme.sh && ([ -f /install_acme.sh/acme.sh ] && /install_acme.sh/acme.sh --install || curl https://get.acme.sh | sh) && rm -rf /install_acme.sh/ RUN cd /install_acme.sh && ([ -f /install_acme.sh/acme.sh ] && /install_acme.sh/acme.sh --install || curl https://get.acme.sh | sh) && rm -rf /install_acme.sh/
RUN ln -s /root/.acme.sh/acme.sh /usr/local/bin/acme.sh && crontab -l | sed 's#> /dev/null##' | crontab - RUN ln -s /root/.acme.sh/acme.sh /usr/local/bin/acme.sh && crontab -l | grep acme.sh | sed 's#> /dev/null##' | crontab -
RUN for verb in help \ RUN for verb in help \
version \ version \

181
acme.sh
View File

@ -1,6 +1,6 @@
#!/usr/bin/env sh #!/usr/bin/env sh
VER=2.7.3 VER=2.7.4
PROJECT_NAME="acme.sh" PROJECT_NAME="acme.sh"
@ -100,6 +100,10 @@ _PREPARE_LINK="https://github.com/Neilpang/acme.sh/wiki/Install-preparations"
_STATELESS_WIKI="https://github.com/Neilpang/acme.sh/wiki/Stateless-Mode" _STATELESS_WIKI="https://github.com/Neilpang/acme.sh/wiki/Stateless-Mode"
_DNS_MANUAL_ERR="The dns manual mode can not renew automatically, you must issue it again manually. You'd better use the other modes instead."
_DNS_MANUAL_WARN="It seems that you are using dns manual mode. please take care: $_DNS_MANUAL_ERR"
__INTERACTIVE="" __INTERACTIVE=""
if [ -t 1 ]; then if [ -t 1 ]; then
__INTERACTIVE="1" __INTERACTIVE="1"
@ -160,11 +164,11 @@ _dlg_versions() {
echo "nginx doesn't exists." echo "nginx doesn't exists."
fi fi
echo "nc:" echo "socat:"
if _exists "nc"; then if _exists "socat"; then
nc -h 2>&1 socat -h 2>&1
else else
_debug "nc doesn't exists." _debug "socat doesn't exists."
fi fi
} }
@ -1367,6 +1371,10 @@ _time2str() {
echo "$_t_s_a" echo "$_t_s_a"
fi fi
#Busybox
if echo "$1" | awk '{ print strftime("%c", $0); }' 2>/dev/null; then
return
fi
} }
_normalizeJson() { _normalizeJson() {
@ -1806,7 +1814,13 @@ _send_signed_request() {
_CACHED_NONCE="$(echo "$responseHeaders" | grep "Replay-Nonce:" | _head_n 1 | tr -d "\r\n " | cut -d ':' -f 2)" _CACHED_NONCE="$(echo "$responseHeaders" | grep "Replay-Nonce:" | _head_n 1 | tr -d "\r\n " | cut -d ':' -f 2)"
if _contains "$response" "JWS has invalid anti-replay nonce"; then _body="$response"
if [ "$needbase64" ]; then
_body="$(echo "$_body" | _dbase64)"
_debug2 _body "$_body"
fi
if _contains "$_body" "JWS has invalid anti-replay nonce"; then
_info "It seems the CA server is busy now, let's wait and retry." _info "It seems the CA server is busy now, let's wait and retry."
_request_retry_times=$(_math "$_request_retry_times" + 1) _request_retry_times=$(_math "$_request_retry_times" + 1)
_sleep 5 _sleep 5
@ -1959,68 +1973,22 @@ _startserver() {
_debug "ncaddr" "$ncaddr" _debug "ncaddr" "$ncaddr"
_debug "startserver: $$" _debug "startserver: $$"
nchelp="$(nc -h 2>&1)"
_debug Le_HTTPPort "$Le_HTTPPort" _debug Le_HTTPPort "$Le_HTTPPort"
_debug Le_Listen_V4 "$Le_Listen_V4" _debug Le_Listen_V4 "$Le_Listen_V4"
_debug Le_Listen_V6 "$Le_Listen_V6" _debug Le_Listen_V6 "$Le_Listen_V6"
_NC="nc"
_NC="socat"
if [ "$Le_Listen_V4" ]; then if [ "$Le_Listen_V4" ]; then
_NC="$_NC -4" _NC="$_NC -4"
elif [ "$Le_Listen_V6" ]; then elif [ "$Le_Listen_V6" ]; then
_NC="$_NC -6" _NC="$_NC -6"
fi fi
if [ "$Le_Listen_V4$Le_Listen_V6$ncaddr" ]; then
if ! _contains "$nchelp" "-4"; then
_err "The nc doesn't support '-4', '-6' or local-address, please install 'netcat-openbsd' and try again."
_err "See $(__green $_PREPARE_LINK)"
return 1
fi
fi
if echo "$nchelp" | grep "\-q[ ,]" >/dev/null; then
_NC="$_NC -q 1 -l $ncaddr"
else
if echo "$nchelp" | grep "GNU netcat" >/dev/null && echo "$nchelp" | grep "\-c, \-\-close" >/dev/null; then
_NC="$_NC -c -l $ncaddr"
elif echo "$nchelp" | grep "\-N" | grep "Shutdown the network socket after EOF on stdin" >/dev/null; then
_NC="$_NC -N -l $ncaddr"
else
_NC="$_NC -l $ncaddr"
fi
fi
_debug "_NC" "$_NC" _debug "_NC" "$_NC"
#todo listen address
#for centos ncat $_NC TCP-LISTEN:$Le_HTTPPort,crlf,reuseaddr,fork SYSTEM:"sleep 0.5; echo HTTP/1.1 200 OK; echo ; echo $content; echo;" &
if _contains "$nchelp" "nmap.org"; then serverproc="$!"
_debug "Using ncat: nmap.org"
if ! _exec "printf \"%s\r\n\r\n%s\" \"HTTP/1.1 200 OK\" \"$content\" | $_NC \"$Le_HTTPPort\" >&2"; then
_exec_err
return 1
fi
if [ "$DEBUG" ]; then
_exec_err
fi
return
fi
# while true ; do
if ! _exec "printf \"%s\r\n\r\n%s\" \"HTTP/1.1 200 OK\" \"$content\" | $_NC -p \"$Le_HTTPPort\" >&2"; then
_exec "printf \"%s\r\n\r\n%s\" \"HTTP/1.1 200 OK\" \"$content\" | $_NC \"$Le_HTTPPort\" >&2"
fi
if [ "$?" != "0" ]; then
_err "nc listen error."
_exec_err
exit 1
fi
if [ "$DEBUG" ]; then
_exec_err
fi
# done
} }
_stopserver() { _stopserver() {
@ -2030,25 +1998,8 @@ _stopserver() {
return return
fi fi
_debug2 "Le_HTTPPort" "$Le_HTTPPort" kill $pid
if [ "$Le_HTTPPort" ]; then
if [ "$DEBUG" ] && [ "$DEBUG" -gt "3" ]; then
_get "http://localhost:$Le_HTTPPort" "" 1
else
_get "http://localhost:$Le_HTTPPort" "" 1 >/dev/null 2>&1
fi
fi
_debug2 "Le_TLSPort" "$Le_TLSPort"
if [ "$Le_TLSPort" ]; then
if [ "$DEBUG" ] && [ "$DEBUG" -gt "3" ]; then
_get "https://localhost:$Le_TLSPort" "" 1
_get "https://localhost:$Le_TLSPort" "" 1
else
_get "https://localhost:$Le_TLSPort" "" 1 >/dev/null 2>&1
_get "https://localhost:$Le_TLSPort" "" 1 >/dev/null 2>&1
fi
fi
} }
# sleep sec # sleep sec
@ -2103,7 +2054,7 @@ _starttlsserver() {
return 1 return 1
fi fi
__S_OPENSSL="${ACME_OPENSSL_BIN:-openssl} s_server -cert $TLS_CERT -key $TLS_KEY " __S_OPENSSL="${ACME_OPENSSL_BIN:-openssl} s_server -www -cert $TLS_CERT -key $TLS_KEY "
if [ "$opaddr" ]; then if [ "$opaddr" ]; then
__S_OPENSSL="$__S_OPENSSL -accept $opaddr:$port" __S_OPENSSL="$__S_OPENSSL -accept $opaddr:$port"
else else
@ -2120,9 +2071,9 @@ _starttlsserver() {
_debug "$__S_OPENSSL" _debug "$__S_OPENSSL"
if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then
(printf "%s\r\n\r\n%s" "HTTP/1.1 200 OK" "$content" | $__S_OPENSSL -tlsextdebug) & $__S_OPENSSL -tlsextdebug &
else else
(printf "%s\r\n\r\n%s" "HTTP/1.1 200 OK" "$content" | $__S_OPENSSL >/dev/null 2>&1) & $__S_OPENSSL >/dev/null 2>&1 &
fi fi
serverproc="$!" serverproc="$!"
@ -2298,6 +2249,7 @@ _initpath() {
fi fi
fi fi
_debug2 ACME_DIRECTORY "$ACME_DIRECTORY"
_ACME_SERVER_HOST="$(echo "$ACME_DIRECTORY" | cut -d : -f 2 | tr -s / | cut -d / -f 2)" _ACME_SERVER_HOST="$(echo "$ACME_DIRECTORY" | cut -d : -f 2 | tr -s / | cut -d / -f 2)"
_debug2 "_ACME_SERVER_HOST" "$_ACME_SERVER_HOST" _debug2 "_ACME_SERVER_HOST" "$_ACME_SERVER_HOST"
@ -2935,8 +2887,8 @@ _on_before_issue() {
fi fi
if _hasfield "$_chk_web_roots" "$NO_VALUE"; then if _hasfield "$_chk_web_roots" "$NO_VALUE"; then
if ! _exists "nc"; then if ! _exists "socat"; then
_err "Please install netcat(nc) tools first." _err "Please install socat tools first."
return 1 return 1
fi fi
fi fi
@ -3042,6 +2994,10 @@ _on_issue_err() {
) )
fi fi
if [ "$IS_RENEW" = "1" ] && _hasfield "$Le_Webroot" "dns"; then
_err "$_DNS_MANUAL_ERR"
fi
if [ "$DEBUG" ] && [ "$DEBUG" -gt "0" ]; then if [ "$DEBUG" ] && [ "$DEBUG" -gt "0" ]; then
_debug "$(_dlg_versions)" _debug "$(_dlg_versions)"
fi fi
@ -3074,6 +3030,10 @@ _on_issue_success() {
fi fi
fi fi
if _hasfield "$Le_Webroot" "dns"; then
_err "$_DNS_MANUAL_WARN"
fi
} }
updateaccount() { updateaccount() {
@ -3175,7 +3135,7 @@ _regAccount() {
fi fi
if [ "$code" = '202' ]; then if [ "$code" = '202' ]; then
_info "Update account tos info success." _info "Update account tos info success."
echo "$response" >"$ACCOUNT_JSON_PATH"
CA_KEY_HASH="$(__calcAccountKeyHash)" CA_KEY_HASH="$(__calcAccountKeyHash)"
_debug "Calc CA_KEY_HASH" "$CA_KEY_HASH" _debug "Calc CA_KEY_HASH" "$CA_KEY_HASH"
_savecaconf CA_KEY_HASH "$CA_KEY_HASH" _savecaconf CA_KEY_HASH "$CA_KEY_HASH"
@ -3649,13 +3609,12 @@ issue() {
_info "Standalone mode server" _info "Standalone mode server"
_ncaddr="$(_getfield "$_local_addr" "$_ncIndex")" _ncaddr="$(_getfield "$_local_addr" "$_ncIndex")"
_ncIndex="$(_math $_ncIndex + 1)" _ncIndex="$(_math $_ncIndex + 1)"
_startserver "$keyauthorization" "$_ncaddr" & _startserver "$keyauthorization" "$_ncaddr"
if [ "$?" != "0" ]; then if [ "$?" != "0" ]; then
_clearup _clearup
_on_issue_err "$_post_hook" "$vlist" _on_issue_err "$_post_hook" "$vlist"
return 1 return 1
fi fi
serverproc="$!"
sleep 1 sleep 1
_debug serverproc "$serverproc" _debug serverproc "$serverproc"
elif [ "$_currentRoot" = "$MODE_STATELESS" ]; then elif [ "$_currentRoot" = "$MODE_STATELESS" ]; then
@ -3990,7 +3949,10 @@ issue() {
Le_NextRenewTime=$(_math "$Le_NextRenewTime" - 86400) Le_NextRenewTime=$(_math "$Le_NextRenewTime" - 86400)
_savedomainconf "Le_NextRenewTime" "$Le_NextRenewTime" _savedomainconf "Le_NextRenewTime" "$Le_NextRenewTime"
_on_issue_success "$_post_hook" "$_renew_hook" if ! _on_issue_success "$_post_hook" "$_renew_hook"; then
_err "Call hook error."
return 1
fi
if [ "$_real_cert$_real_key$_real_ca$_reload_cmd$_real_fullchain" ]; then if [ "$_real_cert$_real_key$_real_ca$_reload_cmd$_real_fullchain" ]; then
_savedomainconf "Le_RealCertPath" "$_real_cert" _savedomainconf "Le_RealCertPath" "$_real_cert"
@ -4417,15 +4379,19 @@ _installcert() {
installcronjob() { installcronjob() {
_c_home="$1" _c_home="$1"
_initpath _initpath
if ! _exists "crontab"; then _CRONTAB="crontab"
_err "crontab doesn't exist, so, we can not install cron jobs." if ! _exists "$_CRONTAB" && _exists "fcrontab"; then
_CRONTAB="fcrontab"
fi
if ! _exists "$_CRONTAB"; then
_err "crontab/fcrontab doesn't exist, so, we can not install cron jobs."
_err "All your certs will not be renewed automatically." _err "All your certs will not be renewed automatically."
_err "You must add your own cron job to call '$PROJECT_ENTRY --cron' everyday." _err "You must add your own cron job to call '$PROJECT_ENTRY --cron' everyday."
return 1 return 1
fi fi
_info "Installing cron job" _info "Installing cron job"
if ! crontab -l | grep "$PROJECT_ENTRY --cron"; then if ! $_CRONTAB -l | grep "$PROJECT_ENTRY --cron"; then
if [ -f "$LE_WORKING_DIR/$PROJECT_ENTRY" ]; then if [ -f "$LE_WORKING_DIR/$PROJECT_ENTRY" ]; then
lesh="\"$LE_WORKING_DIR\"/$PROJECT_ENTRY" lesh="\"$LE_WORKING_DIR\"/$PROJECT_ENTRY"
else else
@ -4439,15 +4405,15 @@ installcronjob() {
_t=$(_time) _t=$(_time)
random_minute=$(_math $_t % 60) random_minute=$(_math $_t % 60)
if _exists uname && uname -a | grep SunOS >/dev/null; then if _exists uname && uname -a | grep SunOS >/dev/null; then
crontab -l | { $_CRONTAB -l | {
cat cat
echo "$random_minute 0 * * * $lesh --cron --home \"$LE_WORKING_DIR\" $_c_entry> /dev/null" echo "$random_minute 0 * * * $lesh --cron --home \"$LE_WORKING_DIR\" $_c_entry> /dev/null"
} | crontab -- } | $_CRONTAB --
else else
crontab -l | { $_CRONTAB -l | {
cat cat
echo "$random_minute 0 * * * $lesh --cron --home \"$LE_WORKING_DIR\" $_c_entry> /dev/null" echo "$random_minute 0 * * * $lesh --cron --home \"$LE_WORKING_DIR\" $_c_entry> /dev/null"
} | crontab - } | $_CRONTAB -
fi fi
fi fi
if [ "$?" != "0" ]; then if [ "$?" != "0" ]; then
@ -4459,16 +4425,21 @@ installcronjob() {
} }
uninstallcronjob() { uninstallcronjob() {
if ! _exists "crontab"; then _CRONTAB="crontab"
if ! _exists "$_CRONTAB" && _exists "fcrontab"; then
_CRONTAB="fcrontab"
fi
if ! _exists "$_CRONTAB"; then
return return
fi fi
_info "Removing cron job" _info "Removing cron job"
cr="$(crontab -l | grep "$PROJECT_ENTRY --cron")" cr="$($_CRONTAB -l | grep "$PROJECT_ENTRY --cron")"
if [ "$cr" ]; then if [ "$cr" ]; then
if _exists uname && uname -a | grep solaris >/dev/null; then if _exists uname && uname -a | grep solaris >/dev/null; then
crontab -l | sed "/$PROJECT_ENTRY --cron/d" | crontab -- $_CRONTAB -l | sed "/$PROJECT_ENTRY --cron/d" | $_CRONTAB --
else else
crontab -l | sed "/$PROJECT_ENTRY --cron/d" | crontab - $_CRONTAB -l | sed "/$PROJECT_ENTRY --cron/d" | $_CRONTAB -
fi fi
LE_WORKING_DIR="$(echo "$cr" | cut -d ' ' -f 9 | tr -d '"')" LE_WORKING_DIR="$(echo "$cr" | cut -d ' ' -f 9 | tr -d '"')"
_info LE_WORKING_DIR "$LE_WORKING_DIR" _info LE_WORKING_DIR "$LE_WORKING_DIR"
@ -4745,7 +4716,7 @@ _precheck() {
fi fi
if [ -z "$_nocron" ]; then if [ -z "$_nocron" ]; then
if ! _exists "crontab"; then if ! _exists "crontab" && ! _exists "fcrontab"; then
_err "It is recommended to install crontab first. try to install 'cron, crontab, crontabs or vixie-cron'." _err "It is recommended to install crontab first. try to install 'cron, crontab, crontabs or vixie-cron'."
_err "We need to set cron job to renew the certs automatically." _err "We need to set cron job to renew the certs automatically."
_err "Otherwise, your certs will not be able to be renewed automatically." _err "Otherwise, your certs will not be able to be renewed automatically."
@ -4763,9 +4734,9 @@ _precheck() {
return 1 return 1
fi fi
if ! _exists "nc"; then if ! _exists "socat"; then
_err "It is recommended to install nc first, try to install 'nc' or 'netcat'." _err "It is recommended to install socat first."
_err "We use nc for standalone server if you use standalone mode." _err "We use socat for standalone server if you use standalone mode."
_err "If you don't use standalone mode, just ignore this warning." _err "If you don't use standalone mode, just ignore this warning."
fi fi
@ -4865,9 +4836,11 @@ install() {
_debug "Skip install cron job" _debug "Skip install cron job"
fi fi
if ! _precheck "$_nocron"; then if [ "$IN_CRON" != "1" ]; then
_err "Pre-check failed, can not install." if ! _precheck "$_nocron"; then
return 1 _err "Pre-check failed, can not install."
return 1
fi
fi fi
if [ -z "$_c_home" ] && [ "$LE_CONFIG_HOME" != "$LE_WORKING_DIR" ]; then if [ -z "$_c_home" ] && [ "$LE_CONFIG_HOME" != "$LE_WORKING_DIR" ]; then
@ -4920,7 +4893,9 @@ install() {
_info "Installed to $LE_WORKING_DIR/$PROJECT_ENTRY" _info "Installed to $LE_WORKING_DIR/$PROJECT_ENTRY"
_installalias "$_c_home" if [ "$IN_CRON" != "1" ]; then
_installalias "$_c_home"
fi
for subf in $_SUB_FOLDERS; do for subf in $_SUB_FOLDERS; do
if [ -d "$subf" ]; then if [ -d "$subf" ]; then
@ -5010,7 +4985,7 @@ _uninstallalias() {
} }
cron() { cron() {
IN_CRON=1 export IN_CRON=1
_initpath _initpath
_info "$(__green "===Starting cron===")" _info "$(__green "===Starting cron===")"
if [ "$AUTO_UPGRADE" = "1" ]; then if [ "$AUTO_UPGRADE" = "1" ]; then

View File

@ -4,7 +4,9 @@ Before you can deploy your cert, you must [issue the cert first](https://github.
Here are the scripts to deploy the certs/key to the server/services. Here are the scripts to deploy the certs/key to the server/services.
## 1. Deploy the certs to your cpanel host. ## 1. Deploy the certs to your cpanel host
If you want to deploy using cpanel UAPI see 7.
(cpanel deploy hook is not finished yet, this is just an example.) (cpanel deploy hook is not finished yet, this is just an example.)
@ -18,7 +20,7 @@ export DEPLOY_CPANEL_PASSWORD=PASSWORD
acme.sh --deploy -d example.com --deploy-hook cpanel acme.sh --deploy -d example.com --deploy-hook cpanel
``` ```
## 2. Deploy ssl cert on kong proxy engine based on api. ## 2. Deploy ssl cert on kong proxy engine based on api
Before you can deploy your cert, you must [issue the cert first](https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert). Before you can deploy your cert, you must [issue the cert first](https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert).
Currently supports Kong-v0.10.x. Currently supports Kong-v0.10.x.
@ -27,7 +29,7 @@ Currently supports Kong-v0.10.x.
acme.sh --deploy -d ftp.example.com --deploy-hook kong acme.sh --deploy -d ftp.example.com --deploy-hook kong
``` ```
## 3. Deploy the cert to remote server through SSH access. ## 3. Deploy the cert to remote server through SSH access
The ssh deploy plugin allows you to deploy certificates to a remote host The ssh deploy plugin allows you to deploy certificates to a remote host
using SSH command to connect to the remote server. The ssh plugin is invoked using SSH command to connect to the remote server. The ssh plugin is invoked
@ -170,7 +172,7 @@ export DEPLOY_SSH_BACKUP=no
&& service unifi restart && service unifi restart
``` ```
## 4. Deploy the cert to local vsftpd server. ## 4. Deploy the cert to local vsftpd server
```sh ```sh
acme.sh --deploy -d ftp.example.com --deploy-hook vsftpd acme.sh --deploy -d ftp.example.com --deploy-hook vsftpd
@ -192,7 +194,7 @@ export DEPLOY_VSFTPD_RELOAD="/etc/init.d/vsftpd restart"
acme.sh --deploy -d ftp.example.com --deploy-hook vsftpd acme.sh --deploy -d ftp.example.com --deploy-hook vsftpd
``` ```
## 5. Deploy the cert to local exim4 server. ## 5. Deploy the cert to local exim4 server
```sh ```sh
acme.sh --deploy -d ftp.example.com --deploy-hook exim4 acme.sh --deploy -d ftp.example.com --deploy-hook exim4
@ -219,3 +221,37 @@ acme.sh --deploy -d ftp.example.com --deploy-hook exim4
```sh ```sh
acme.sh --deploy -d ftp.example.com --deploy-hook keychain acme.sh --deploy -d ftp.example.com --deploy-hook keychain
``` ```
## 7. Deploy to cpanel host using UAPI
This hook is using UAPI and works in cPanel & WHM version 56 or newer.
```
acme.sh --deploy -d example.com --deploy-hook cpanel_uapi
```
DEPLOY_CPANEL_USER is required only if you run the script as root and it should contain cpanel username.
```sh
export DEPLOY_CPANEL_USER=username
acme.sh --deploy -d example.com --deploy-hook cpanel_uapi
```
Please note, that the cpanel_uapi hook will deploy only the first domain when your certificate will automatically renew. Therefore you should issue a separete certificate for each domain.
## 8. Deploy the cert to your FRITZ!Box router
You must specify the credentials that have administrative privileges on the FRITZ!Box in order to deploy the certificate, plus the URL of your FRITZ!Box, through the following environment variables:
```sh
$ export DEPLOY_FRITZBOX_USERNAME=my_username
$ export DEPLOY_FRITZBOX_PASSWORD=the_password
$ export DEPLOY_FRITZBOX_URL=https://fritzbox.example.com
```
After the first deployment, these values will be stored in your $HOME/.acme.sh/account.conf. You may now deploy the certificate like this:
```sh
acme.sh --deploy -d fritzbox.example.com --deploy-hook fritzbox
```
## 9. Deploy the cert to strongswan
```sh
acme.sh --deploy -d ftp.example.com --deploy-hook strongswan
```

View File

@ -1,29 +0,0 @@
#!/usr/bin/env sh
#Here is the script to deploy the cert to your cpanel account by the cpanel APIs.
#returns 0 means success, otherwise error.
#export DEPLOY_CPANEL_USER=myusername
#export DEPLOY_CPANEL_PASSWORD=PASSWORD
######## Public functions #####################
#domain keyfile certfile cafile fullchain
cpanel_deploy() {
_cdomain="$1"
_ckey="$2"
_ccert="$3"
_cca="$4"
_cfullchain="$5"
_debug _cdomain "$_cdomain"
_debug _ckey "$_ckey"
_debug _ccert "$_ccert"
_debug _cca "$_cca"
_debug _cfullchain "$_cfullchain"
_err "Not implemented yet"
return 1
}

64
deploy/cpanel_uapi.sh Normal file
View File

@ -0,0 +1,64 @@
#!/usr/bin/env sh
# Here is the script to deploy the cert to your cpanel using the cpanel API.
# Uses command line uapi. --user option is needed only if run as root.
# Returns 0 when success.
# Written by Santeri Kannisto <santeri.kannisto@2globalnomads.info>
# Public domain, 2017
#export DEPLOY_CPANEL_USER=myusername
######## Public functions #####################
#domain keyfile certfile cafile fullchain
cpanel_uapi_deploy() {
_cdomain="$1"
_ckey="$2"
_ccert="$3"
_cca="$4"
_cfullchain="$5"
_debug _cdomain "$_cdomain"
_debug _ckey "$_ckey"
_debug _ccert "$_ccert"
_debug _cca "$_cca"
_debug _cfullchain "$_cfullchain"
if ! _exists uapi; then
_err "The command uapi is not found."
return 1
fi
if ! _exists php; then
_err "The command php is not found."
return 1
fi
# read cert and key files and urlencode both
_certstr=$(cat "$_ccert")
_keystr=$(cat "$_ckey")
_cert=$(php -r "echo urlencode(\"$_certstr\");")
_key=$(php -r "echo urlencode(\"$_keystr\");")
_debug _cert "$_cert"
_debug _key "$_key"
if [ "$(id -u)" = 0 ]; then
if [ -z "$DEPLOY_CPANEL_USER" ]; then
_err "It seems that you are root, please define the target user name: export DEPLOY_CPANEL_USER=username"
return 1
fi
_savedomainconf DEPLOY_CPANEL_USER "$DEPLOY_CPANEL_USER"
_response=$(uapi --user="$DEPLOY_CPANEL_USER" SSL install_ssl domain="$_cdomain" cert="$_cert" key="$_key")
else
_response=$(uapi SSL install_ssl domain="$_cdomain" cert="$_cert" key="$_key")
fi
error_response="status: 0"
if test "${_response#*$error_response}" != "$_response"; then
_err "Error in deploying certificate:"
_err "$_response"
return 1
fi
_debug response "$_response"
_info "Certificate successfully deployed"
return 0
}

108
deploy/fritzbox.sh Normal file
View File

@ -0,0 +1,108 @@
#!/usr/bin/env sh
#Here is a script to deploy cert to an AVM FRITZ!Box router.
#returns 0 means success, otherwise error.
#DEPLOY_FRITZBOX_USERNAME="username"
#DEPLOY_FRITZBOX_PASSWORD="password"
#DEPLOY_FRITZBOX_URL="https://fritz.box"
# Kudos to wikrie at Github for his FRITZ!Box update script:
# https://gist.github.com/wikrie/f1d5747a714e0a34d0582981f7cb4cfb
######## Public functions #####################
#domain keyfile certfile cafile fullchain
fritzbox_deploy() {
_cdomain="$1"
_ckey="$2"
_ccert="$3"
_cca="$4"
_cfullchain="$5"
_debug _cdomain "$_cdomain"
_debug _ckey "$_ckey"
_debug _ccert "$_ccert"
_debug _cca "$_cca"
_debug _cfullchain "$_cfullchain"
if ! _exists iconv; then
_err "iconv not found"
return 1
fi
_fritzbox_username="${DEPLOY_FRITZBOX_USERNAME}"
_fritzbox_password="${DEPLOY_FRITZBOX_PASSWORD}"
_fritzbox_url="${DEPLOY_FRITZBOX_URL}"
_debug _fritzbox_url "$_fritzbox_url"
_debug _fritzbox_username "$_fritzbox_username"
_secure_debug _fritzbox_password "$_fritzbox_password"
if [ -z "$_fritzbox_username" ]; then
_err "FRITZ!Box username is not found, please define DEPLOY_FRITZBOX_USERNAME."
return 1
fi
if [ -z "$_fritzbox_password" ]; then
_err "FRITZ!Box password is not found, please define DEPLOY_FRITZBOX_PASSWORD."
return 1
fi
if [ -z "$_fritzbox_url" ]; then
_err "FRITZ!Box url is not found, please define DEPLOY_FRITZBOX_URL."
return 1
fi
_saveaccountconf DEPLOY_FRITZBOX_USERNAME "${_fritzbox_username}"
_saveaccountconf DEPLOY_FRITZBOX_PASSWORD "${_fritzbox_password}"
_saveaccountconf DEPLOY_FRITZBOX_URL "${_fritzbox_url}"
# Do not check for a valid SSL certificate, because initially the cert is not valid, so it could not install the LE generated certificate
export HTTPS_INSECURE=1
_info "Log in to the FRITZ!Box"
_fritzbox_challenge="$(_get "${_fritzbox_url}/login_sid.lua" | sed -e 's/^.*<Challenge>//' -e 's/<\/Challenge>.*$//')"
_fritzbox_hash="$(printf "%s-%s" "${_fritzbox_challenge}" "${_fritzbox_password}" | iconv -f ASCII -t UTF16LE | md5sum | awk '{print $1}')"
_fritzbox_sid="$(_get "${_fritzbox_url}/login_sid.lua?sid=0000000000000000&username=${_fritzbox_username}&response=${_fritzbox_challenge}-${_fritzbox_hash}" | sed -e 's/^.*<SID>//' -e 's/<\/SID>.*$//')"
if [ -z "${_fritzbox_sid}" ] || [ "${_fritzbox_sid}" = "0000000000000000" ]; then
_err "Logging in to the FRITZ!Box failed. Please check username, password and URL."
return 1
fi
_info "Generate form POST request"
_post_request="$(_mktemp)"
_post_boundary="---------------------------$(date +%Y%m%d%H%M%S)"
# _CERTPASSWORD_ is unset because Let's Encrypt certificates don't have a password. But if they ever do, here's the place to use it!
_CERTPASSWORD_=
{
printf -- "--"
printf -- "%s\r\n" "${_post_boundary}"
printf "Content-Disposition: form-data; name=\"sid\"\r\n\r\n%s\r\n" "${_fritzbox_sid}"
printf -- "--"
printf -- "%s\r\n" "${_post_boundary}"
printf "Content-Disposition: form-data; name=\"BoxCertPassword\"\r\n\r\n%s\r\n" "${_CERTPASSWORD_}"
printf -- "--"
printf -- "%s\r\n" "${_post_boundary}"
printf "Content-Disposition: form-data; name=\"BoxCertImportFile\"; filename=\"BoxCert.pem\"\r\n"
printf "Content-Type: application/octet-stream\r\n\r\n"
cat "${_ckey}" "${_cfullchain}"
printf "\r\n"
printf -- "--"
printf -- "%s--" "${_post_boundary}"
} >>"${_post_request}"
_info "Upload certificate to the FRITZ!Box"
export _H1="Content-type: multipart/form-data boundary=${_post_boundary}"
_post "$(cat "${_post_request}")" "${_fritzbox_url}/cgi-bin/firmwarecfg" | grep SSL
retval=$?
if [ $retval = 0 ]; then
_info "Upload successful"
else
_err "Upload failed"
fi
rm "${_post_request}"
return $retval
}

32
deploy/strongswan.sh Normal file
View File

@ -0,0 +1,32 @@
#!/usr/bin/env sh
#Here is a sample custom api script.
#This file name is "myapi.sh"
#So, here must be a method myapi_deploy()
#Which will be called by acme.sh to deploy the cert
#returns 0 means success, otherwise error.
######## Public functions #####################
#domain keyfile certfile cafile fullchain
strongswan_deploy() {
_cdomain="$1"
_ckey="$2"
_ccert="$3"
_cca="$4"
_cfullchain="$5"
_debug _cdomain "$_cdomain"
_debug _ckey "$_ckey"
_debug _ccert "$_ccert"
_debug _cca "$_cca"
_debug _cfullchain "$_cfullchain"
cat "$_ckey" >"/etc/ipsec.d/private/$(basename "$_ckey")"
cat "$_ccert" >"/etc/ipsec.d/certs/$(basename "$_ccert")"
cat "$_cca" >"/etc/ipsec.d/cacerts/$(basename "$_cca")"
cat "$_cfullchain" >"/etc/ipsec.d/cacerts/$(basename "$_cfullchain")"
ipsec reload
}

View File

@ -420,6 +420,7 @@ Ok, let's issue a cert now:
``` ```
acme.sh --issue --dns dns_cloudns -d example.com -d www.example.com acme.sh --issue --dns dns_cloudns -d example.com -d www.example.com
``` ```
The `CLOUDNS_AUTH_ID` and `CLOUDNS_AUTH_PASSWORD` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
## 22. Use Infoblox API ## 22. Use Infoblox API
@ -512,14 +513,11 @@ export DuckDNS_Token="aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
``` ```
Please note that since DuckDNS uses StartSSL as their cert provider, thus Please note that since DuckDNS uses StartSSL as their cert provider, thus
--insecure must be used when issuing certs: --insecure may need to be used when issuing certs:
``` ```
acme.sh --insecure --issue --dns dns_duckdns -d mydomain.duckdns.org acme.sh --insecure --issue --dns dns_duckdns -d mydomain.duckdns.org
``` ```
Also, DuckDNS uses the domain name as username for recording changing, so the
account file will always store the lastly used domain name.
For issues, please report to https://github.com/raidenii/acme.sh/issues. For issues, please report to https://github.com/raidenii/acme.sh/issues.
## 28. Use Name.com API ## 28. Use Name.com API

View File

@ -87,6 +87,7 @@ _get_root() {
_debug "response" "$response" _debug "response" "$response"
while true; do while true; do
h=$(printf "%s" "$domain" | cut -d . -f $i-100) h=$(printf "%s" "$domain" | cut -d . -f $i-100)
_debug2 "Checking domain: $h"
if [ -z "$h" ]; then if [ -z "$h" ]; then
if _contains "$response" "<IsTruncated>true</IsTruncated>" && _contains "$response" "<NextMarker>"; then if _contains "$response" "<IsTruncated>true</IsTruncated>" && _contains "$response" "<NextMarker>"; then
_debug "IsTruncated" _debug "IsTruncated"
@ -102,23 +103,23 @@ _get_root() {
fi fi
fi fi
#not valid #not valid
_err "Invalid domain"
return 1 return 1
fi fi
if _contains "$response" "<Name>$h.</Name>"; then if _contains "$response" "<Name>$h.</Name>"; then
hostedzone="$(echo "$response" | sed 's/<HostedZone>/#&/g' | tr '#' '\n' | _egrep_o "<HostedZone><Id>[^<]*<.Id><Name>$h.<.Name>.*<PrivateZone>false<.PrivateZone>.*<.HostedZone>")" hostedzone="$(echo "$response" | sed 's/<HostedZone>/#&/g' | tr '#' '\n' | _egrep_o "<HostedZone><Id>[^<]*<.Id><Name>$h.<.Name>.*<PrivateZone>false<.PrivateZone>.*<.HostedZone>")"
_debug hostedzone "$hostedzone" _debug hostedzone "$hostedzone"
if [ -z "$hostedzone" ]; then if [ "$hostedzone" ]; then
_err "Error, can not get hostedzone." _domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o "<Id>.*<.Id>" | head -n 1 | _egrep_o ">.*<" | tr -d "<>")
if [ "$_domain_id" ]; then
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_domain=$h
return 0
fi
_err "Can not find domain id: $h"
return 1 return 1
fi fi
_domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o "<Id>.*<.Id>" | head -n 1 | _egrep_o ">.*<" | tr -d "<>")
if [ "$_domain_id" ]; then
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_domain=$h
return 0
fi
return 1
fi fi
p=$i p=$i
i=$(_math "$i" + 1) i=$(_math "$i" + 1)

View File

@ -96,6 +96,16 @@ _dns_cloudns_init_check() {
return 0 return 0
fi fi
CLOUDNS_AUTH_ID="${CLOUDNS_AUTH_ID:-$(_readaccountconf_mutable CLOUDNS_AUTH_ID)}"
CLOUDNS_AUTH_PASSWORD="${CLOUDNS_AUTH_PASSWORD:-$(_readaccountconf_mutable CLOUDNS_AUTH_PASSWORD)}"
if [ -z "$CLOUDNS_AUTH_ID" ] || [ -z "$CLOUDNS_AUTH_PASSWORD" ]; then
CLOUDNS_AUTH_ID=""
CLOUDNS_AUTH_PASSWORD=""
_err "You don't specify cloudns api id and password yet."
_err "Please create you id and password and try again."
return 1
fi
if [ -z "$CLOUDNS_AUTH_ID" ]; then if [ -z "$CLOUDNS_AUTH_ID" ]; then
_err "CLOUDNS_AUTH_ID is not configured" _err "CLOUDNS_AUTH_ID is not configured"
return 1 return 1
@ -113,6 +123,10 @@ _dns_cloudns_init_check() {
return 1 return 1
fi fi
#save the api id and password to the account conf file.
_saveaccountconf_mutable CLOUDNS_AUTH_ID "$CLOUDNS_AUTH_ID"
_saveaccountconf_mutable CLOUDNS_AUTH_PASSWORD "$CLOUDNS_AUTH_PASSWORD"
CLOUDNS_INIT_CHECK_COMPLETED=1 CLOUDNS_INIT_CHECK_COMPLETED=1
return 0 return 0

View File

@ -3,11 +3,14 @@
#Created by RaidenII, to use DuckDNS's API to add/remove text records #Created by RaidenII, to use DuckDNS's API to add/remove text records
#06/27/2017 #06/27/2017
# Currently only support single domain access # Pass credentials before "acme.sh --issue --dns dns_duckdns ..."
# Due to the fact that DuckDNS uses StartSSL as cert provider, --insecure must be used with acme.sh # --
# export DuckDNS_Token="aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
# --
#
# Due to the fact that DuckDNS uses StartSSL as cert provider, --insecure may need to be used with acme.sh
DuckDNS_API="https://www.duckdns.org/update" DuckDNS_API="https://www.duckdns.org/update"
API_Params="domains=$DuckDNS_Domain&token=$DuckDNS_Token"
######## Public functions ##################### ######## Public functions #####################
@ -16,35 +19,36 @@ dns_duckdns_add() {
fulldomain=$1 fulldomain=$1
txtvalue=$2 txtvalue=$2
# We'll extract the domain/username from full domain DuckDNS_Token="${DuckDNS_Token:-$(_readaccountconf_mutable DuckDNS_Token)}"
DuckDNS_Domain=$(echo "$fulldomain" | _lower_case | _egrep_o '.[^.]*.duckdns.org' | cut -d . -f 2)
if [ -z "$DuckDNS_Domain" ]; then
_err "Error extracting the domain."
return 1
fi
if [ -z "$DuckDNS_Token" ]; then if [ -z "$DuckDNS_Token" ]; then
DuckDNS_Token="" _err "You must export variable: DuckDNS_Token"
_err "The token for your DuckDNS account is necessary." _err "The token for your DuckDNS account is necessary."
_err "You can look it up in your DuckDNS account." _err "You can look it up in your DuckDNS account."
return 1 return 1
fi fi
# Now save the credentials. # Now save the credentials.
_saveaccountconf DuckDNS_Domain "$DuckDNS_Domain" _saveaccountconf_mutable DuckDNS_Token "$DuckDNS_Token"
_saveaccountconf DuckDNS_Token "$DuckDNS_Token"
# Unfortunately, DuckDNS does not seems to support lookup domain through API # Unfortunately, DuckDNS does not seems to support lookup domain through API
# So I assume your credentials (which are your domain and token) are correct # So I assume your credentials (which are your domain and token) are correct
# If something goes wrong, we will get a KO response from DuckDNS # If something goes wrong, we will get a KO response from DuckDNS
if ! _duckdns_get_domain; then
return 1
fi
# Now add the TXT record to DuckDNS # Now add the TXT record to DuckDNS
_info "Trying to add TXT record" _info "Trying to add TXT record"
if _duckdns_rest GET "$API_Params&txt=$txtvalue" && [ "$response" = "OK" ]; then if _duckdns_rest GET "domains=$_duckdns_domain&token=$DuckDNS_Token&txt=$txtvalue"; then
_info "TXT record has been successfully added to your DuckDNS domain." if [ "$response" = "OK" ]; then
_info "Note that all subdomains under this domain uses the same TXT record." _info "TXT record has been successfully added to your DuckDNS domain."
return 0 _info "Note that all subdomains under this domain uses the same TXT record."
return 0
else
_err "Errors happened during adding the TXT record, response=$response"
return 1
fi
else else
_err "Errors happened during adding the TXT record." _err "Errors happened during adding the TXT record."
return 1 return 1
@ -57,11 +61,28 @@ dns_duckdns_rm() {
fulldomain=$1 fulldomain=$1
txtvalue=$2 txtvalue=$2
DuckDNS_Token="${DuckDNS_Token:-$(_readaccountconf_mutable DuckDNS_Token)}"
if [ -z "$DuckDNS_Token" ]; then
_err "You must export variable: DuckDNS_Token"
_err "The token for your DuckDNS account is necessary."
_err "You can look it up in your DuckDNS account."
return 1
fi
if ! _duckdns_get_domain; then
return 1
fi
# Now remove the TXT record from DuckDNS # Now remove the TXT record from DuckDNS
_info "Trying to remove TXT record" _info "Trying to remove TXT record"
if _duckdns_rest GET "$API_Params&txt=&clear=true" && [ "$response" = "OK" ]; then if _duckdns_rest GET "domains=$_duckdns_domain&token=$DuckDNS_Token&txt=&clear=true"; then
_info "TXT record has been successfully removed from your DuckDNS domain." if [ "$response" = "OK" ]; then
return 0 _info "TXT record has been successfully removed from your DuckDNS domain."
return 0
else
_err "Errors happened during removing the TXT record, response=$response"
return 1
fi
else else
_err "Errors happened during removing the TXT record." _err "Errors happened during removing the TXT record."
return 1 return 1
@ -70,6 +91,22 @@ dns_duckdns_rm() {
#################### Private functions below ################################## #################### Private functions below ##################################
#fulldomain=_acme-challenge.domain.duckdns.org
#returns
# _duckdns_domain=domain
_duckdns_get_domain() {
# We'll extract the domain/username from full domain
_duckdns_domain="$(printf "%s" "$fulldomain" | _lower_case | _egrep_o '[.][^.][^.]*[.]duckdns.org' | cut -d . -f 2)"
if [ -z "$_duckdns_domain" ]; then
_err "Error extracting the domain."
return 1
fi
return 0
}
#Usage: method URI #Usage: method URI
_duckdns_rest() { _duckdns_rest() {
method=$1 method=$1

View File

@ -11,7 +11,7 @@
# #
######## Public functions ##################### ######## Public functions #####################
GANDI_LIVEDNS_API="https://dns.beta.gandi.net/api/v5" GANDI_LIVEDNS_API="https://dns.api.gandi.net/api/v5"
#Usage: dns_gandi_livedns_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" #Usage: dns_gandi_livedns_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_gandi_livedns_add() { dns_gandi_livedns_add() {

View File

@ -47,7 +47,7 @@ dns_he_add() {
response="$(_post "$body" "https://dns.he.net/")" response="$(_post "$body" "https://dns.he.net/")"
exit_code="$?" exit_code="$?"
if [ "$exit_code" -eq 0 ]; then if [ "$exit_code" -eq 0 ]; then
_info "TXT record added successfuly." _info "TXT record added successfully."
else else
_err "Couldn't add the TXT record." _err "Couldn't add the TXT record."
fi fi
@ -96,7 +96,7 @@ dns_he_rm() {
>/dev/null >/dev/null
exit_code="$?" exit_code="$?"
if [ "$exit_code" -eq 0 ]; then if [ "$exit_code" -eq 0 ]; then
_info "Record removed successfuly." _info "Record removed successfully."
else else
_err "Could not clean (remove) up the record. Please go to HE administration interface and clean it by hand." _err "Could not clean (remove) up the record. Please go to HE administration interface and clean it by hand."
return "$exit_code" return "$exit_code"