Browse Source

Merge branch 'master' of https://github.com/acmesh-official/acme.sh into master

pull/2919/head
Sergey Pashinin 5 years ago
parent
commit
cd4baab794
No known key found for this signature in database GPG Key ID: 3C0D84EA2D933E47
  1. 18
      .github/auto-comment.yml
  2. 16
      .github/workflows/dockerhub.yml
  3. 3
      .travis.yml
  4. 2
      Dockerfile
  5. 52
      README.md
  6. 411
      acme.sh
  7. 2
      deploy/docker.sh
  8. 1
      deploy/haproxy.sh
  9. 4
      deploy/kong.sh
  10. 262
      deploy/openstack.sh
  11. 139
      deploy/ssh.sh
  12. 33
      deploy/synology_dsm.sh
  13. 10
      dnsapi/dns_1984hosting.sh
  14. 1
      dnsapi/dns_ali.sh
  15. 163
      dnsapi/dns_arvan.sh
  16. 2
      dnsapi/dns_azure.sh
  17. 16
      dnsapi/dns_cf.sh
  18. 6
      dnsapi/dns_cloudns.sh
  19. 4
      dnsapi/dns_cyon.sh
  20. 2
      dnsapi/dns_ddnss.sh
  21. 65
      dnsapi/dns_df.sh
  22. 10
      dnsapi/dns_dgon.sh
  23. 7
      dnsapi/dns_duckdns.sh
  24. 4
      dnsapi/dns_dynu.sh
  25. 6
      dnsapi/dns_dynv6.sh
  26. 5
      dnsapi/dns_easydns.sh
  27. 2
      dnsapi/dns_gd.sh
  28. 11
      dnsapi/dns_gdnsdk.sh
  29. 2
      dnsapi/dns_he.sh
  30. 252
      dnsapi/dns_hetzner.sh
  31. 8
      dnsapi/dns_hexonet.sh
  32. 81
      dnsapi/dns_inwx.sh
  33. 150
      dnsapi/dns_kappernet.sh
  34. 2
      dnsapi/dns_kinghost.sh
  35. 4
      dnsapi/dns_lexicon.sh
  36. 2
      dnsapi/dns_loopia.sh
  37. 162
      dnsapi/dns_netlify.sh
  38. 168
      dnsapi/dns_njalla.sh
  39. 170
      dnsapi/dns_one.sh
  40. 19
      dnsapi/dns_openprovider.sh
  41. 348
      dnsapi/dns_openstack.sh
  42. 2
      dnsapi/dns_opnsense.sh
  43. 2
      dnsapi/dns_rackspace.sh
  44. 67
      dnsapi/dns_regru.sh
  45. 162
      dnsapi/dns_transip.sh
  46. 8
      dnsapi/dns_unoeuro.sh
  47. 2
      dnsapi/dns_variomedia.sh
  48. 2
      dnsapi/dns_yandex.sh
  49. 51
      notify/mail.sh
  50. 86
      notify/teams.sh

18
.github/auto-comment.yml

@ -4,17 +4,23 @@ issuesOpened: >
如果有 bug, 请先更新到最新版试试:
```sh
```
acme.sh --upgrade
```
please also provide the log with `--debug 2`.
同时请提供调试输出 `--debug 2`
see: https://github.com/acmesh-official/acme.sh/wiki/How-to-debug-acme.sh
Without `--debug 2` log, your issue will NEVER get replied.
没有调试输出, 你的 issue 不会得到任何解答.
pullRequestOpened: >
First, never send a PR to `master` branch, it will never be accepted. Please send to the `dev` branch instead.
First, NEVER send a PR to `master` branch, it will NEVER be accepted. Please send to the `dev` branch instead.
If this is a PR to support new DNS API or new notification API, please read this guide first:
https://github.com/acmesh-official/acme.sh/wiki/DNS-API-Dev-Guide
@ -23,6 +29,12 @@ pullRequestOpened: >
Then add your usage here:
https://github.com/acmesh-official/acme.sh/wiki/dnsapi
Or some other wiki pages:
https://github.com/acmesh-official/acme.sh/wiki/deployhooks
https://github.com/acmesh-official/acme.sh/wiki/notify

16
.github/workflows/dockerhub.yml

@ -0,0 +1,16 @@
name: Build DockerHub
on:
push:
branches: [ master, dev ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: trigger
run: curl -X POST https://hub.docker.com/api/build/v1/source/1813a660-2ee5-4583-a238-dd54e9a6ebac/trigger/c8cd9f1f-f269-45bc-9750-a08327257f62/call/

3
.travis.yml

@ -1,6 +1,5 @@
language: shell
sudo: required
dist: trusty
dist: bionic
os:
- linux

2
Dockerfile

@ -1,4 +1,4 @@
FROM alpine:3.10
FROM alpine:3.12
RUN apk update -f \
&& apk --no-cache add -f \

52
README.md

@ -1,6 +1,13 @@
# An ACME Shell script: acme.sh [![Build Status](https://travis-ci.org/acmesh-official/acme.sh.svg?branch=master)](https://travis-ci.org/acmesh-official/acme.sh)
<a href="https://opencollective.com/acmesh" alt="Financial Contributors on Open Collective"><img src="https://opencollective.com/acmesh/all/badge.svg?label=financial+contributors" /></a> [![Join the chat at https://gitter.im/acme-sh/Lobby](https://badges.gitter.im/acme-sh/Lobby.svg)](https://gitter.im/acme-sh/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Docker stars](https://img.shields.io/docker/stars/neilpang/acme.sh.svg)](https://hub.docker.com/r/neilpang/acme.sh "Click to view the image on Docker Hub")
[![Docker pulls](https://img.shields.io/docker/pulls/neilpang/acme.sh.svg)](https://hub.docker.com/r/neilpang/acme.sh "Click to view the image on Docker Hub")
acme.sh is being sponsored by the following tool; please help to support us by taking a look and signing up to a free trial
- An ACME protocol client written purely in Shell (Unix shell) language.
- Full ACME protocol implementation.
- Support ACME v1 and ACME v2
@ -29,7 +36,7 @@ Twitter: [@neilpangxa](https://twitter.com/neilpangxa)
# Who:
- [FreeBSD.org](https://blog.crashed.org/letsencrypt-in-freebsd-org/)
- [ruby-china.org](https://ruby-china.org/topics/31983)
- [Proxmox](https://pve.proxmox.com/wiki/HTTPS_Certificate_Configuration_(Version_4.x_and_newer))
- [Proxmox](https://pve.proxmox.com/wiki/Certificate_Management)
- [pfsense](https://github.com/pfsense/FreeBSD-ports/pull/89)
- [webfaction](https://community.webfaction.com/questions/19988/using-letsencrypt)
- [Loadbalancer.org](https://www.loadbalancer.org/blog/loadbalancer-org-with-lets-encrypt-quick-and-dirty)
@ -46,27 +53,27 @@ Twitter: [@neilpangxa](https://twitter.com/neilpangxa)
| NO | Status| Platform|
|----|-------|---------|
|1|[![](https://acmesh-official.github.io/acmetest/status/ubuntu-latest.svg)](https://github.com/acmesh-official/letest#here-are-the-latest-status)| Ubuntu
|2|[![](https://acmesh-official.github.io/acmetest/status/debian-latest.svg)](https://github.com/acmesh-official/letest#here-are-the-latest-status)| Debian
|3|[![](https://acmesh-official.github.io/acmetest/status/centos-latest.svg)](https://github.com/acmesh-official/letest#here-are-the-latest-status)|CentOS
|4|[![](https://acmesh-official.github.io/acmetest/status/windows-cygwin.svg)](https://github.com/acmesh-official/letest#here-are-the-latest-status)|Windows (cygwin with curl, openssl and crontab included)
|5|[![](https://acmesh-official.github.io/acmetest/status/freebsd.svg)](https://github.com/acmesh-official/letest#here-are-the-latest-status)|FreeBSD
|6|[![](https://acmesh-official.github.io/acmetest/status/pfsense.svg)](https://github.com/acmesh-official/letest#here-are-the-latest-status)|pfsense
|7|[![](https://acmesh-official.github.io/acmetest/status/opensuse-leap.svg)](https://github.com/acmesh-official/letest#here-are-the-latest-status)|openSUSE
|8|[![](https://acmesh-official.github.io/acmetest/status/alpine-latest.svg)](https://github.com/acmesh-official/letest#here-are-the-latest-status)|Alpine Linux (with curl)
|9|[![](https://acmesh-official.github.io/acmetest/status/archlinux-latest.svg)](https://github.com/acmesh-official/letest#here-are-the-latest-status)|Archlinux
|10|[![](https://acmesh-official.github.io/acmetest/status/fedora-latest.svg)](https://github.com/acmesh-official/letest#here-are-the-latest-status)|fedora
|11|[![](https://acmesh-official.github.io/acmetest/status/kalilinux-kali.svg)](https://github.com/acmesh-official/letest#here-are-the-latest-status)|Kali Linux
|12|[![](https://acmesh-official.github.io/acmetest/status/oraclelinux-latest.svg)](https://github.com/acmesh-official/letest#here-are-the-latest-status)|Oracle Linux
|13|[![](https://acmesh-official.github.io/acmetest/status/proxmox.svg)](https://github.com/acmesh-official/letest#here-are-the-latest-status)| Proxmox https://pve.proxmox.com/wiki/HTTPSCertificateConfiguration#Let.27s_Encrypt_using_acme.sh
|1|[![](https://acmesh-official.github.io/acmetest/status/ubuntu-latest.svg)](https://github.com/acmesh-official/acmetest#here-are-the-latest-status)| Ubuntu
|2|[![](https://acmesh-official.github.io/acmetest/status/debian-latest.svg)](https://github.com/acmesh-official/acmetest#here-are-the-latest-status)| Debian
|3|[![](https://acmesh-official.github.io/acmetest/status/centos-latest.svg)](https://github.com/acmesh-official/acmetest#here-are-the-latest-status)|CentOS
|4|[![](https://acmesh-official.github.io/acmetest/status/windows-cygwin.svg)](https://github.com/acmesh-official/acmetest#here-are-the-latest-status)|Windows (cygwin with curl, openssl and crontab included)
|5|[![](https://acmesh-official.github.io/acmetest/status/freebsd.svg)](https://github.com/acmesh-official/acmetest#here-are-the-latest-status)|FreeBSD
|6|[![](https://acmesh-official.github.io/acmetest/status/pfsense.svg)](https://github.com/acmesh-official/acmetest#here-are-the-latest-status)|pfsense
|7|[![](https://acmesh-official.github.io/acmetest/status/opensuse-leap-latest.svg)](https://github.com/acmesh-official/acmetest#here-are-the-latest-status)|openSUSE
|8|[![](https://acmesh-official.github.io/acmetest/status/alpine-latest.svg)](https://github.com/acmesh-official/acmetest#here-are-the-latest-status)|Alpine Linux (with curl)
|9|[![](https://acmesh-official.github.io/acmetest/status/archlinux-latest.svg)](https://github.com/acmesh-official/acmetest#here-are-the-latest-status)|Archlinux
|10|[![](https://acmesh-official.github.io/acmetest/status/fedora-latest.svg)](https://github.com/acmesh-official/acmetest#here-are-the-latest-status)|fedora
|11|[![](https://acmesh-official.github.io/acmetest/status/kalilinux-kali.svg)](https://github.com/acmesh-official/acmetest#here-are-the-latest-status)|Kali Linux
|12|[![](https://acmesh-official.github.io/acmetest/status/oraclelinux-latest.svg)](https://github.com/acmesh-official/acmetest#here-are-the-latest-status)|Oracle Linux
|13|[![](https://acmesh-official.github.io/acmetest/status/proxmox.svg)](https://github.com/acmesh-official/letest#here-are-the-latest-status)| Proxmox: See Proxmox VE Wiki. Version [4.x, 5.0, 5.1](https://pve.proxmox.com/wiki/HTTPS_Certificate_Configuration_(Version_4.x,_5.0_and_5.1)#Let.27s_Encrypt_using_acme.sh), version [5.2 and up](https://pve.proxmox.com/wiki/Certificate_Management)
|14|-----| Cloud Linux https://github.com/acmesh-official/acme.sh/issues/111
|15|[![](https://acmesh-official.github.io/acmetest/status/openbsd.svg)](https://github.com/acmesh-official/letest#here-are-the-latest-status)|OpenBSD
|16|[![](https://acmesh-official.github.io/acmetest/status/mageia.svg)](https://github.com/acmesh-official/letest#here-are-the-latest-status)|Mageia
|15|[![](https://acmesh-official.github.io/acmetest/status/openbsd.svg)](https://github.com/acmesh-official/acmetest#here-are-the-latest-status)|OpenBSD
|16|[![](https://acmesh-official.github.io/acmetest/status/mageia.svg)](https://github.com/acmesh-official/acmetest#here-are-the-latest-status)|Mageia
|17|-----| OpenWRT: Tested and working. See [wiki page](https://github.com/acmesh-official/acme.sh/wiki/How-to-run-on-OpenWRT)
|18|[![](https://acmesh-official.github.io/acmetest/status/solaris.svg)](https://github.com/acmesh-official/letest#here-are-the-latest-status)|SunOS/Solaris
|19|[![](https://acmesh-official.github.io/acmetest/status/gentoo-stage3-amd64.svg)](https://github.com/acmesh-official/letest#here-are-the-latest-status)|Gentoo Linux
|18|[![](https://acmesh-official.github.io/acmetest/status/solaris.svg)](https://github.com/acmesh-official/acmetest#here-are-the-latest-status)|SunOS/Solaris
|19|[![](https://acmesh-official.github.io/acmetest/status/gentoo-stage3-amd64.svg)](https://github.com/acmesh-official/acmetest#here-are-the-latest-status)|Gentoo Linux
|20|[![Build Status](https://travis-ci.org/acmesh-official/acme.sh.svg?branch=master)](https://travis-ci.org/acmesh-official/acme.sh)|Mac OSX
|21|[![](https://acmesh-official.github.io/acmetest/status/clearlinux-latest.svg)](https://github.com/acmesh-official/letest#here-are-the-latest-status)|ClearLinux
|21|[![](https://acmesh-official.github.io/acmetest/status/clearlinux-latest.svg)](https://github.com/acmesh-official/acmetest#here-are-the-latest-status)|ClearLinux
For all build statuses, check our [weekly build project](https://github.com/acmesh-official/acmetest):
@ -75,6 +82,7 @@ https://github.com/acmesh-official/acmetest
# Supported CA
- Letsencrypt.org CA(default)
- [ZeroSSL.com CA](https://github.com/acmesh-official/acme.sh/wiki/ZeroSSL.com-CA)
- [BuyPass.com CA](https://github.com/acmesh-official/acme.sh/wiki/BuyPass.com-CA)
- [Pebble strict Mode](https://github.com/letsencrypt/pebble)
@ -246,7 +254,7 @@ More examples: https://github.com/acmesh-official/acme.sh/wiki/How-to-issue-a-ce
**(requires you to be root/sudoer, since it is required to interact with Apache server)**
If you are running a web server, Apache or Nginx, it is recommended to use the `Webroot mode`.
If you are running a web server, it is recommended to use the `Webroot mode`.
Particularly, if you are running an Apache server, you can use Apache mode instead. This mode doesn't write any files to your web root folder.
@ -266,7 +274,7 @@ More examples: https://github.com/acmesh-official/acme.sh/wiki/How-to-issue-a-ce
**(requires you to be root/sudoer, since it is required to interact with Nginx server)**
If you are running a web server, Apache or Nginx, it is recommended to use the `Webroot mode`.
If you are running a web server, it is recommended to use the `Webroot mode`.
Particularly, if you are running an nginx server, you can use nginx mode instead. This mode doesn't write any files to your web root folder.
@ -300,7 +308,7 @@ https://github.com/acmesh-official/acme.sh/wiki/dnsapi
See: https://github.com/acmesh-official/acme.sh/wiki/dns-manual-mode first.
If your dns provider doesn't support any api access, you can add the txt record by your hand.
If your dns provider doesn't support any api access, you can add the txt record by hand.
```bash
acme.sh --issue --dns -d example.com -d www.example.com -d cp.example.com

411
acme.sh

@ -1,6 +1,6 @@
#!/usr/bin/env sh
VER=2.8.6
VER=2.8.7
PROJECT_NAME="acme.sh"
@ -23,23 +23,35 @@ _SUB_FOLDERS="$_SUB_FOLDER_DNSAPI $_SUB_FOLDER_DEPLOY $_SUB_FOLDER_NOTIFY"
LETSENCRYPT_CA_V1="https://acme-v01.api.letsencrypt.org/directory"
LETSENCRYPT_STAGING_CA_V1="https://acme-staging.api.letsencrypt.org/directory"
LETSENCRYPT_CA_V2="https://acme-v02.api.letsencrypt.org/directory"
LETSENCRYPT_STAGING_CA_V2="https://acme-staging-v02.api.letsencrypt.org/directory"
CA_LETSENCRYPT_V2="https://acme-v02.api.letsencrypt.org/directory"
CA_LETSENCRYPT_V2_TEST="https://acme-staging-v02.api.letsencrypt.org/directory"
DEFAULT_CA=$LETSENCRYPT_CA_V2
DEFAULT_STAGING_CA=$LETSENCRYPT_STAGING_CA_V2
CA_BUYPASS="https://api.buypass.com/acme/directory"
CA_BUYPASS_TEST="https://api.test4.buypass.no/acme/directory"
CA_ZEROSSL="https://acme.zerossl.com/v2/DV90"
_ZERO_EAB_ENDPOINT="http://api.zerossl.com/acme/eab-credentials-email"
DEFAULT_CA=$CA_LETSENCRYPT_V2
DEFAULT_STAGING_CA=$CA_LETSENCRYPT_V2_TEST
CA_NAMES="
Letsencrypt.org,letsencrypt
Letsencrypt.org_test,letsencrypt_test,letsencrypttest
BuyPass.com,buypass
BuyPass.com_test,buypass_test,buypasstest
ZeroSSL.com,zerossl
"
CA_SERVERS="$CA_LETSENCRYPT_V2,$CA_LETSENCRYPT_V2_TEST,$CA_BUYPASS,$CA_BUYPASS_TEST,$CA_ZEROSSL"
DEFAULT_USER_AGENT="$PROJECT_NAME/$VER ($PROJECT)"
DEFAULT_ACCOUNT_EMAIL=""
DEFAULT_ACCOUNT_KEY_LENGTH=2048
DEFAULT_DOMAIN_KEY_LENGTH=2048
DEFAULT_OPENSSL_BIN="openssl"
_OLD_CA_HOST="https://acme-v01.api.letsencrypt.org"
_OLD_STAGE_CA_HOST="https://acme-staging.api.letsencrypt.org"
VTYPE_HTTP="http-01"
VTYPE_DNS="dns-01"
VTYPE_ALPN="tls-alpn-01"
@ -138,6 +150,12 @@ _NOTIFY_WIKI="https://github.com/acmesh-official/acme.sh/wiki/notify"
_SUDO_WIKI="https://github.com/acmesh-official/acme.sh/wiki/sudo"
_REVOKE_WIKI="https://github.com/acmesh-official/acme.sh/wiki/revokecert"
_ZEROSSL_WIKI="https://github.com/acmesh-official/acme.sh/wiki/ZeroSSL.com-CA"
_SERVER_WIKI="https://github.com/acmesh-official/acme.sh/wiki/Server"
_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"
@ -186,28 +204,28 @@ _dlg_versions() {
if _exists "${ACME_OPENSSL_BIN:-openssl}"; then
${ACME_OPENSSL_BIN:-openssl} version 2>&1
else
echo "$ACME_OPENSSL_BIN doesn't exists."
echo "$ACME_OPENSSL_BIN doesn't exist."
fi
echo "apache:"
if [ "$_APACHECTL" ] && _exists "$_APACHECTL"; then
$_APACHECTL -V 2>&1
else
echo "apache doesn't exists."
echo "apache doesn't exist."
fi
echo "nginx:"
if _exists "nginx"; then
nginx -V 2>&1
else
echo "nginx doesn't exists."
echo "nginx doesn't exist."
fi
echo "socat:"
if _exists "socat"; then
socat -V 2>&1
else
_debug "socat doesn't exists."
_debug "socat doesn't exist."
fi
}
@ -1001,7 +1019,7 @@ _sign() {
_sign_openssl="${ACME_OPENSSL_BIN:-openssl} dgst -sign $keyfile "
if grep "BEGIN RSA PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then
if grep "BEGIN RSA PRIVATE KEY" "$keyfile" >/dev/null 2>&1 || grep "BEGIN PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then
$_sign_openssl -$alg | _base64
elif grep "BEGIN EC PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then
if ! _signedECText="$($_sign_openssl -sha$__ECC_KEY_LEN | ${ACME_OPENSSL_BIN:-openssl} asn1parse -inform DER)"; then
@ -1012,8 +1030,32 @@ _sign() {
fi
_debug3 "_signedECText" "$_signedECText"
_ec_r="$(echo "$_signedECText" | _head_n 2 | _tail_n 1 | cut -d : -f 4 | tr -d "\r\n")"
_debug3 "_ec_r" "$_ec_r"
_ec_s="$(echo "$_signedECText" | _head_n 3 | _tail_n 1 | cut -d : -f 4 | tr -d "\r\n")"
if [ "$__ECC_KEY_LEN" -eq "256" ]; then
while [ "${#_ec_r}" -lt "64" ]; do
_ec_r="0${_ec_r}"
done
while [ "${#_ec_s}" -lt "64" ]; do
_ec_s="0${_ec_s}"
done
fi
if [ "$__ECC_KEY_LEN" -eq "384" ]; then
while [ "${#_ec_r}" -lt "96" ]; do
_ec_r="0${_ec_r}"
done
while [ "${#_ec_s}" -lt "96" ]; do
_ec_s="0${_ec_s}"
done
fi
if [ "$__ECC_KEY_LEN" -eq "512" ]; then
while [ "${#_ec_r}" -lt "132" ]; do
_ec_r="0${_ec_r}"
done
while [ "${#_ec_s}" -lt "132" ]; do
_ec_s="0${_ec_s}"
done
fi
_debug3 "_ec_r" "$_ec_r"
_debug3 "_ec_s" "$_ec_s"
printf "%s" "$_ec_r$_ec_s" | _h2b | _base64
else
@ -1172,9 +1214,8 @@ _createcsr() {
_info "Multi domain" "$alt"
printf -- "\nsubjectAltName=$alt" >>"$csrconf"
fi
if [ "$Le_OCSP_Staple" ] || [ "$Le_OCSP_Stable" ]; then
if [ "$Le_OCSP_Staple" = "1" ]; then
_savedomainconf Le_OCSP_Staple "$Le_OCSP_Staple"
_cleardomainconf Le_OCSP_Stable
printf -- "\nbasicConstraints = CA:FALSE\n1.3.6.1.5.5.7.1.24=DER:30:03:02:01:05" >>"$csrconf"
fi
@ -1482,6 +1523,19 @@ _url_replace() {
tr '/+' '_-' | tr -d '= '
}
#base64 string
_durl_replace_base64() {
_l=$((${#1} % 4))
if [ $_l -eq 2 ]; then
_s="$1"'=='
elif [ $_l -eq 3 ]; then
_s="$1"'='
else
_s="$1"
fi
echo "$_s" | tr '_-' '/+'
}
_time2str() {
#BSD
if date -u -r "$1" 2>/dev/null; then
@ -1985,7 +2039,9 @@ _send_signed_request() {
continue
fi
if [ "$ACME_VERSION" = "2" ]; then
if [ "$url" = "$ACME_NEW_ACCOUNT" ] || [ "$url" = "$ACME_REVOKE_CERT" ]; then
if [ "$url" = "$ACME_NEW_ACCOUNT" ]; then
protected="$JWK_HEADERPLACE_PART1$nonce\", \"url\": \"${url}$JWK_HEADERPLACE_PART2, \"jwk\": $jwk"'}'
elif [ "$url" = "$ACME_REVOKE_CERT" ] && [ "$keyfile" != "$ACCOUNT_KEY_PATH" ]; then
protected="$JWK_HEADERPLACE_PART1$nonce\", \"url\": \"${url}$JWK_HEADERPLACE_PART2, \"jwk\": $jwk"'}'
else
protected="$JWK_HEADERPLACE_PART1$nonce\", \"url\": \"${url}$JWK_HEADERPLACE_PART2, \"kid\": \"${ACCOUNT_URL}\""'}'
@ -2536,17 +2592,18 @@ _initpath() {
CA_HOME="$DEFAULT_CA_HOME"
fi
if [ "$ACME_VERSION" = "2" ]; then
DEFAULT_CA="$LETSENCRYPT_CA_V2"
DEFAULT_STAGING_CA="$LETSENCRYPT_STAGING_CA_V2"
fi
if [ -z "$ACME_DIRECTORY" ]; then
if [ -z "$STAGE" ]; then
ACME_DIRECTORY="$DEFAULT_CA"
else
if [ "$STAGE" ]; then
ACME_DIRECTORY="$DEFAULT_STAGING_CA"
_info "Using stage ACME_DIRECTORY: $ACME_DIRECTORY"
else
default_acme_server=$(_readaccountconf "DEFAULT_ACME_SERVER")
_debug default_acme_server "$default_acme_server"
if [ "$default_acme_server" ]; then
ACME_DIRECTORY="$default_acme_server"
else
ACME_DIRECTORY="$DEFAULT_CA"
fi
fi
fi
@ -2798,10 +2855,10 @@ _setApache() {
apacheVer="$($_APACHECTL -V | grep "Server version:" | cut -d : -f 2 | cut -d " " -f 2 | cut -d '/' -f 2)"
_debug "apacheVer" "$apacheVer"
apacheMajer="$(echo "$apacheVer" | cut -d . -f 1)"
apacheMajor="$(echo "$apacheVer" | cut -d . -f 1)"
apacheMinor="$(echo "$apacheVer" | cut -d . -f 2)"
if [ "$apacheVer" ] && [ "$apacheMajer$apacheMinor" -ge "24" ]; then
if [ "$apacheVer" ] && [ "$apacheMajor$apacheMinor" -ge "24" ]; then
echo "
Alias /.well-known/acme-challenge $ACME_DIR
@ -3379,10 +3436,13 @@ _on_issue_success() {
}
#account_key_length eab-kid eab-hmac-key
registeraccount() {
_reg_length="$1"
_account_key_length="$1"
_eab_id="$2"
_eab_hmac_key="$3"
_initpath
_regAccount "$_reg_length"
_regAccount "$_account_key_length" "$_eab_id" "$_eab_hmac_key"
}
__calcAccountKeyHash() {
@ -3393,10 +3453,27 @@ __calc_account_thumbprint() {
printf "%s" "$jwk" | tr -d ' ' | _digest "sha256" | _url_replace
}
_getAccountEmail() {
if [ "$ACCOUNT_EMAIL" ]; then
echo "$ACCOUNT_EMAIL"
return 0
fi
if [ -z "$CA_EMAIL" ]; then
CA_EMAIL="$(_readcaconf CA_EMAIL)"
fi
if [ "$CA_EMAIL" ]; then
echo "$CA_EMAIL"
return 0
fi
_readaccountconf "ACCOUNT_EMAIL"
}
#keylength
_regAccount() {
_initpath
_reg_length="$1"
_eab_id="$2"
_eab_hmac_key="$3"
_debug3 _regAccount "$_regAccount"
_initAPI
@ -3421,46 +3498,115 @@ _regAccount() {
if ! _calcjwk "$ACCOUNT_KEY_PATH"; then
return 1
fi
if [ "$_eab_id" ] && [ "$_eab_hmac_key" ]; then
_savecaconf CA_EAB_KEY_ID "$_eab_id"
_savecaconf CA_EAB_HMAC_KEY "$_eab_hmac_key"
fi
_eab_id=$(_readcaconf "CA_EAB_KEY_ID")
_eab_hmac_key=$(_readcaconf "CA_EAB_HMAC_KEY")
_secure_debug3 _eab_id "$_eab_id"
_secure_debug3 _eab_hmac_key "$_eab_hmac_key"
_email="$(_getAccountEmail)"
if [ "$_email" ]; then
_savecaconf "CA_EMAIL" "$_email"
fi
if [ "$ACME_VERSION" = "2" ]; then
regjson='{"termsOfServiceAgreed": true}'
if [ "$ACCOUNT_EMAIL" ]; then
regjson='{"contact": ["mailto:'$ACCOUNT_EMAIL'"], "termsOfServiceAgreed": true}'
if [ "$ACME_DIRECTORY" = "$CA_ZEROSSL" ]; then
if [ -z "$_eab_id" ] || [ -z "$_eab_hmac_key" ]; then
_info "No EAB credentials found for ZeroSSL, let's get one"
if [ -z "$_email" ]; then
_err "Please provide a email address for ZeroSSL account."
_err "See ZeroSSL usage: $_ZEROSSL_WIKI"
return 1
fi
_eabresp=$(_post "email=$_email" $_ZERO_EAB_ENDPOINT)
if [ "$?" != "0" ]; then
_debug2 "$_eabresp"
_err "Can not get EAB credentials from ZeroSSL."
return 1
fi
_eab_id="$(echo "$_eabresp" | tr ',}' '\n' | grep '"eab_kid"' | cut -d : -f 2 | tr -d '"')"
if [ -z "$_eab_id" ]; then
_err "Can not resolve _eab_id"
return 1
fi
_eab_hmac_key="$(echo "$_eabresp" | tr ',}' '\n' | grep '"eab_hmac_key"' | cut -d : -f 2 | tr -d '"')"
if [ -z "$_eab_hmac_key" ]; then
_err "Can not resolve _eab_hmac_key"
return 1
fi
_savecaconf CA_EAB_KEY_ID "$_eab_id"
_savecaconf CA_EAB_HMAC_KEY "$_eab_hmac_key"
fi
fi
if [ "$_eab_id" ] && [ "$_eab_hmac_key" ]; then
eab_protected="{\"alg\":\"HS256\",\"kid\":\"$_eab_id\",\"url\":\"${ACME_NEW_ACCOUNT}\"}"
_debug3 eab_protected "$eab_protected"
eab_protected64=$(printf "%s" "$eab_protected" | _base64 | _url_replace)
_debug3 eab_protected64 "$eab_protected64"
eab_payload64=$(printf "%s" "$jwk" | _base64 | _url_replace)
_debug3 eab_payload64 "$eab_payload64"
eab_sign_t="$eab_protected64.$eab_payload64"
_debug3 eab_sign_t "$eab_sign_t"
key_hex="$(_durl_replace_base64 "$_eab_hmac_key" | _dbase64 | _hex_dump | tr -d ' ')"
_debug3 key_hex "$key_hex"
eab_signature=$(printf "%s" "$eab_sign_t" | _hmac sha256 $key_hex | _base64 | _url_replace)
_debug3 eab_signature "$eab_signature"
externalBinding=",\"externalAccountBinding\":{\"protected\":\"$eab_protected64\", \"payload\":\"$eab_payload64\", \"signature\":\"$eab_signature\"}"
_debug3 externalBinding "$externalBinding"
fi
if [ "$_email" ]; then
email_sg="\"contact\": [\"mailto:$_email\"], "
fi
regjson="{$email_sg\"termsOfServiceAgreed\": true$externalBinding}"
else
_reg_res="$ACME_NEW_ACCOUNT_RES"
regjson='{"resource": "'$_reg_res'", "terms-of-service-agreed": true, "agreement": "'$ACME_AGREEMENT'"}'
if [ "$ACCOUNT_EMAIL" ]; then
regjson='{"resource": "'$_reg_res'", "contact": ["mailto:'$ACCOUNT_EMAIL'"], "terms-of-service-agreed": true, "agreement": "'$ACME_AGREEMENT'"}'
if [ "$_email" ]; then
regjson='{"resource": "'$_reg_res'", "contact": ["mailto:'$_email'"], "terms-of-service-agreed": true, "agreement": "'$ACME_AGREEMENT'"}'
fi
fi
_info "Registering account"
_info "Registering account: $ACME_DIRECTORY"
if ! _send_signed_request "${ACME_NEW_ACCOUNT}" "$regjson"; then
_err "Register account Error: $response"
return 1
fi
_eabAlreadyBound=""
if [ "$code" = "" ] || [ "$code" = '201' ]; then
echo "$response" >"$ACCOUNT_JSON_PATH"
_info "Registered"
elif [ "$code" = '409' ] || [ "$code" = '200' ]; then
_info "Already registered"
elif [ "$code" = '400' ] && _contains "$response" 'The account is not awaiting external account binding'; then
_info "Already register EAB."
_eabAlreadyBound=1
else
_err "Register account Error: $response"
return 1
fi
_debug2 responseHeaders "$responseHeaders"
_accUri="$(echo "$responseHeaders" | grep -i "^Location:" | _head_n 1 | cut -d ':' -f 2- | tr -d "\r\n ")"
_debug "_accUri" "$_accUri"
if [ -z "$_accUri" ]; then
_err "Can not find account id url."
_err "$responseHeaders"
return 1
if [ -z "$_eabAlreadyBound" ]; then
_debug2 responseHeaders "$responseHeaders"
_accUri="$(echo "$responseHeaders" | grep -i "^Location:" | _head_n 1 | cut -d ':' -f 2- | tr -d "\r\n ")"
_debug "_accUri" "$_accUri"
if [ -z "$_accUri" ]; then
_err "Can not find account id url."
_err "$responseHeaders"
return 1
fi
_savecaconf "ACCOUNT_URL" "$_accUri"
else
ACCOUNT_URL="$(_readcaconf ACCOUNT_URL)"
fi
_savecaconf "ACCOUNT_URL" "$_accUri"
export ACCOUNT_URL="$_accUri"
CA_KEY_HASH="$(__calcAccountKeyHash)"
@ -3509,9 +3655,12 @@ updateaccount() {
fi
_initAPI
_email="$(_getAccountEmail)"
if [ "$ACME_VERSION" = "2" ]; then
if [ "$ACCOUNT_EMAIL" ]; then
updjson='{"contact": ["mailto:'$ACCOUNT_EMAIL'"]}'
updjson='{"contact": ["mailto:'$_email'"]}'
else
updjson='{"contact": []}'
fi
else
# ACMEv1: Updates happen the same way a registration is done.
@ -3931,13 +4080,10 @@ issue() {
_cleardomainconf "Le_ChallengeAlias"
fi
if [ "$ACME_DIRECTORY" != "$DEFAULT_CA" ]; then
Le_API="$ACME_DIRECTORY"
_savedomainconf "Le_API" "$Le_API"
else
_cleardomainconf Le_API
fi
Le_API="$ACME_DIRECTORY"
_savedomainconf "Le_API" "$Le_API"
_info "Using CA: $ACME_DIRECTORY"
if [ "$_alt_domains" = "$NO_VALUE" ]; then
_alt_domains=""
fi
@ -4093,17 +4239,17 @@ $_authorizations_map"
if [ "$ACME_VERSION" = "2" ]; then
_idn_d="$(_idn "$d")"
_candindates="$(echo "$_authorizations_map" | grep -i "^$_idn_d,")"
_debug2 _candindates "$_candindates"
if [ "$(echo "$_candindates" | wc -l)" -gt 1 ]; then
for _can in $_candindates; do
_candidates="$(echo "$_authorizations_map" | grep -i "^$_idn_d,")"
_debug2 _candidates "$_candidates"
if [ "$(echo "$_candidates" | wc -l)" -gt 1 ]; then
for _can in $_candidates; do
if _startswith "$(echo "$_can" | tr '.' '|')" "$(echo "$_idn_d" | tr '.' '|'),"; then
_candindates="$_can"
_candidates="$_can"
break
fi
done
fi
response="$(echo "$_candindates" | sed "s/$_idn_d,//")"
response="$(echo "$_candidates" | sed "s/$_idn_d,//")"
_debug2 "response" "$response"
if [ -z "$response" ]; then
_err "get to authz error."
@ -4294,7 +4440,7 @@ $_authorizations_map"
if [ "$dns_entries" ]; then
if [ -z "$Le_DNSSleep" ]; then
_info "Let's check each dns records now. Sleep 20 seconds first."
_info "Let's check each DNS record now. Sleep 20 seconds first."
_sleep 20
if ! _check_dns_entries; then
_err "check dns error."
@ -4563,7 +4709,14 @@ $_authorizations_map"
break
elif _contains "$response" "\"processing\""; then
_info "Order status is processing, lets sleep and retry."
_sleep 2
_retryafter=$(echo "$responseHeaders" | grep -i "^Retry-After *:" | cut -d : -f 2 | tr -d ' ' | tr -d '\r')
_debug "_retryafter" "$_retryafter"
if [ "$_retryafter" ]; then
_info "Retry after: $_retryafter"
_sleep $_retryafter
else
_sleep 2
fi
else
_err "Sign error, wrong status"
_err "$response"
@ -4816,14 +4969,6 @@ renew() {
fi
if [ "$Le_API" ]; then
if [ "$_OLD_CA_HOST" = "$Le_API" ]; then
export Le_API="$DEFAULT_CA"
_savedomainconf Le_API "$Le_API"
fi
if [ "$_OLD_STAGE_CA_HOST" = "$Le_API" ]; then
export Le_API="$DEFAULT_STAGING_CA"
_savedomainconf Le_API "$Le_API"
fi
export ACME_DIRECTORY="$Le_API"
#reload ca configs
ACCOUNT_KEY_PATH=""
@ -5089,7 +5234,7 @@ list() {
_sep="|"
if [ "$_raw" ]; then
printf "%s\n" "Main_Domain${_sep}KeyLength${_sep}SAN_Domains${_sep}Created${_sep}Renew"
printf "%s\n" "Main_Domain${_sep}KeyLength${_sep}SAN_Domains${_sep}CA${_sep}Created${_sep}Renew"
for di in "${CERT_HOME}"/*.*/; do
d=$(basename "$di")
_debug d "$d"
@ -5101,7 +5246,8 @@ list() {
DOMAIN_CONF="$di/$d.conf"
if [ -f "$DOMAIN_CONF" ]; then
. "$DOMAIN_CONF"
printf "%s\n" "$Le_Domain${_sep}\"$Le_Keylength\"${_sep}$Le_Alt${_sep}$Le_CertCreateTimeStr${_sep}$Le_NextRenewTimeStr"
_ca="$(_getCAShortName "$Le_API")"
printf "%s\n" "$Le_Domain${_sep}\"$Le_Keylength\"${_sep}$Le_Alt${_sep}$_ca${_sep}$Le_CertCreateTimeStr${_sep}$Le_NextRenewTimeStr"
fi
)
done
@ -5454,6 +5600,7 @@ uninstallcronjob() {
}
#domain isECC revokeReason
revoke() {
Le_Domain="$1"
if [ -z "$Le_Domain" ]; then
@ -5462,7 +5609,10 @@ revoke() {
fi
_isEcc="$2"
_reason="$3"
if [ -z "$_reason" ]; then
_reason="0"
fi
_initpath "$Le_Domain" "$_isEcc"
if [ ! -f "$DOMAIN_CONF" ]; then
_err "$Le_Domain is not a issued domain, skip."
@ -5484,7 +5634,7 @@ revoke() {
_initAPI
if [ "$ACME_VERSION" = "2" ]; then
data="{\"certificate\": \"$cert\"}"
data="{\"certificate\": \"$cert\",\"reason\":$_reason}"
else
data="{\"resource\": \"revoke-cert\", \"certificate\": \"$cert\"}"
fi
@ -5503,7 +5653,7 @@ revoke() {
fi
fi
else
_info "Domain key file doesn't exists."
_info "Domain key file doesn't exist."
fi
_info "Try account key."
@ -5602,7 +5752,7 @@ _deactivate() {
_URL_NAME="uri"
fi
entries="$(echo "$response" | _egrep_o "{ *\"type\":\"[^\"]*\", *\"status\": *\"valid\", *\"$_URL_NAME\"[^}]*")"
entries="$(echo "$response" | _egrep_o "[^{]*\"type\":\"[^\"]*\", *\"status\": *\"valid\", *\"$_URL_NAME\"[^}]*")"
if [ -z "$entries" ]; then
_info "No valid entries found."
if [ -z "$thumbprint" ]; then
@ -6214,6 +6364,7 @@ Commands:
--createCSR, -ccsr Create CSR , professional use.
--deactivate Deactivate the domain authz, professional use.
--set-notify Set the cron notification hook, level or mode.
--set-default-ca Used with '--server' , to set the default CA to use to use.
Parameters:
@ -6238,6 +6389,10 @@ Parameters:
--log-level 1|2 Specifies the log level, default is 1.
--syslog [0|3|6|7] Syslog level, 0: disable syslog, 3: error, 6: info, 7: debug.
--eab-kid EAB_KID Key Identifier for External Account Binding.
--eab-hmac-key EAB_HMAC_KEY HMAC key for External Account Binding.
These parameters are to install the cert to nginx/apache or any other server after issue/renew a cert:
--cert-file After issue/renew, the cert will be copied to this path.
@ -6247,13 +6402,13 @@ Parameters:
--reloadcmd \"service nginx reload\" After issue/renew, it's used to reload the server.
--server SERVER ACME Directory Resource URI. (default: $DEFAULT_CA)
--server SERVER ACME Directory Resource URI. See: $_SERVER_WIKI (default: $DEFAULT_CA)
--accountconf Specifies a customized account config file.
--home Specifies the home dir for $PROJECT_NAME.
--cert-home Specifies the home dir to save all the certs, only valid for '--install' command.
--config-home Specifies the home dir to save all the configurations.
--useragent Specifies the user agent string. it will be saved for future use too.
--accountemail Specifies the account email, only valid for the '--install' and '--update-account' command.
--accountemail, -m Specifies the account email, only valid for the '--install' and '--update-account' command.
--accountkey Specifies the account key path, only valid for the '--install' command.
--days Specifies the days to renew the cert when using '--issue' command. The default value is $DEFAULT_RENEW days.
--httpport Specifies the standalone listening port. Only valid if the server is behind a reverse proxy or load balancer.
@ -6293,6 +6448,7 @@ Parameters:
0: Bulk mode. Send all the domain's notifications in one message(mail).
1: Cert mode. Send a message for every single cert.
--notify-hook [hookname] Set the notify hook
--revoke-reason [0-10] The reason for '--revoke' command. See: $_REVOKE_WIKI
"
}
@ -6375,12 +6531,6 @@ _processAccountConf() {
_saveaccountconf "USER_AGENT" "$USER_AGENT"
fi
if [ "$_accountemail" ]; then
_saveaccountconf "ACCOUNT_EMAIL" "$_accountemail"
elif [ "$ACCOUNT_EMAIL" ] && [ "$ACCOUNT_EMAIL" != "$DEFAULT_ACCOUNT_EMAIL" ]; then
_saveaccountconf "ACCOUNT_EMAIL" "$ACCOUNT_EMAIL"
fi
if [ "$_openssl_bin" ]; then
_saveaccountconf "ACME_OPENSSL_BIN" "$_openssl_bin"
elif [ "$ACME_OPENSSL_BIN" ] && [ "$ACME_OPENSSL_BIN" != "$DEFAULT_OPENSSL_BIN" ]; then
@ -6407,10 +6557,10 @@ _checkSudo() {
#it's root using sudo, no matter it's using sudo or not, just fine
return 0
fi
if [ "$SUDO_COMMAND" = "/bin/su" ] || [ "$SUDO_COMMAND" = "/bin/bash" ]; then
if [ -n "$SUDO_COMMAND" ]; then
#it's a normal user doing "sudo su", or `sudo -i` or `sudo -s`
#fine
return 0
_endswith "$SUDO_COMMAND" /bin/su || grep "^$SUDO_COMMAND\$" /etc/shells >/dev/null 2>&1
return $?
fi
#otherwise
return 1
@ -6418,6 +6568,61 @@ _checkSudo() {
return 0
}
#server
_selectServer() {
_server="$1"
_server_lower="$(echo "$_server" | _lower_case)"
_sindex=0
for snames in $CA_NAMES; do
snames="$(echo "$snames" | _lower_case)"
_sindex="$(_math $_sindex + 1)"
_debug2 "_selectServer try snames" "$snames"
for sname in $(echo "$snames" | tr ',' ' '); do
if [ "$_server_lower" = "$sname" ]; then
_debug2 "_selectServer match $sname"
_serverdir="$(_getfield "$CA_SERVERS" $_sindex)"
_debug "Selected server: $_serverdir"
ACME_DIRECTORY="$_serverdir"
export ACME_DIRECTORY
return
fi
done
done
ACME_DIRECTORY="$_server"
export ACME_DIRECTORY
}
#url
_getCAShortName() {
caurl="$1"
caurl_lower="$(echo $caurl | _lower_case)"
_sindex=0
for surl in $(echo "$CA_SERVERS" | _lower_case | tr , ' '); do
_sindex="$(_math $_sindex + 1)"
if [ "$caurl_lower" = "$surl" ]; then
_nindex=0
for snames in $CA_NAMES; do
_nindex="$(_math $_nindex + 1)"
if [ $_nindex -ge $_sindex ]; then
_getfield "$snames" 1
return
fi
done
fi
done
echo "$caurl"
}
#set default ca to $ACME_DIRECTORY
setdefaultca() {
if [ -z "$ACME_DIRECTORY" ]; then
_err "Please give a --server parameter."
return 1
fi
_saveaccountconf "DEFAULT_ACME_SERVER" "$ACME_DIRECTORY"
_info "Changed default CA to: $(__green "$ACME_DIRECTORY")"
}
_process() {
_CMD=""
_domain=""
@ -6468,6 +6673,9 @@ _process() {
_notify_hook=""
_notify_level=""
_notify_mode=""
_revoke_reason=""
_eab_kid=""
_eab_hmac_key=""
while [ ${#} -gt 0 ]; do
case "${1}" in
@ -6557,6 +6765,9 @@ _process() {
--set-notify)
_CMD="setnotify"
;;
--set-default-ca)
_CMD="setdefaultca"
;;
--domain | -d)
_dvalue="$2"
@ -6595,9 +6806,8 @@ _process() {
STAGE="1"
;;
--server)
ACME_DIRECTORY="$2"
_server="$ACME_DIRECTORY"
export ACME_DIRECTORY
_server="$2"
_selectServer "$_server"
shift
;;
--debug)
@ -6754,7 +6964,7 @@ _process() {
USER_AGENT="$_useragent"
shift
;;
--accountemail)
--accountemail | -m)
_accountemail="$2"
ACCOUNT_EMAIL="$_accountemail"
shift
@ -6940,6 +7150,22 @@ _process() {
_notify_mode="$_nmode"
shift
;;
--revoke-reason)
_revoke_reason="$2"
if _startswith "$_revoke_reason" "-"; then
_err "'$_revoke_reason' is not a integer for '$1'"
return 1
fi
shift
;;
--eab-kid)
_eab_kid="$2"
shift
;;
--eab-hmac-key)
_eab_hmac_key="$2"
shift
;;
*)
_err "Unknown parameter : $1"
return 1
@ -7027,7 +7253,7 @@ _process() {
renewAll "$_stopRenewOnError"
;;
revoke)
revoke "$_domain" "$_ecc"
revoke "$_domain" "$_ecc" "$_revoke_reason"
;;
remove)
remove "$_domain" "$_ecc"
@ -7036,7 +7262,7 @@ _process() {
deactivate "$_domain,$_altdomains"
;;
registeraccount)
registeraccount "$_accountkeylength"
registeraccount "$_accountkeylength" "$_eab_kid" "$_eab_hmac_key"
;;
updateaccount)
updateaccount
@ -7068,6 +7294,9 @@ _process() {
setnotify)
setnotify "$_notify_hook" "$_notify_level" "$_notify_mode"
;;
setdefaultca)
setdefaultca
;;
*)
if [ "$_CMD" ]; then
_err "Invalid command: $_CMD"

2
deploy/docker.sh

@ -91,7 +91,7 @@ docker_deploy() {
_getdeployconf DEPLOY_DOCKER_CONTAINER_RELOAD_CMD
_debug2 DEPLOY_DOCKER_CONTAINER_RELOAD_CMD "$DEPLOY_DOCKER_CONTAINER_RELOAD_CMD"
if [ "$DEPLOY_DOCKER_CONTAINER_RELOAD_CMD" ]; then
_savedeployconf DEPLOY_DOCKER_CONTAINER_RELOAD_CMD "$DEPLOY_DOCKER_CONTAINER_RELOAD_CMD"
_savedeployconf DEPLOY_DOCKER_CONTAINER_RELOAD_CMD "$DEPLOY_DOCKER_CONTAINER_RELOAD_CMD" "base64"
fi
_cid="$(_get_id "$DEPLOY_DOCKER_CONTAINER_LABEL")"

1
deploy/haproxy.sh

@ -233,7 +233,6 @@ haproxy_deploy() {
-header Host${_header_sep}\"${_ocsp_host}\" \
-respout \"${_ocsp}\" \
-verify_other \"${_issuer}\" \
-no_nonce \
${_cafile_argument} \
| grep -q \"${_pem}: good\""
_debug _openssl_ocsp_cmd "${_openssl_ocsp_cmd}"

4
deploy/kong.sh

@ -1,6 +1,6 @@
#!/usr/bin/env sh
# If certificate already exist it will update only cert and key not touching other parameter
# If certificate doesn't exist it will only upload cert and key and not set other parameter
# If certificate already exists it will update only cert and key, not touching other parameters
# If certificate doesn't exist it will only upload cert and key, and not set other parameters
# Note that we deploy full chain
# Written by Geoffroi Genot <ggenot@voxbone.com>

262
deploy/openstack.sh

@ -0,0 +1,262 @@
#!/usr/bin/env sh
# OpenStack Barbican deploy hook
#
# This requires you to have OpenStackClient and python-barbicanclient
# installed.
#
# You will require Keystone V3 credentials loaded into your environment, which
# could be either password or v3applicationcredential type.
#
# Author: Andy Botting <andy@andybotting.com>
openstack_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 openstack; then
_err "OpenStack client not found"
return 1
fi
_openstack_credentials || return $?
_info "Generate import pkcs12"
_import_pkcs12="$(_mktemp)"
if ! _openstack_to_pkcs "$_import_pkcs12" "$_ckey" "$_ccert" "$_cca"; then
_err "Error creating pkcs12 certificate"
return 1
fi
_debug _import_pkcs12 "$_import_pkcs12"
_base64_pkcs12=$(_base64 "multiline" <"$_import_pkcs12")
secretHrefs=$(_openstack_get_secrets)
_debug secretHrefs "$secretHrefs"
_openstack_store_secret || return $?
if [ -n "$secretHrefs" ]; then
_info "Cleaning up existing secret"
_openstack_delete_secrets || return $?
fi
_info "Certificate successfully deployed"
return 0
}
_openstack_store_secret() {
if ! openstack secret store --name "$_cdomain." -t 'application/octet-stream' -e base64 --payload "$_base64_pkcs12"; then
_err "Failed to create OpenStack secret"
return 1
fi
return
}
_openstack_delete_secrets() {
echo "$secretHrefs" | while read -r secretHref; do
_info "Deleting old secret $secretHref"
if ! openstack secret delete "$secretHref"; then
_err "Failed to delete OpenStack secret"
return 1
fi
done
return
}
_openstack_get_secrets() {
if ! secretHrefs=$(openstack secret list -f value --name "$_cdomain." | cut -d' ' -f1); then
_err "Failed to list secrets"
return 1
fi
echo "$secretHrefs"
}
_openstack_to_pkcs() {
# The existing _toPkcs command can't allow an empty password, due to sh
# -z test, so copied here and forcing the empty password.
_cpfx="$1"
_ckey="$2"
_ccert="$3"
_cca="$4"
${ACME_OPENSSL_BIN:-openssl} pkcs12 -export -out "$_cpfx" -inkey "$_ckey" -in "$_ccert" -certfile "$_cca" -password "pass:"
}
_openstack_credentials() {
_debug "Check OpenStack credentials"
# If we have OS_AUTH_URL already set in the environment, then assume we want
# to use those, otherwise use stored credentials
if [ -n "$OS_AUTH_URL" ]; then
_debug "OS_AUTH_URL env var found, using environment"
else
_debug "OS_AUTH_URL not found, loading stored credentials"
OS_AUTH_URL="${OS_AUTH_URL:-$(_readaccountconf_mutable OS_AUTH_URL)}"
OS_IDENTITY_API_VERSION="${OS_IDENTITY_API_VERSION:-$(_readaccountconf_mutable OS_IDENTITY_API_VERSION)}"
OS_AUTH_TYPE="${OS_AUTH_TYPE:-$(_readaccountconf_mutable OS_AUTH_TYPE)}"
OS_APPLICATION_CREDENTIAL_ID="${OS_APPLICATION_CREDENTIAL_ID:-$(_readaccountconf_mutable OS_APPLICATION_CREDENTIAL_ID)}"
OS_APPLICATION_CREDENTIAL_SECRET="${OS_APPLICATION_CREDENTIAL_SECRET:-$(_readaccountconf_mutable OS_APPLICATION_CREDENTIAL_SECRET)}"
OS_USERNAME="${OS_USERNAME:-$(_readaccountconf_mutable OS_USERNAME)}"
OS_PASSWORD="${OS_PASSWORD:-$(_readaccountconf_mutable OS_PASSWORD)}"
OS_PROJECT_NAME="${OS_PROJECT_NAME:-$(_readaccountconf_mutable OS_PROJECT_NAME)}"
OS_PROJECT_ID="${OS_PROJECT_ID:-$(_readaccountconf_mutable OS_PROJECT_ID)}"
OS_USER_DOMAIN_NAME="${OS_USER_DOMAIN_NAME:-$(_readaccountconf_mutable OS_USER_DOMAIN_NAME)}"
OS_USER_DOMAIN_ID="${OS_USER_DOMAIN_ID:-$(_readaccountconf_mutable OS_USER_DOMAIN_ID)}"
OS_PROJECT_DOMAIN_NAME="${OS_PROJECT_DOMAIN_NAME:-$(_readaccountconf_mutable OS_PROJECT_DOMAIN_NAME)}"
OS_PROJECT_DOMAIN_ID="${OS_PROJECT_DOMAIN_ID:-$(_readaccountconf_mutable OS_PROJECT_DOMAIN_ID)}"
fi
# Check each var and either save or clear it depending on whether its set.
# The helps us clear out old vars in the case where a user may want
# to switch between password and app creds
_debug "OS_AUTH_URL" "$OS_AUTH_URL"
if [ -n "$OS_AUTH_URL" ]; then
export OS_AUTH_URL
_saveaccountconf_mutable OS_AUTH_URL "$OS_AUTH_URL"
else
unset OS_AUTH_URL
_clearaccountconf SAVED_OS_AUTH_URL
fi
_debug "OS_IDENTITY_API_VERSION" "$OS_IDENTITY_API_VERSION"
if [ -n "$OS_IDENTITY_API_VERSION" ]; then
export OS_IDENTITY_API_VERSION
_saveaccountconf_mutable OS_IDENTITY_API_VERSION "$OS_IDENTITY_API_VERSION"
else
unset OS_IDENTITY_API_VERSION
_clearaccountconf SAVED_OS_IDENTITY_API_VERSION
fi
_debug "OS_AUTH_TYPE" "$OS_AUTH_TYPE"
if [ -n "$OS_AUTH_TYPE" ]; then
export OS_AUTH_TYPE
_saveaccountconf_mutable OS_AUTH_TYPE "$OS_AUTH_TYPE"
else
unset OS_AUTH_TYPE
_clearaccountconf SAVED_OS_AUTH_TYPE
fi
_debug "OS_APPLICATION_CREDENTIAL_ID" "$OS_APPLICATION_CREDENTIAL_ID"
if [ -n "$OS_APPLICATION_CREDENTIAL_ID" ]; then
export OS_APPLICATION_CREDENTIAL_ID
_saveaccountconf_mutable OS_APPLICATION_CREDENTIAL_ID "$OS_APPLICATION_CREDENTIAL_ID"
else
unset OS_APPLICATION_CREDENTIAL_ID
_clearaccountconf SAVED_OS_APPLICATION_CREDENTIAL_ID
fi
_secure_debug "OS_APPLICATION_CREDENTIAL_SECRET" "$OS_APPLICATION_CREDENTIAL_SECRET"
if [ -n "$OS_APPLICATION_CREDENTIAL_SECRET" ]; then
export OS_APPLICATION_CREDENTIAL_SECRET
_saveaccountconf_mutable OS_APPLICATION_CREDENTIAL_SECRET "$OS_APPLICATION_CREDENTIAL_SECRET"
else
unset OS_APPLICATION_CREDENTIAL_SECRET
_clearaccountconf SAVED_OS_APPLICATION_CREDENTIAL_SECRET
fi
_debug "OS_USERNAME" "$OS_USERNAME"
if [ -n "$OS_USERNAME" ]; then
export OS_USERNAME
_saveaccountconf_mutable OS_USERNAME "$OS_USERNAME"
else
unset OS_USERNAME
_clearaccountconf SAVED_OS_USERNAME
fi
_secure_debug "OS_PASSWORD" "$OS_PASSWORD"
if [ -n "$OS_PASSWORD" ]; then
export OS_PASSWORD
_saveaccountconf_mutable OS_PASSWORD "$OS_PASSWORD"
else
unset OS_PASSWORD
_clearaccountconf SAVED_OS_PASSWORD
fi
_debug "OS_PROJECT_NAME" "$OS_PROJECT_NAME"
if [ -n "$OS_PROJECT_NAME" ]; then
export OS_PROJECT_NAME
_saveaccountconf_mutable OS_PROJECT_NAME "$OS_PROJECT_NAME"
else
unset OS_PROJECT_NAME
_clearaccountconf SAVED_OS_PROJECT_NAME
fi
_debug "OS_PROJECT_ID" "$OS_PROJECT_ID"
if [ -n "$OS_PROJECT_ID" ]; then
export OS_PROJECT_ID
_saveaccountconf_mutable OS_PROJECT_ID "$OS_PROJECT_ID"
else
unset OS_PROJECT_ID
_clearaccountconf SAVED_OS_PROJECT_ID
fi
_debug "OS_USER_DOMAIN_NAME" "$OS_USER_DOMAIN_NAME"
if [ -n "$OS_USER_DOMAIN_NAME" ]; then
export OS_USER_DOMAIN_NAME
_saveaccountconf_mutable OS_USER_DOMAIN_NAME "$OS_USER_DOMAIN_NAME"
else
unset OS_USER_DOMAIN_NAME
_clearaccountconf SAVED_OS_USER_DOMAIN_NAME
fi
_debug "OS_USER_DOMAIN_ID" "$OS_USER_DOMAIN_ID"
if [ -n "$OS_USER_DOMAIN_ID" ]; then
export OS_USER_DOMAIN_ID
_saveaccountconf_mutable OS_USER_DOMAIN_ID "$OS_USER_DOMAIN_ID"
else
unset OS_USER_DOMAIN_ID
_clearaccountconf SAVED_OS_USER_DOMAIN_ID
fi
_debug "OS_PROJECT_DOMAIN_NAME" "$OS_PROJECT_DOMAIN_NAME"
if [ -n "$OS_PROJECT_DOMAIN_NAME" ]; then
export OS_PROJECT_DOMAIN_NAME
_saveaccountconf_mutable OS_PROJECT_DOMAIN_NAME "$OS_PROJECT_DOMAIN_NAME"
else
unset OS_PROJECT_DOMAIN_NAME
_clearaccountconf SAVED_OS_PROJECT_DOMAIN_NAME
fi
_debug "OS_PROJECT_DOMAIN_ID" "$OS_PROJECT_DOMAIN_ID"
if [ -n "$OS_PROJECT_DOMAIN_ID" ]; then
export OS_PROJECT_DOMAIN_ID
_saveaccountconf_mutable OS_PROJECT_DOMAIN_ID "$OS_PROJECT_DOMAIN_ID"
else
unset OS_PROJECT_DOMAIN_ID
_clearaccountconf SAVED_OS_PROJECT_DOMAIN_ID
fi
if [ "$OS_AUTH_TYPE" = "v3applicationcredential" ]; then
# Application Credential auth
if [ -z "$OS_APPLICATION_CREDENTIAL_ID" ] || [ -z "$OS_APPLICATION_CREDENTIAL_SECRET" ]; then
_err "When using OpenStack application credentials, OS_APPLICATION_CREDENTIAL_ID"
_err "and OS_APPLICATION_CREDENTIAL_SECRET must be set."
_err "Please check your credentials and try again."
return 1
fi
else
# Password auth
if [ -z "$OS_USERNAME" ] || [ -z "$OS_PASSWORD" ]; then
_err "OpenStack username or password not found."
_err "Please check your credentials and try again."
return 1
fi
if [ -z "$OS_PROJECT_NAME" ] && [ -z "$OS_PROJECT_ID" ]; then
_err "When using password authentication, OS_PROJECT_NAME or"
_err "OS_PROJECT_ID must be set."
_err "Please check your credentials and try again."
return 1
fi
fi
return 0
}

139
deploy/ssh.sh

@ -12,7 +12,7 @@
# Only a username is required. All others are optional.
#
# The following examples are for QNAP NAS running QTS 4.2
# export DEPLOY_SSH_CMD="" # defaults to ssh
# export DEPLOY_SSH_CMD="" # defaults to "ssh -T"
# export DEPLOY_SSH_USER="admin" # required
# export DEPLOY_SSH_SERVER="qnap" # defaults to domain name
# export DEPLOY_SSH_KEYFILE="/etc/stunnel/stunnel.pem"
@ -20,7 +20,9 @@
# export DEPLOY_SSH_CAFILE="/etc/stunnel/uca.pem"
# export DEPLOY_SSH_FULLCHAIN=""
# export DEPLOY_SSH_REMOTE_CMD="/etc/init.d/stunnel.sh restart"
# export DEPLOY_SSH_BACKUP="" # yes or no, default to yes
# export DEPLOY_SSH_BACKUP="" # yes or no, default to yes or previously saved value
# export DEPLOY_SSH_BACKUP_PATH=".acme_ssh_deploy" # path on remote system. Defaults to .acme_ssh_deploy
# export DEPLOY_SSH_MULTI_CALL="" # yes or no, default to no or previously saved value
#
######## Public functions #####################
@ -31,10 +33,7 @@ ssh_deploy() {
_ccert="$3"
_cca="$4"
_cfullchain="$5"
_cmdstr=""
_homedir='~'
_backupprefix="$_homedir/.acme_ssh_deploy/$_cdomain-backup"
_backupdir="$_backupprefix-$(_utc_date | tr ' ' '-')"
_deploy_ssh_servers=""
if [ -f "$DOMAIN_CONF" ]; then
# shellcheck disable=SC1090
@ -71,18 +70,74 @@ ssh_deploy() {
Le_Deploy_ssh_cmd="$DEPLOY_SSH_CMD"
_savedomainconf Le_Deploy_ssh_cmd "$Le_Deploy_ssh_cmd"
elif [ -z "$Le_Deploy_ssh_cmd" ]; then
Le_Deploy_ssh_cmd="ssh"
Le_Deploy_ssh_cmd="ssh -T"
fi
# BACKUP is optional. If not provided then default to yes
# BACKUP is optional. If not provided then default to previously saved value or yes.
if [ "$DEPLOY_SSH_BACKUP" = "no" ]; then
Le_Deploy_ssh_backup="no"
elif [ -z "$Le_Deploy_ssh_backup" ]; then
elif [ -z "$Le_Deploy_ssh_backup" ] || [ "$DEPLOY_SSH_BACKUP" = "yes" ]; then
Le_Deploy_ssh_backup="yes"
fi
_savedomainconf Le_Deploy_ssh_backup "$Le_Deploy_ssh_backup"
# BACKUP_PATH is optional. If not provided then default to previously saved value or .acme_ssh_deploy
if [ -n "$DEPLOY_SSH_BACKUP_PATH" ]; then
Le_Deploy_ssh_backup_path="$DEPLOY_SSH_BACKUP_PATH"
elif [ -z "$Le_Deploy_ssh_backup_path" ]; then
Le_Deploy_ssh_backup_path=".acme_ssh_deploy"
fi
_savedomainconf Le_Deploy_ssh_backup_path "$Le_Deploy_ssh_backup_path"
# MULTI_CALL is optional. If not provided then default to previously saved
# value (which may be undefined... equivalent to "no").
if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
Le_Deploy_ssh_multi_call="yes"
_savedomainconf Le_Deploy_ssh_multi_call "$Le_Deploy_ssh_multi_call"
elif [ "$DEPLOY_SSH_MULTI_CALL" = "no" ]; then
Le_Deploy_ssh_multi_call=""
_cleardomainconf Le_Deploy_ssh_multi_call
fi
_deploy_ssh_servers=$Le_Deploy_ssh_server
for Le_Deploy_ssh_server in $_deploy_ssh_servers; do
_ssh_deploy
done
}
_ssh_deploy() {
_err_code=0
_cmdstr=""
_backupprefix=""
_backupdir=""
_info "Deploy certificates to remote server $Le_Deploy_ssh_user@$Le_Deploy_ssh_server"
if [ "$Le_Deploy_ssh_multi_call" = "yes" ]; then
_info "Using MULTI_CALL mode... Required commands sent in multiple calls to remote host"
else
_info "Required commands batched and sent in single call to remote host"
fi
if [ "$Le_Deploy_ssh_backup" = "yes" ]; then
_backupprefix="$Le_Deploy_ssh_backup_path/$_cdomain-backup"
_backupdir="$_backupprefix-$(_utc_date | tr ' ' '-')"
# run cleanup on the backup directory, erase all older
# than 180 days (15552000 seconds).
_cmdstr="{ now=\"\$(date -u +%s)\"; for fn in $_backupprefix*; \
do if [ -d \"\$fn\" ] && [ \"\$(expr \$now - \$(date -ur \$fn +%s) )\" -ge \"15552000\" ]; \
then rm -rf \"\$fn\"; echo \"Backup \$fn deleted as older than 180 days\"; fi; done; }; $_cmdstr"
# Alternate version of above... _cmdstr="find $_backupprefix* -type d -mtime +180 2>/dev/null | xargs rm -rf; $_cmdstr"
# Create our backup directory for overwritten cert files.
_cmdstr="mkdir -p $_backupdir; $_cmdstr"
_info "Backup of old certificate files will be placed in remote directory $_backupdir"
_info "Backup directories erased after 180 days."
if [ "$Le_Deploy_ssh_multi_call" = "yes" ]; then
if ! _ssh_remote_cmd "$_cmdstr"; then
return $_err_code
fi
_cmdstr=""
fi
fi
# KEYFILE is optional.
# If provided then private key will be copied to provided filename.
@ -98,6 +153,12 @@ ssh_deploy() {
# copy new certificate into file.
_cmdstr="$_cmdstr echo \"$(cat "$_ckey")\" > $Le_Deploy_ssh_keyfile;"
_info "will copy private key to remote file $Le_Deploy_ssh_keyfile"
if [ "$Le_Deploy_ssh_multi_call" = "yes" ]; then
if ! _ssh_remote_cmd "$_cmdstr"; then
return $_err_code
fi
_cmdstr=""
fi
fi
# CERTFILE is optional.
@ -118,6 +179,12 @@ ssh_deploy() {
# copy new certificate into file.
_cmdstr="$_cmdstr echo \"$(cat "$_ccert")\" $_pipe $Le_Deploy_ssh_certfile;"
_info "will copy certificate to remote file $Le_Deploy_ssh_certfile"
if [ "$Le_Deploy_ssh_multi_call" = "yes" ]; then
if ! _ssh_remote_cmd "$_cmdstr"; then
return $_err_code
fi
_cmdstr=""
fi
fi
# CAFILE is optional.
@ -139,6 +206,12 @@ ssh_deploy() {
# copy new certificate into file.
_cmdstr="$_cmdstr echo \"$(cat "$_cca")\" $_pipe $Le_Deploy_ssh_cafile;"
_info "will copy CA file to remote file $Le_Deploy_ssh_cafile"
if [ "$Le_Deploy_ssh_multi_call" = "yes" ]; then
if ! _ssh_remote_cmd "$_cmdstr"; then
return $_err_code
fi
_cmdstr=""
fi
fi
# FULLCHAIN is optional.
@ -161,6 +234,12 @@ ssh_deploy() {
# copy new certificate into file.
_cmdstr="$_cmdstr echo \"$(cat "$_cfullchain")\" $_pipe $Le_Deploy_ssh_fullchain;"
_info "will copy fullchain to remote file $Le_Deploy_ssh_fullchain"
if [ "$Le_Deploy_ssh_multi_call" = "yes" ]; then
if ! _ssh_remote_cmd "$_cmdstr"; then
return $_err_code
fi
_cmdstr=""
fi
fi
# REMOTE_CMD is optional.
@ -172,34 +251,36 @@ ssh_deploy() {
if [ -n "$Le_Deploy_ssh_remote_cmd" ]; then
_cmdstr="$_cmdstr $Le_Deploy_ssh_remote_cmd;"
_info "Will execute remote command $Le_Deploy_ssh_remote_cmd"
if [ "$Le_Deploy_ssh_multi_call" = "yes" ]; then
if ! _ssh_remote_cmd "$_cmdstr"; then
return $_err_code
fi
_cmdstr=""
fi
fi
if [ -z "$_cmdstr" ]; then
_err "No remote commands to excute. Failed to deploy certificates to remote server"
return 1
elif [ "$Le_Deploy_ssh_backup" = "yes" ]; then
# run cleanup on the backup directory, erase all older
# than 180 days (15552000 seconds).
_cmdstr="{ now=\"\$(date -u +%s)\"; for fn in $_backupprefix*; \
do if [ -d \"\$fn\" ] && [ \"\$(expr \$now - \$(date -ur \$fn +%s) )\" -ge \"15552000\" ]; \
then rm -rf \"\$fn\"; echo \"Backup \$fn deleted as older than 180 days\"; fi; done; }; $_cmdstr"
# Alternate version of above... _cmdstr="find $_backupprefix* -type d -mtime +180 2>/dev/null | xargs rm -rf; $_cmdstr"
# Create our backup directory for overwritten cert files.
_cmdstr="mkdir -p $_backupdir; $_cmdstr"
_info "Backup of old certificate files will be placed in remote directory $_backupdir"
_info "Backup directories erased after 180 days."
# if commands not all sent in multiple calls then all commands sent in a single SSH call now...
if [ -n "$_cmdstr" ]; then
if ! _ssh_remote_cmd "$_cmdstr"; then
return $_err_code
fi
fi
return 0
}
_secure_debug "Remote commands to execute: " "$_cmdstr"
#cmd
_ssh_remote_cmd() {
_cmd="$1"
_secure_debug "Remote commands to execute: $_cmd"
_info "Submitting sequence of commands to remote server by ssh"
# quotations in bash cmd below intended. Squash travis spellcheck error
# shellcheck disable=SC2029
$Le_Deploy_ssh_cmd -T "$Le_Deploy_ssh_user@$Le_Deploy_ssh_server" sh -c "'$_cmdstr'"
_ret="$?"
$Le_Deploy_ssh_cmd "$Le_Deploy_ssh_user@$Le_Deploy_ssh_server" sh -c "'$_cmd'"
_err_code="$?"
if [ "$_ret" != "0" ]; then
_err "Error code $_ret returned from $Le_Deploy_ssh_cmd"
if [ "$_err_code" != "0" ]; then
_err "Error code $_err_code returned from ssh"
fi
return $_ret
return $_err_code
}

33
deploy/synology_dsm.sh

@ -22,7 +22,7 @@
######## Public functions #####################
_syno_get_cookie_data() {
grep "\W$1=" "$HTTP_HEADER" | grep "^Set-Cookie:" | _tail_n 1 | _egrep_o "$1=[^;]*;" | tr -d ';'
grep -i "\W$1=" | grep -i "^Set-Cookie:" | _tail_n 1 | _egrep_o "$1=[^;]*;" | tr -d ';'
}
#domain keyfile certfile cafile fullchain
@ -40,9 +40,7 @@ synology_dsm_deploy() {
_getdeployconf SYNO_Password
_getdeployconf SYNO_Create
_getdeployconf SYNO_DID
if [ -z "$SYNO_Username" ] || [ -z "$SYNO_Password" ]; then
SYNO_Username=""
SYNO_Password=""
if [ -z "${SYNO_Username:-}" ] || [ -z "${SYNO_Password:-}" ]; then
_err "SYNO_Username & SYNO_Password must be set"
return 1
fi
@ -70,20 +68,20 @@ synology_dsm_deploy() {
# Get the certificate description, but don't save it until we verfiy it's real
_getdeployconf SYNO_Certificate
if [ -z "${SYNO_Certificate:?}" ]; then
_err "SYNO_Certificate needs to be defined (with the Certificate description name)"
return 1
fi
_debug SYNO_Certificate "$SYNO_Certificate"
_debug SYNO_Certificate "${SYNO_Certificate:-}"
_base_url="$SYNO_Scheme://$SYNO_Hostname:$SYNO_Port"
_debug _base_url "$_base_url"
# Login, get the token from JSON and session id from cookie
_info "Logging into $SYNO_Hostname:$SYNO_Port"
response=$(_get "$_base_url/webman/login.cgi?username=$SYNO_Username&passwd=$SYNO_Password&enable_syno_token=yes&device_id=$SYNO_DID")
token=$(echo "$response" | grep "SynoToken" | sed -n 's/.*"SynoToken" *: *"\([^"]*\).*/\1/p')
encoded_username="$(printf "%s" "$SYNO_Username" | _url_encode)"
encoded_password="$(printf "%s" "$SYNO_Password" | _url_encode)"
encoded_did="$(printf "%s" "$SYNO_DID" | _url_encode)"
response=$(_get "$_base_url/webman/login.cgi?username=$encoded_username&passwd=$encoded_password&enable_syno_token=yes&device_id=$encoded_did" 1)
token=$(echo "$response" | grep -i "X-SYNO-TOKEN:" | sed -n 's/^X-SYNO-TOKEN: \(.*\)$/\1/pI' | tr -d "\r\n")
_debug3 response "$response"
_debug token "$token"
if [ -z "$token" ]; then
_err "Unable to authenticate to $SYNO_Hostname:$SYNO_Port using $SYNO_Scheme."
@ -91,7 +89,7 @@ synology_dsm_deploy() {
return 1
fi
_H1="Cookie: $(_syno_get_cookie_data "id"); $(_syno_get_cookie_data "smid")"
_H1="Cookie: $(echo "$response" | _syno_get_cookie_data "id"); $(echo "$response" | _syno_get_cookie_data "smid")"
_H2="X-SYNO-TOKEN: $token"
export _H1
export _H2
@ -102,7 +100,6 @@ synology_dsm_deploy() {
_savedeployconf SYNO_Username "$SYNO_Username"
_savedeployconf SYNO_Password "$SYNO_Password"
_savedeployconf SYNO_DID "$SYNO_DID"
_debug token "$token"
_info "Getting certificates in Synology DSM"
response=$(_post "api=SYNO.Core.Certificate.CRT&method=list&version=1" "$_base_url/webapi/entry.cgi")
@ -110,7 +107,7 @@ synology_dsm_deploy() {
id=$(echo "$response" | sed -n "s/.*\"desc\":\"$SYNO_Certificate\",\"id\":\"\([^\"]*\).*/\1/p")
_debug2 id "$id"
if [ -z "$id" ] && [ -z "${SYNO_Create:?}" ]; then
if [ -z "$id" ] && [ -z "${SYNO_Create:-}" ]; then
_err "Unable to find certificate: $SYNO_Certificate and \$SYNO_Create is not set"
return 1
fi
@ -125,11 +122,11 @@ synology_dsm_deploy() {
_debug2 default "$default"
_info "Generate form POST request"
nl="\015\012"
nl="\0015\0012"
delim="--------------------------$(_utc_date | tr -d -- '-: ')"
content="--$delim${nl}Content-Disposition: form-data; name=\"key\"; filename=\"$(basename "$_ckey")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_ckey")\012"
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"cert\"; filename=\"$(basename "$_ccert")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_ccert")\012"
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"inter_cert\"; filename=\"$(basename "$_cca")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_cca")\012"
content="--$delim${nl}Content-Disposition: form-data; name=\"key\"; filename=\"$(basename "$_ckey")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_ckey")\0012"
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"cert\"; filename=\"$(basename "$_ccert")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_ccert")\0012"
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"inter_cert\"; filename=\"$(basename "$_cca")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_cca")\0012"
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"id\"${nl}${nl}$id"
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"desc\"${nl}${nl}${SYNO_Certificate}"
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"as_default\"${nl}${nl}${default}"

10
dnsapi/dns_1984hosting.sh

@ -3,7 +3,7 @@
#So, here must be a method dns_1984hosting_add()
#Which will be called by acme.sh to add the txt record to your api system.
#returns 0 means success, otherwise error.
#
#Author: Adrian Fedoreanu
#Report Bugs here: https://github.com/acmesh-official/acme.sh
# or here... https://github.com/acmesh-official/acme.sh/issues/2851
@ -100,7 +100,7 @@ _1984hosting_add_txt_record() {
elif _contains "$response" "<html>"; then
_err "1984Hosting failed to add TXT record for $subdomain. Check $HTTP_HEADER file"
return 1
elif [ "$response" = '{"auth": false, "ok": false}' ]; then
elif _contains "$response" '"auth": false'; then
_err "1984Hosting failed to add TXT record for $subdomain. Invalid or expired cookie"
return 1
fi
@ -167,8 +167,8 @@ _1984hosting_login() {
response="$(echo "$response" | _normalizeJson)"
_debug2 response "$response"
if [ "$response" = '{"loggedin": true, "ok": true}' ]; then
One984HOSTING_COOKIE="$(grep '^Set-Cookie:' "$HTTP_HEADER" | _tail_n 1 | _egrep_o 'sessionid=[^;]*;' | tr -d ';')"
if _contains "$response" '"loggedin": true'; then
One984HOSTING_COOKIE="$(grep -i '^set-cookie:' "$HTTP_HEADER" | _tail_n 1 | _egrep_o 'sessionid=[^;]*;' | tr -d ';')"
export One984HOSTING_COOKIE
_saveaccountconf_mutable One984HOSTING_COOKIE "$One984HOSTING_COOKIE"
return 0
@ -196,7 +196,7 @@ _check_cookie() {
_authget "https://management.1984hosting.com/accounts/loginstatus/"
response="$(echo "$_response" | _normalizeJson)"
if [ "$_response" = '{"ok": true}' ]; then
if _contains "$response" '"ok": true'; then
_debug "Cached cookie still valid"
return 0
fi

1
dnsapi/dns_ali.sh

@ -181,6 +181,7 @@ _describe_records_query() {
_clean() {
_check_exist_query "$_domain" "$_sub_domain"
# do not correct grammar here
if ! _ali_rest "Check exist records" "ignore"; then
return 1
fi

163
dnsapi/dns_arvan.sh

@ -0,0 +1,163 @@
#!/usr/bin/env sh
#Arvan_Token="xxxx"
ARVAN_API_URL="https://napi.arvancloud.com/cdn/4.0/domains"
#Author: Ehsan Aliakbar
#Report Bugs here: https://github.com/Neilpang/acme.sh
#
######## Public functions #####################
#Usage: dns_arvan_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_arvan_add() {
fulldomain=$1
txtvalue=$2
_info "Using Arvan"
Arvan_Token="${Arvan_Token:-$(_readaccountconf_mutable Arvan_Token)}"
if [ -z "$Arvan_Token" ]; then
_err "You didn't specify \"Arvan_Token\" token yet."
_err "You can get yours from here https://npanel.arvancloud.com/profile/api-keys"
return 1
fi
#save the api token to the account conf file.
_saveaccountconf_mutable Arvan_Token "$Arvan_Token"
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug _domain_id "$_domain_id"
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
_info "Adding record"
if _arvan_rest POST "$_domain/dns-records" "{\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"value\":{\"text\":\"$txtvalue\"},\"ttl\":120}"; then
if _contains "$response" "$txtvalue"; then
_info "Added, OK"
return 0
elif _contains "$response" "Record Data is Duplicated"; then
_info "Already exists, OK"
return 0
else
_err "Add txt record error."
return 1
fi
fi
_err "Add txt record error."
return 1
}
#Usage: fulldomain txtvalue
#Remove the txt record after validation.
dns_arvan_rm() {
fulldomain=$1
txtvalue=$2
_info "Using Arvan"
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
Arvan_Token="${Arvan_Token:-$(_readaccountconf_mutable Arvan_Token)}"
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug _domain_id "$_domain_id"
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
_debug "Getting txt records"
shorted_txtvalue=$(printf "%s" "$txtvalue" | cut -d "-" -d "_" -f1)
_arvan_rest GET "${_domain}/dns-records?search=$shorted_txtvalue"
if ! printf "%s" "$response" | grep \"current_page\":1 >/dev/null; then
_err "Error on Arvan Api"
_err "Please create a github issue with debbug log"
return 1
fi
count=$(printf "%s\n" "$response" | _egrep_o "\"total\":[^,]*" | cut -d : -f 2)
_debug count "$count"
if [ "$count" = "0" ]; then
_info "Don't need to remove."
else
record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | head -n 1)
_debug "record_id" "$record_id"
if [ -z "$record_id" ]; then
_err "Can not get record id to remove."
return 1
fi
if ! _arvan_rest "DELETE" "${_domain}/dns-records/$record_id"; then
_err "Delete record error."
return 1
fi
_debug "$response"
_contains "$response" 'dns record deleted'
fi
}
#################### Private functions below ##################################
#_acme-challenge.www.domain.com
#returns
# _sub_domain=_acme-challenge.www
# _domain=domain.com
# _domain_id=sdjkglgdfewsdfg
_get_root() {
domain=$1
i=1
p=1
while true; do
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
_debug h "$h"
if [ -z "$h" ]; then
#not valid
return 1
fi
if ! _arvan_rest GET "?search=$h"; then
return 1
fi
if _contains "$response" "\"domain\":\"$h\"" || _contains "$response" '"total":1'; then
_domain_id=$(echo "$response" | _egrep_o "\[.\"id\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \")
if [ "$_domain_id" ]; then
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_domain=$h
return 0
fi
return 1
fi
p=$i
i=$(_math "$i" + 1)
done
return 1
}
_arvan_rest() {
mtd="$1"
ep="$2"
data="$3"
token_trimmed=$(echo "$Arvan_Token" | tr -d '"')
export _H1="Authorization: $token_trimmed"
if [ "$mtd" = "DELETE" ]; then
#DELETE Request shouldn't have Content-Type
_debug data "$data"
response="$(_post "$data" "$ARVAN_API_URL/$ep" "" "$mtd")"
elif [ "$mtd" = "POST" ]; then
export _H2="Content-Type: application/json"
_debug data "$data"
response="$(_post "$data" "$ARVAN_API_URL/$ep" "" "$mtd")"
else
response="$(_get "$ARVAN_API_URL/$ep$data")"
fi
}

2
dnsapi/dns_azure.sh

@ -172,7 +172,7 @@ dns_azure_rm() {
_azure_rest GET "$acmeRecordURI" "" "$accesstoken"
timestamp="$(_time)"
if [ "$_code" = "200" ]; then
vlist="$(echo "$response" | _egrep_o "\"value\"\\s*:\\s*\\[\\s*\"[^\"]*\"\\s*]" | cut -d : -f 2 | tr -d "[]\"" | grep -v "$txtvalue")"
vlist="$(echo "$response" | _egrep_o "\"value\"\\s*:\\s*\\[\\s*\"[^\"]*\"\\s*]" | cut -d : -f 2 | tr -d "[]\"" | grep -v -- "$txtvalue")"
values=""
comma=""
for v in $vlist; do

16
dnsapi/dns_cf.sh

@ -59,7 +59,7 @@ dns_cf_add() {
_debug "Getting txt records"
_cf_rest GET "zones/${_domain_id}/dns_records?type=TXT&name=$fulldomain"
if ! printf "%s" "$response" | grep \"success\":true >/dev/null; then
if ! echo "$response" | tr -d " " | grep \"success\":true >/dev/null; then
_err "Error"
return 1
fi
@ -110,17 +110,17 @@ dns_cf_rm() {
_debug "Getting txt records"
_cf_rest GET "zones/${_domain_id}/dns_records?type=TXT&name=$fulldomain&content=$txtvalue"
if ! printf "%s" "$response" | grep \"success\":true >/dev/null; then
if ! echo "$response" | tr -d " " | grep \"success\":true >/dev/null; then
_err "Error: $response"
return 1
fi
count=$(printf "%s\n" "$response" | _egrep_o "\"count\":[^,]*" | cut -d : -f 2)
count=$(echo "$response" | _egrep_o "\"count\": *[^,]*" | cut -d : -f 2 | tr -d " ")
_debug count "$count"
if [ "$count" = "0" ]; then
_info "Don't need to remove."
else
record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | head -n 1)
record_id=$(echo "$response" | _egrep_o "\"id\": *\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | _head_n 1 | tr -d " ")
_debug "record_id" "$record_id"
if [ -z "$record_id" ]; then
_err "Can not get record id to remove."
@ -130,7 +130,7 @@ dns_cf_rm() {
_err "Delete record error."
return 1
fi
_contains "$response" '"success":true'
echo "$response" | tr -d " " | grep \"success\":true >/dev/null
fi
}
@ -151,8 +151,8 @@ _get_root() {
if ! _cf_rest GET "zones/$CF_Zone_ID"; then
return 1
else
if _contains "$response" '"success":true'; then
_domain=$(printf "%s\n" "$response" | _egrep_o "\"name\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | head -n 1)
if echo "$response" | tr -d " " | grep \"success\":true >/dev/null; then
_domain=$(echo "$response" | _egrep_o "\"name\": *\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | _head_n 1 | tr -d " ")
if [ "$_domain" ]; then
_cutlength=$((${#domain} - ${#_domain} - 1))
_sub_domain=$(printf "%s" "$domain" | cut -c "1-$_cutlength")
@ -186,7 +186,7 @@ _get_root() {
fi
if _contains "$response" "\"name\":\"$h\"" || _contains "$response" '"total_count":1'; then
_domain_id=$(echo "$response" | _egrep_o "\[.\"id\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \")
_domain_id=$(echo "$response" | _egrep_o "\[.\"id\": *\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \" | tr -d " ")
if [ "$_domain_id" ]; then
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_domain=$h

6
dnsapi/dns_cloudns.sh

@ -69,7 +69,7 @@ dns_cloudns_rm() {
for i in $(echo "$response" | tr '{' "\n" | grep "$record"); do
record_id=$(echo "$i" | tr ',' "\n" | grep -E '^"id"' | sed -re 's/^\"id\"\:\"([0-9]+)\"$/\1/g')
if [ ! -z "$record_id" ]; then
if [ -n "$record_id" ]; then
_debug zone "$zone"
_debug host "$host"
_debug record "$record"
@ -91,7 +91,7 @@ dns_cloudns_rm() {
#################### Private functions below ##################################
_dns_cloudns_init_check() {
if [ ! -z "$CLOUDNS_INIT_CHECK_COMPLETED" ]; then
if [ -n "$CLOUDNS_INIT_CHECK_COMPLETED" ]; then
return 0
fi
@ -164,7 +164,7 @@ _dns_cloudns_http_api_call() {
_debug CLOUDNS_SUB_AUTH_ID "$CLOUDNS_SUB_AUTH_ID"
_debug CLOUDNS_AUTH_PASSWORD "$CLOUDNS_AUTH_PASSWORD"
if [ ! -z "$CLOUDNS_SUB_AUTH_ID" ]; then
if [ -n "$CLOUDNS_SUB_AUTH_ID" ]; then
auth_user="sub-auth-id=$CLOUDNS_SUB_AUTH_ID"
else
auth_user="auth-id=$CLOUDNS_AUTH_ID"

4
dnsapi/dns_cyon.sh

@ -66,7 +66,7 @@ _cyon_load_credentials() {
_debug "Save credentials to account.conf"
_saveaccountconf CY_Username "${CY_Username}"
_saveaccountconf CY_Password_B64 "$CY_Password_B64"
if [ ! -z "${CY_OTP_Secret}" ]; then
if [ -n "${CY_OTP_Secret}" ]; then
_saveaccountconf CY_OTP_Secret "$CY_OTP_Secret"
else
_clearaccountconf CY_OTP_Secret
@ -164,7 +164,7 @@ _cyon_login() {
# todo: instead of just checking if the env variable is defined, check if we actually need to do a 2FA auth request.
# 2FA authentication with OTP?
if [ ! -z "${CY_OTP_Secret}" ]; then
if [ -n "${CY_OTP_Secret}" ]; then
_info " - Authorising with OTP code..."
if ! _exists oathtool; then

2
dnsapi/dns_ddnss.sh

@ -119,7 +119,7 @@ _ddnss_rest() {
# DDNSS uses GET to update domain info
if [ "$method" = "GET" ]; then
response="$(_get "$url" | sed 's/<[a-zA-Z\/][^>]*>//g' | _tail_n 1)"
response="$(_get "$url" | sed 's/<[a-zA-Z\/][^>]*>//g' | tr -s "\n" | _tail_n 1)"
else
_err "Unsupported method"
return 1

65
dnsapi/dns_df.sh

@ -0,0 +1,65 @@
#!/usr/bin/env sh
########################################################################
# https://dyndnsfree.de hook script for acme.sh
#
# Environment variables:
#
# - $DF_user (your dyndnsfree.de username)
# - $DF_password (your dyndnsfree.de password)
#
# Author: Thilo Gass <thilo.gass@gmail.com>
# Git repo: https://github.com/ThiloGa/acme.sh
#-- dns_df_add() - Add TXT record --------------------------------------
# Usage: dns_df_add _acme-challenge.subdomain.domain.com "XyZ123..."
dyndnsfree_api="https://dynup.de/acme.php"
dns_df_add() {
fulldomain=$1
txt_value=$2
_info "Using DNS-01 dyndnsfree.de hook"
DF_user="${DF_user:-$(_readaccountconf_mutable DF_user)}"
DF_password="${DF_password:-$(_readaccountconf_mutable DF_password)}"
if [ -z "$DF_user" ] || [ -z "$DF_password" ]; then
DF_user=""
DF_password=""
_err "No auth details provided. Please set user credentials using the \$DF_user and \$DF_password environment variables."
return 1
fi
#save the api user and password to the account conf file.
_debug "Save user and password"
_saveaccountconf_mutable DF_user "$DF_user"
_saveaccountconf_mutable DF_password "$DF_password"
domain="$(printf "%s" "$fulldomain" | cut -d"." -f2-)"
get="$dyndnsfree_api?username=$DF_user&password=$DF_password&hostname=$domain&add_hostname=$fulldomain&txt=$txt_value"
if ! erg="$(_get "$get")"; then
_err "error Adding $fulldomain TXT: $txt_value"
return 1
fi
if _contains "$erg" "success"; then
_info "Success, TXT Added, OK"
else
_err "error Adding $fulldomain TXT: $txt_value erg: $erg"
return 1
fi
_debug "ok Auto $fulldomain TXT: $txt_value erg: $erg"
return 0
}
dns_df_rm() {
fulldomain=$1
txtvalue=$2
_info "TXT enrty in $fulldomain is deleted automatically"
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
}

10
dnsapi/dns_dgon.sh

@ -22,7 +22,7 @@ dns_dgon_add() {
txtvalue=$2
DO_API_KEY="${DO_API_KEY:-$(_readaccountconf_mutable DO_API_KEY)}"
# Check if API Key Exist
# Check if API Key Exists
if [ -z "$DO_API_KEY" ]; then
DO_API_KEY=""
_err "You did not specify DigitalOcean API key."
@ -77,7 +77,7 @@ dns_dgon_rm() {
txtvalue=$2
DO_API_KEY="${DO_API_KEY:-$(_readaccountconf_mutable DO_API_KEY)}"
# Check if API Key Exist
# Check if API Key Exists
if [ -z "$DO_API_KEY" ]; then
DO_API_KEY=""
_err "You did not specify DigitalOcean API key."
@ -122,12 +122,12 @@ dns_dgon_rm() {
## check for what we are looking for: "type":"A","name":"$_sub_domain"
record="$(echo "$domain_list" | _egrep_o "\"id\"\s*\:\s*\"*[0-9]+\"*[^}]*\"name\"\s*\:\s*\"$_sub_domain\"[^}]*\"data\"\s*\:\s*\"$txtvalue\"")"
if [ ! -z "$record" ]; then
if [ -n "$record" ]; then
## we found records
rec_ids="$(echo "$record" | _egrep_o "id\"\s*\:\s*\"*[0-9]+" | _egrep_o "[0-9]+")"
_debug rec_ids "$rec_ids"
if [ ! -z "$rec_ids" ]; then
if [ -n "$rec_ids" ]; then
echo "$rec_ids" | while IFS= read -r rec_id; do
## delete the record
## delete URL for removing the one we dont want
@ -218,7 +218,7 @@ _get_base_domain() {
## we got part of a domain back - grep it out
found="$(echo "$domain_list" | _egrep_o "\"name\"\s*\:\s*\"$_domain\"")"
## check if it exists
if [ ! -z "$found" ]; then
if [ -n "$found" ]; then
## exists - exit loop returning the parts
sub_point=$(_math $i - 1)
_sub_domain=$(printf "%s" "$fulldomain" | cut -d . -f 1-"$sub_point")

7
dnsapi/dns_duckdns.sh

@ -91,13 +91,12 @@ dns_duckdns_rm() {
#################### Private functions below ##################################
#fulldomain=_acme-challenge.domain.duckdns.org
#returns
# _duckdns_domain=domain
# fulldomain may be 'domain.duckdns.org' (if using --domain-alias) or '_acme-challenge.domain.duckdns.org'
# either way, return 'domain'. (duckdns does not allow further subdomains and restricts domains to [a-z0-9-].)
_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)"
_duckdns_domain="$(printf "%s" "$fulldomain" | _lower_case | _egrep_o '^(_acme-challenge\.)?[a-z0-9-]*\.duckdns\.org' | sed 's/^\(_acme-challenge\.\)\?\([a-z0-9-]*\)\.duckdns\.org/\2/')"
if [ -z "$_duckdns_domain" ]; then
_err "Error extracting the domain."

4
dnsapi/dns_dynu.sh

@ -216,6 +216,10 @@ _dynu_authentication() {
_err "Authentication failed."
return 1
fi
if _contains "$response" "Authentication Exception"; then
_err "Authentication failed."
return 1
fi
if _contains "$response" "access_token"; then
Dynu_Token=$(printf "%s" "$response" | tr -d "{}" | cut -d , -f 1 | cut -d : -f 2 | cut -d '"' -f 2)
fi

6
dnsapi/dns_dynv6.sh

@ -18,7 +18,7 @@ dns_dynv6_add() {
if ! _contains "$_your_hosts" "$_host"; then
_debug "The host is $_host and the record $_record"
_debug "Dynv6 returned $_your_hosts"
_err "The host $_host does not exists on your dynv6 account"
_err "The host $_host does not exist on your dynv6 account"
return 1
fi
_debug "found host on your account"
@ -49,7 +49,7 @@ dns_dynv6_rm() {
if ! _contains "$_your_hosts" "$_host"; then
_debug "The host is $_host and the record $_record"
_debug "Dynv6 returned $_your_hosts"
_err "The host $_host does not exists on your dynv6 account"
_err "The host $_host does not exist on your dynv6 account"
return 1
fi
_debug "found host on your account"
@ -80,7 +80,7 @@ _generate_new_key() {
_get_domain() {
_full_domain="$1"
_debug "getting domain for $_full_domain"
if ! _contains "$_full_domain" 'dynv6.net' && ! _contains "$_full_domain" 'dns.army' && ! _contains "$_full_domain" 'dns.navy'; then
if ! _contains "$_full_domain" 'dynv6.net' && ! _contains "$_full_domain" 'dns.army' && ! _contains "$_full_domain" 'dns.navy' && ! _contains "$_full_domain" 'v6.rocks'; then
_err "The hosts does not seem to be a dynv6 host"
return 1
fi

5
dnsapi/dns_easydns.sh

@ -4,8 +4,7 @@
#
# easyDNS REST API for acme.sh by Neilpang based on dns_cf.sh
#
# Please note: # API is currently beta and subject to constant change
# http://sandbox.rest.easydns.net:3000/
# API Documentation: https://sandbox.rest.easydns.net:3001/
#
# Author: wurzelpanzer [wurzelpanzer@maximolider.net]
# Report Bugs here: https://github.com/acmesh-official/acme.sh/issues/2647
@ -25,7 +24,7 @@ dns_easydns_add() {
EASYDNS_Key="${EASYDNS_Key:-$(_readaccountconf_mutable EASYDNS_Key)}"
if [ -z "$EASYDNS_Token" ] || [ -z "$EASYDNS_Key" ]; then
_err "You didn't specify an easydns.net token or api key. Please sign up at http://docs.sandbox.rest.easydns.net/beta_signup.php"
_err "You didn't specify an easydns.net token or api key. Signup at https://cp.easydns.com/manage/security/api/signup.php"
return 1
else
_saveaccountconf_mutable EASYDNS_Token "$EASYDNS_Token"

2
dnsapi/dns_gd.sh

@ -91,7 +91,7 @@ dns_gd_rm() {
fi
if ! _contains "$response" "$txtvalue"; then
_info "The record is not existing, skip"
_info "The record does not exist, skip"
return 0
fi

11
dnsapi/dns_gdnsdk.sh

@ -157,9 +157,18 @@ _successful_update() {
}
_findentry() {
#args $1: fulldomain, $2: txtvalue
#returns id of dns entry, if it exists
_myget "action=dns_primary_changeDNSsetup&user_domain=$_domain"
_id=$(echo "$_result" | _egrep_o "<td>$1</td>\s*<td>$2</td>[^?]*[^&]*&id=[^&]*" | sed 's/^.*=//')
_debug3 "_result: $_result"
_tmp_result=$(echo "$_result" | tr -d '\n\r' | _egrep_o "<td>$1</td>\s*<td>$2</td>[^?]*[^&]*&id=[^&]*")
_debug _tmp_result "$_tmp_result"
if [ -z "${_tmp_result:-}" ]; then
_debug "The variable is _tmp_result is not supposed to be empty, there may be something wrong with the script"
fi
_id=$(echo "$_tmp_result" | sed 's/^.*=//')
if [ -n "$_id" ]; then
_debug "Entry found with _id=$_id"
return 0

2
dnsapi/dns_he.sh

@ -24,7 +24,7 @@ dns_he_add() {
if [ -z "$HE_Username" ] || [ -z "$HE_Password" ]; then
HE_Username=
HE_Password=
_err "No auth details provided. Please set user credentials using the \$HE_Username and \$HE_Password envoronment variables."
_err "No auth details provided. Please set user credentials using the \$HE_Username and \$HE_Password environment variables."
return 1
fi
_saveaccountconf_mutable HE_Username "$HE_Username"

252
dnsapi/dns_hetzner.sh

@ -0,0 +1,252 @@
#!/usr/bin/env sh
#
#HETZNER_Token="sdfsdfsdfljlbjkljlkjsdfoiwje"
#
HETZNER_Api="https://dns.hetzner.com/api/v1"
######## Public functions #####################
# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
# Used to add txt record
# Ref: https://dns.hetzner.com/api-docs/
dns_hetzner_add() {
full_domain=$1
txt_value=$2
HETZNER_Token="${HETZNER_Token:-$(_readaccountconf_mutable HETZNER_Token)}"
if [ -z "$HETZNER_Token" ]; then
HETZNER_Token=""
_err "You didn't specify a Hetzner api token."
_err "You can get yours from here https://dns.hetzner.com/settings/api-token."
return 1
fi
#save the api key and email to the account conf file.
_saveaccountconf_mutable HETZNER_Token "$HETZNER_Token"
_debug "First detect the root zone"
if ! _get_root "$full_domain"; then
_err "Invalid domain"
return 1
fi
_debug _domain_id "$_domain_id"
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
_debug "Getting TXT records"
if ! _find_record "$_sub_domain" "$txt_value"; then
return 1
fi
if [ -z "$_record_id" ]; then
_info "Adding record"
if _hetzner_rest POST "records" "{\"zone_id\":\"${HETZNER_Zone_ID}\",\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"value\":\"$txt_value\",\"ttl\":120}"; then
if _contains "$response" "$txt_value"; then
_info "Record added, OK"
_sleep 2
return 0
fi
fi
_err "Add txt record error${_response_error}"
return 1
else
_info "Found record id: $_record_id."
_info "Record found, do nothing."
return 0
# we could modify a record, if the names for txt records for *.example.com and example.com would be not the same
#if _hetzner_rest PUT "records/${_record_id}" "{\"zone_id\":\"${HETZNER_Zone_ID}\",\"type\":\"TXT\",\"name\":\"$full_domain\",\"value\":\"$txt_value\",\"ttl\":120}"; then
# if _contains "$response" "$txt_value"; then
# _info "Modified, OK"
# return 0
# fi
#fi
#_err "Add txt record error (modify)."
#return 1
fi
}
# Usage: full_domain txt_value
# Used to remove the txt record after validation
dns_hetzner_rm() {
full_domain=$1
txt_value=$2
HETZNER_Token="${HETZNER_Token:-$(_readaccountconf_mutable HETZNER_Token)}"
_debug "First detect the root zone"
if ! _get_root "$full_domain"; then
_err "Invalid domain"
return 1
fi
_debug _domain_id "$_domain_id"
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
_debug "Getting TXT records"
if ! _find_record "$_sub_domain" "$txt_value"; then
return 1
fi
if [ -z "$_record_id" ]; then
_info "Remove not needed. Record not found."
else
if ! _hetzner_rest DELETE "records/$_record_id"; then
_err "Delete record error${_response_error}"
return 1
fi
_sleep 2
_info "Record deleted"
fi
}
#################### Private functions below ##################################
#returns
# _record_id=a8d58f22d6931bf830eaa0ec6464bf81 if found; or 1 if error
_find_record() {
unset _record_id
_record_name=$1
_record_value=$2
if [ -z "$_record_value" ]; then
_record_value='[^"]*'
fi
_debug "Getting all records"
_hetzner_rest GET "records?zone_id=${_domain_id}"
if _response_has_error; then
_err "Error${_response_error}"
return 1
else
_record_id=$(
echo "$response" \
| grep -o "{[^\{\}]*\"name\":\"$_record_name\"[^\}]*}" \
| grep "\"value\":\"$_record_value\"" \
| while read -r record; do
# test for type and
if [ -n "$(echo "$record" | _egrep_o '"type":"TXT"')" ]; then
echo "$record" | _egrep_o '"id":"[^"]*"' | cut -d : -f 2 | tr -d \"
break
fi
done
)
fi
}
#_acme-challenge.www.domain.com
#returns
# _sub_domain=_acme-challenge.www
# _domain=domain.com
# _domain_id=sdjkglgdfewsdfg
_get_root() {
domain=$1
i=1
p=1
domain_without_acme=$(echo "$domain" | cut -d . -f 2-)
domain_param_name=$(echo "HETZNER_Zone_ID_for_${domain_without_acme}" | sed 's/[\.\-]/_/g')
_debug "Reading zone_id for '$domain_without_acme' from config..."
HETZNER_Zone_ID=$(_readdomainconf "$domain_param_name")
if [ "$HETZNER_Zone_ID" ]; then
_debug "Found, using: $HETZNER_Zone_ID"
if ! _hetzner_rest GET "zones/${HETZNER_Zone_ID}"; then
_debug "Zone with id '$HETZNER_Zone_ID' does not exist."
_cleardomainconf "$domain_param_name"
unset HETZNER_Zone_ID
else
if _contains "$response" "\"id\":\"$HETZNER_Zone_ID\""; then
_domain=$(printf "%s\n" "$response" | _egrep_o '"name":"[^"]*"' | cut -d : -f 2 | tr -d \" | head -n 1)
if [ "$_domain" ]; then
_cut_length=$((${#domain} - ${#_domain} - 1))
_sub_domain=$(printf "%s" "$domain" | cut -c "1-$_cut_length")
_domain_id="$HETZNER_Zone_ID"
return 0
else
return 1
fi
else
return 1
fi
fi
fi
_debug "Trying to get zone id by domain name for '$domain_without_acme'."
while true; do
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
if [ -z "$h" ]; then
#not valid
return 1
fi
_debug h "$h"
_hetzner_rest GET "zones?name=$h"
if _contains "$response" "\"name\":\"$h\"" || _contains "$response" '"total_entries":1'; then
_domain_id=$(echo "$response" | _egrep_o "\[.\"id\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \")
if [ "$_domain_id" ]; then
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_domain=$h
HETZNER_Zone_ID=$_domain_id
_savedomainconf "$domain_param_name" "$HETZNER_Zone_ID"
return 0
fi
return 1
fi
p=$i
i=$(_math "$i" + 1)
done
return 1
}
#returns
# _response_error
_response_has_error() {
unset _response_error
err_part="$(echo "$response" | _egrep_o '"error":{[^}]*}')"
if [ -n "$err_part" ]; then
err_code=$(echo "$err_part" | _egrep_o '"code":[0-9]+' | cut -d : -f 2)
err_message=$(echo "$err_part" | _egrep_o '"message":"[^"]+"' | cut -d : -f 2 | tr -d \")
if [ -n "$err_code" ] && [ -n "$err_message" ]; then
_response_error=" - message: ${err_message}, code: ${err_code}"
return 0
fi
fi
return 1
}
#returns
# response
_hetzner_rest() {
m=$1
ep="$2"
data="$3"
_debug "$ep"
key_trimmed=$(echo "$HETZNER_Token" | tr -d \")
export _H1="Content-TType: application/json"
export _H2="Auth-API-Token: $key_trimmed"
if [ "$m" != "GET" ]; then
_debug data "$data"
response="$(_post "$data" "$HETZNER_Api/$ep" "" "$m")"
else
response="$(_get "$HETZNER_Api/$ep")"
fi
if [ "$?" != "0" ] || _response_has_error; then
_debug "Error$_response_error"
return 1
fi
_debug2 response "$response"
return 0
}

8
dnsapi/dns_hexonet.sh

@ -42,7 +42,7 @@ dns_hexonet_add() {
_debug _domain "$_domain"
_debug "Getting txt records"
_hexonet_rest "&command=QueryDNSZoneRRList&dnszone=${h}.&RRTYPE=TXT"
_hexonet_rest "command=QueryDNSZoneRRList&dnszone=${h}.&RRTYPE=TXT"
if ! _contains "$response" "CODE=200"; then
_err "Error"
@ -88,7 +88,7 @@ dns_hexonet_rm() {
_debug _domain "$_domain"
_debug "Getting txt records"
_hexonet_rest "&command=QueryDNSZoneRRList&dnszone=${h}.&RRTYPE=TXT&RR=${txtvalue}"
_hexonet_rest "command=QueryDNSZoneRRList&dnszone=${h}.&RRTYPE=TXT&RR=${_sub_domain}%20IN%20TXT%20\"${txtvalue}\""
if ! _contains "$response" "CODE=200"; then
_err "Error"
@ -100,7 +100,7 @@ dns_hexonet_rm() {
if [ "$count" = "0" ]; then
_info "Don't need to remove."
else
if ! _hexonet_rest "&command=UpdateDNSZone&dnszone=${_domain}.&delrr0='${_sub_domain}%20IN%20TXT%20\"${txtvalue}\""; then
if ! _hexonet_rest "command=UpdateDNSZone&dnszone=${_domain}.&delrr0=${_sub_domain}%20IN%20TXT%20\"${txtvalue}\""; then
_err "Delete record error."
return 1
fi
@ -126,7 +126,7 @@ _get_root() {
return 1
fi
if ! _hexonet_rest "&command=QueryDNSZoneRRList&dnszone=${h}."; then
if ! _hexonet_rest "command=QueryDNSZoneRRList&dnszone=${h}."; then
return 1
fi

81
dnsapi/dns_inwx.sh

@ -34,6 +34,10 @@ dns_inwx_add() {
_saveaccountconf_mutable INWX_Password "$INWX_Password"
_saveaccountconf_mutable INWX_Shared_Secret "$INWX_Shared_Secret"
if ! _inwx_login; then
return 1
fi
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
@ -64,6 +68,10 @@ dns_inwx_rm() {
return 1
fi
if ! _inwx_login; then
return 1
fi
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
@ -123,8 +131,42 @@ dns_inwx_rm() {
#################### Private functions below ##################################
_inwx_check_cookie() {
INWX_Cookie="${INWX_Cookie:-$(_readaccountconf_mutable INWX_Cookie)}"
if [ -z "$INWX_Cookie" ]; then
_debug "No cached cookie found"
return 1
fi
_H1="$INWX_Cookie"
export _H1
xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?>
<methodCall>
<methodName>account.info</methodName>
</methodCall>')
response="$(_post "$xml_content" "$INWX_Api" "" "POST")"
if _contains "$response" "<member><name>code</name><value><int>1000</int></value></member>"; then
_debug "Cached cookie still valid"
return 0
fi
_debug "Cached cookie no longer valid"
_H1=""
export _H1
INWX_Cookie=""
_saveaccountconf_mutable INWX_Cookie "$INWX_Cookie"
return 1
}
_inwx_login() {
if _inwx_check_cookie; then
_debug "Already logged in"
return 0
fi
xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?>
<methodCall>
<methodName>account.login</methodName>
@ -148,17 +190,25 @@ _inwx_login() {
</value>
</param>
</params>
</methodCall>' $INWX_User $INWX_Password)
</methodCall>' "$INWX_User" "$INWX_Password")
response="$(_post "$xml_content" "$INWX_Api" "" "POST")"
_H1=$(printf "Cookie: %s" "$(grep "domrobot=" "$HTTP_HEADER" | grep "^Set-Cookie:" | _tail_n 1 | _egrep_o 'domrobot=[^;]*;' | tr -d ';')")
INWX_Cookie=$(printf "Cookie: %s" "$(grep "domrobot=" "$HTTP_HEADER" | grep "^Set-Cookie:" | _tail_n 1 | _egrep_o 'domrobot=[^;]*;' | tr -d ';')")
_H1=$INWX_Cookie
export _H1
export INWX_Cookie
_saveaccountconf_mutable INWX_Cookie "$INWX_Cookie"
if ! _contains "$response" "<member><name>code</name><value><int>1000</int></value></member>"; then
_err "INWX API: Authentication error (username/password correct?)"
return 1
fi
#https://github.com/inwx/php-client/blob/master/INWX/Domrobot.php#L71
if _contains "$response" "<member><name>code</name><value><int>1000</int></value></member>" \
&& _contains "$response" "<member><name>tfa</name><value><string>GOOGLE-AUTH</string></value></member>"; then
if _contains "$response" "<member><name>tfa</name><value><string>GOOGLE-AUTH</string></value></member>"; then
if [ -z "$INWX_Shared_Secret" ]; then
_err "Mobile TAN detected."
_err "INWX API: Mobile TAN detected."
_err "Please define a shared secret."
return 1
fi
@ -191,6 +241,11 @@ _inwx_login() {
</methodCall>' "$tan")
response="$(_post "$xml_content" "$INWX_Api" "" "POST")"
if ! _contains "$response" "<member><name>code</name><value><int>1000</int></value></member>"; then
_err "INWX API: Mobile TAN not correct."
return 1
fi
fi
}
@ -203,11 +258,23 @@ _get_root() {
i=2
p=1
_inwx_login
xml_content='<?xml version="1.0" encoding="UTF-8"?>
<methodCall>
<methodName>nameserver.list</methodName>
<params>
<param>
<value>
<struct>
<member>
<name>pagelimit</name>
<value>
<int>9999</int>
</value>
</member>
</struct>
</value>
</param>
</params>
</methodCall>'
response="$(_post "$xml_content" "$INWX_Api" "" "POST")"

150
dnsapi/dns_kappernet.sh

@ -0,0 +1,150 @@
#!/usr/bin/env sh
# kapper.net domain api
# for further questions please contact: support@kapper.net
# please report issues here: https://github.com/acmesh-official/acme.sh/issues/2977
#KAPPERNETDNS_Key="yourKAPPERNETapikey"
#KAPPERNETDNS_Secret="yourKAPPERNETapisecret"
KAPPERNETDNS_Api="https://dnspanel.kapper.net/API/1.2?APIKey=$KAPPERNETDNS_Key&APISecret=$KAPPERNETDNS_Secret"
###############################################################################
# called with
# fullhostname: something.example.com
# txtvalue: someacmegenerated string
dns_kappernet_add() {
fullhostname=$1
txtvalue=$2
KAPPERNETDNS_Key="${KAPPERNETDNS_Key:-$(_readaccountconf_mutable KAPPERNETDNS_Key)}"
KAPPERNETDNS_Secret="${KAPPERNETDNS_Secret:-$(_readaccountconf_mutable KAPPERNETDNS_Secret)}"
if [ -z "$KAPPERNETDNS_Key" ] || [ -z "$KAPPERNETDNS_Secret" ]; then
KAPPERNETDNS_Key=""
KAPPERNETDNS_Secret=""
_err "Please specify your kapper.net api key and secret."
_err "If you have not received yours - send your mail to"
_err "support@kapper.net to get your key and secret."
return 1
fi
#store the api key and email to the account conf file.
_saveaccountconf_mutable KAPPERNETDNS_Key "$KAPPERNETDNS_Key"
_saveaccountconf_mutable KAPPERNETDNS_Secret "$KAPPERNETDNS_Secret"
_debug "Checking Domain ..."
if ! _get_root "$fullhostname"; then
_err "invalid domain"
return 1
fi
_debug _sub_domain "SUBDOMAIN: $_sub_domain"
_debug _domain "DOMAIN: $_domain"
_info "Trying to add TXT DNS Record"
data="%7B%22name%22%3A%22$fullhostname%22%2C%22type%22%3A%22TXT%22%2C%22content%22%3A%22$txtvalue%22%2C%22ttl%22%3A%223600%22%2C%22prio%22%3A%22%22%7D"
if _kappernet_api GET "action=new&subject=$_domain&data=$data"; then
if _contains "$response" "{\"OK\":true"; then
_info "Waiting 120 seconds for DNS to spread the new record"
_sleep 120
return 0
else
_err "Error creating a TXT DNS Record: $fullhostname TXT $txtvalue"
_err "Error Message: $response"
return 1
fi
fi
_err "Failed creating TXT Record"
}
###############################################################################
# called with
# fullhostname: something.example.com
dns_kappernet_rm() {
fullhostname=$1
txtvalue=$2
KAPPERNETDNS_Key="${KAPPERNETDNS_Key:-$(_readaccountconf_mutable KAPPERNETDNS_Key)}"
KAPPERNETDNS_Secret="${KAPPERNETDNS_Secret:-$(_readaccountconf_mutable KAPPERNETDNS_Secret)}"
if [ -z "$KAPPERNETDNS_Key" ] || [ -z "$KAPPERNETDNS_Secret" ]; then
KAPPERNETDNS_Key=""
KAPPERNETDNS_Secret=""
_err "Please specify your kapper.net api key and secret."
_err "If you have not received yours - send your mail to"
_err "support@kapper.net to get your key and secret."
return 1
fi
#store the api key and email to the account conf file.
_saveaccountconf_mutable KAPPERNETDNS_Key "$KAPPERNETDNS_Key"
_saveaccountconf_mutable KAPPERNETDNS_Secret "$KAPPERNETDNS_Secret"
_info "Trying to remove the TXT Record: $fullhostname containing $txtvalue"
data="%7B%22name%22%3A%22$fullhostname%22%2C%22type%22%3A%22TXT%22%2C%22content%22%3A%22$txtvalue%22%2C%22ttl%22%3A%223600%22%2C%22prio%22%3A%22%22%7D"
if _kappernet_api GET "action=del&subject=$fullhostname&data=$data"; then
if _contains "$response" "{\"OK\":true"; then
return 0
else
_err "Error deleting DNS Record: $fullhostname containing $txtvalue"
_err "Problem: $response"
return 1
fi
fi
_err "Problem deleting TXT DNS record"
}
#################### Private functions below ##################################
# called with hostname
# e.g._acme-challenge.www.domain.com returns
# _sub_domain=_acme-challenge.www
# _domain=domain.com
_get_root() {
domain=$1
i=2
p=1
while true; do
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
if [ -z "$h" ]; then
#not valid
return 1
fi
if ! _kappernet_api GET "action=list&subject=$h"; then
return 1
fi
if _contains "$response" '"OK":false'; then
_debug "$h not found"
else
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_domain="$h"
return 0
fi
p="$i"
i=$(_math "$i" + 1)
done
return 1
}
################################################################################
# calls the kapper.net DNS Panel API
# with
# method
# param
_kappernet_api() {
method=$1
param="$2"
_debug param "PARAMETER=$param"
url="$KAPPERNETDNS_Api&$param"
_debug url "URL=$url"
if [ "$method" = "GET" ]; then
response="$(_get "$url")"
else
_err "Unsupported method"
return 1
fi
_debug2 response "$response"
return 0
}

2
dnsapi/dns_kinghost.sh

@ -37,7 +37,7 @@ dns_kinghost_add() {
_debug "Getting txt records"
_kinghost_rest GET "dns" "name=$fulldomain&content=$txtvalue"
#This API call returns "status":"ok" if dns record does not exists
#This API call returns "status":"ok" if dns record does not exist
#We are creating a new txt record here, so we expect the "ok" status
if ! echo "$response" | grep '"status":"ok"' >/dev/null; then
_err "Error"

4
dnsapi/dns_lexicon.sh

@ -92,7 +92,7 @@ dns_lexicon_add() {
_savedomainconf LEXICON_OPTS "$LEXICON_OPTS"
# shellcheck disable=SC2086
$lexicon_cmd "$PROVIDER" $LEXICON_OPTS create "${domain}" TXT --name="_acme-challenge.${domain}." --content="${txtvalue}"
$lexicon_cmd "$PROVIDER" $LEXICON_OPTS create "${domain}" TXT --name="_acme-challenge.${domain}." --content="${txtvalue}" --output QUIET
}
@ -108,6 +108,6 @@ dns_lexicon_rm() {
domain=$(printf "%s" "$fulldomain" | cut -d . -f 2-999)
# shellcheck disable=SC2086
$lexicon_cmd "$PROVIDER" $LEXICON_OPTS delete "${domain}" TXT --name="_acme-challenge.${domain}." --content="${txtvalue}"
$lexicon_cmd "$PROVIDER" $LEXICON_OPTS delete "${domain}" TXT --name="_acme-challenge.${domain}." --content="${txtvalue}" --output QUIET
}

2
dnsapi/dns_loopia.sh

@ -217,7 +217,7 @@ _loopia_add_record() {
</member>
<member>
<name>ttl</name>
<value><int>60</int></value>
<value><int>300</int></value>
</member>
<member>
<name>rdata</name>

162
dnsapi/dns_netlify.sh

@ -0,0 +1,162 @@
#!/usr/bin/env sh
#NETLIFY_ACCESS_TOKEN="xxxx"
NETLIFY_HOST="api.netlify.com/api/v1/"
NETLIFY_URL="https://$NETLIFY_HOST"
######## Public functions #####################
#Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_netlify_add() {
fulldomain=$1
txtvalue=$2
NETLIFY_ACCESS_TOKEN="${NETLIFY_ACCESS_TOKEN:-$(_readaccountconf_mutable NETLIFY_ACCESS_TOKEN)}"
if [ -z "$NETLIFY_ACCESS_TOKEN" ]; then
NETLIFY_ACCESS_TOKEN=""
_err "Please specify your Netlify Access Token and try again."
return 1
fi
_info "Using Netlify"
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
_saveaccountconf_mutable NETLIFY_ACCESS_TOKEN "$NETLIFY_ACCESS_TOKEN"
if ! _get_root "$fulldomain" "$accesstoken"; then
_err "invalid domain"
return 1
fi
_debug _domain_id "$_domain_id"
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
dnsRecordURI="dns_zones/$_domain_id/dns_records"
body="{\"type\":\"TXT\", \"hostname\":\"$_sub_domain\", \"value\":\"$txtvalue\", \"ttl\":\"10\"}"
_netlify_rest POST "$dnsRecordURI" "$body" "$NETLIFY_ACCESS_TOKEN"
_code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")"
if [ "$_code" = "200" ] || [ "$_code" = '201' ]; then
_info "validation value added"
return 0
else
_err "error adding validation value ($_code)"
return 1
fi
_err "Not fully implemented!"
return 1
}
#Usage: dns_myapi_rm _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
#Remove the txt record after validation.
dns_netlify_rm() {
_info "Using Netlify"
txtdomain="$1"
txt="$2"
_debug txtdomain "$txtdomain"
_debug txt "$txt"
_saveaccountconf_mutable NETLIFY_ACCESS_TOKEN "$NETLIFY_ACCESS_TOKEN"
if ! _get_root "$txtdomain" "$accesstoken"; then
_err "invalid domain"
return 1
fi
_debug _domain_id "$_domain_id"
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
dnsRecordURI="dns_zones/$_domain_id/dns_records"
_netlify_rest GET "$dnsRecordURI" "" "$NETLIFY_ACCESS_TOKEN"
_record_id=$(echo "$response" | _egrep_o "\"type\":\"TXT\",[^\}]*\"value\":\"$txt\"" | head -n 1 | _egrep_o "\"id\":\"[^\"\}]*\"" | cut -d : -f 2 | tr -d \")
_debug _record_id "$_record_id"
if [ "$_record_id" ]; then
_netlify_rest DELETE "$dnsRecordURI/$_record_id" "" "$NETLIFY_ACCESS_TOKEN"
_code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")"
if [ "$_code" = "200" ] || [ "$_code" = '204' ]; then
_info "validation value removed"
return 0
else
_err "error removing validation value ($_code)"
return 1
fi
return 0
fi
return 1
}
#################### Private functions below ##################################
_get_root() {
domain=$1
accesstoken=$2
i=1
p=1
_netlify_rest GET "dns_zones" "" "$accesstoken"
while true; do
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
_debug2 "Checking domain: $h"
if [ -z "$h" ]; then
#not valid
_err "Invalid domain"
return 1
fi
if _contains "$response" "\"name\":\"$h\"" >/dev/null; then
_domain_id=$(echo "$response" | _egrep_o "\"[^\"]*\",\"name\":\"$h" | cut -d , -f 1 | tr -d \")
if [ "$_domain_id" ]; then
if [ "$i" = 1 ]; then
#create the record at the domain apex (@) if only the domain name was provided as --domain-alias
_sub_domain="@"
else
_sub_domain=$(echo "$domain" | cut -d . -f 1-$p)
fi
_domain=$h
return 0
fi
return 1
fi
p=$i
i=$(_math "$i" + 1)
done
return 1
}
_netlify_rest() {
m=$1
ep="$2"
data="$3"
_debug "$ep"
token_trimmed=$(echo "$NETLIFY_ACCESS_TOKEN" | tr -d '"')
export _H1="Content-Type: application/json"
export _H2="Authorization: Bearer $token_trimmed"
:>"$HTTP_HEADER"
if [ "$m" != "GET" ]; then
_debug data "$data"
response="$(_post "$data" "$NETLIFY_URL$ep" "" "$m")"
else
response="$(_get "$NETLIFY_URL$ep")"
fi
if [ "$?" != "0" ]; then
_err "error $ep"
return 1
fi
_debug2 response "$response"
return 0
}

168
dnsapi/dns_njalla.sh

@ -0,0 +1,168 @@
#!/usr/bin/env sh
#
#NJALLA_Token="sdfsdfsdfljlbjkljlkjsdfoiwje"
NJALLA_Api="https://njal.la/api/1/"
######## Public functions #####################
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_njalla_add() {
fulldomain=$1
txtvalue=$2
NJALLA_Token="${NJALLA_Token:-$(_readaccountconf_mutable NJALLA_Token)}"
if [ "$NJALLA_Token" ]; then
_saveaccountconf_mutable NJALLA_Token "$NJALLA_Token"
else
NJALLA_Token=""
_err "You didn't specify a Njalla api token yet."
return 1
fi
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
# For wildcard cert, the main root domain and the wildcard domain have the same txt subdomain name, so
# we can not use updating anymore.
# count=$(printf "%s\n" "$response" | _egrep_o "\"count\":[^,]*" | cut -d : -f 2)
# _debug count "$count"
# if [ "$count" = "0" ]; then
_info "Adding record"
if _njalla_rest "{\"method\":\"add-record\",\"params\":{\"domain\":\"$_domain\",\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"content\":\"$txtvalue\",\"ttl\":120}}"; then
if _contains "$response" "$txtvalue"; then
_info "Added, OK"
return 0
else
_err "Add txt record error."
return 1
fi
fi
_err "Add txt record error."
return 1
}
#fulldomain txtvalue
dns_njalla_rm() {
fulldomain=$1
txtvalue=$2
NJALLA_Token="${NJALLA_Token:-$(_readaccountconf_mutable NJALLA_Token)}"
if [ "$NJALLA_Token" ]; then
_saveaccountconf_mutable NJALLA_Token "$NJALLA_Token"
else
NJALLA_Token=""
_err "You didn't specify a Njalla api token yet."
return 1
fi
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
_debug "Getting records for domain"
if ! _njalla_rest "{\"method\":\"list-records\",\"params\":{\"domain\":\"${_domain}\"}}"; then
return 1
fi
if ! echo "$response" | tr -d " " | grep "\"id\":" >/dev/null; then
_err "Error: $response"
return 1
fi
records=$(echo "$response" | _egrep_o "\"records\":\s?\[(.*)\]\}" | _egrep_o "\[.*\]" | _egrep_o "\{[^\{\}]*\"id\":[^\{\}]*\}")
count=$(echo "$records" | wc -l)
_debug count "$count"
if [ "$count" = "0" ]; then
_info "Don't need to remove."
else
echo "$records" | while read -r record; do
record_name=$(echo "$record" | _egrep_o "\"name\":\s?\"[^\"]*\"" | cut -d : -f 2 | tr -d " " | tr -d \")
record_content=$(echo "$record" | _egrep_o "\"content\":\s?\"[^\"]*\"" | cut -d : -f 2 | tr -d " " | tr -d \")
record_id=$(echo "$record" | _egrep_o "\"id\":\s?[0-9]+" | cut -d : -f 2 | tr -d " " | tr -d \")
if [ "$_sub_domain" = "$record_name" ]; then
if [ "$txtvalue" = "$record_content" ]; then
_debug "record_id" "$record_id"
if ! _njalla_rest "{\"method\":\"remove-record\",\"params\":{\"domain\":\"${_domain}\",\"id\":${record_id}}}"; then
_err "Delete record error."
return 1
fi
echo "$response" | tr -d " " | grep "\"result\"" >/dev/null
fi
fi
done
fi
}
#################### Private functions below ##################################
#_acme-challenge.www.domain.com
#returns
# _sub_domain=_acme-challenge.www
# _domain=domain.com
# _domain_id=sdjkglgdfewsdfg
_get_root() {
domain=$1
i=1
p=1
while true; do
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
_debug h "$h"
if [ -z "$h" ]; then
#not valid
return 1
fi
if ! _njalla_rest "{\"method\":\"get-domain\",\"params\":{\"domain\":\"${h}\"}}"; then
return 1
fi
if _contains "$response" "\"$h\""; then
_domain_returned=$(echo "$response" | _egrep_o "\{\"name\": *\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \" | tr -d " ")
if [ "$_domain_returned" ]; then
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_domain=$h
return 0
fi
return 1
fi
p=$i
i=$(_math "$i" + 1)
done
return 1
}
_njalla_rest() {
data="$1"
token_trimmed=$(echo "$NJALLA_Token" | tr -d '"')
export _H1="Content-Type: application/json"
export _H2="Accept: application/json"
export _H3="Authorization: Njalla $token_trimmed"
_debug data "$data"
response="$(_post "$data" "$NJALLA_Api" "" "POST")"
if [ "$?" != "0" ]; then
_err "error $data"
return 1
fi
_debug2 response "$response"
return 0
}

170
dnsapi/dns_one.sh

@ -5,8 +5,11 @@
# Author: github: @diseq
# Created: 2019-02-17
# Fixed by: @der-berni
# Modified: 2019-05-31
#
# Modified: 2020-04-07
#
# Use ONECOM_KeepCnameProxy to keep the CNAME DNS record
# export ONECOM_KeepCnameProxy="1"
#
# export ONECOM_User="username"
# export ONECOM_Password="password"
#
@ -30,32 +33,45 @@ dns_one_add() {
return 1
fi
mysubdomain=$_sub_domain
mydomain=$_domain
_debug mysubdomain "$mysubdomain"
_debug mydomain "$mydomain"
subdomain="${_sub_domain}"
maindomain=${_domain}
# get entries
response="$(_get "https://www.one.com/admin/api/domains/$mydomain/dns/custom_records")"
_debug response "$response"
useProxy=0
if [ "${_sub_domain}" = "_acme-challenge" ]; then
subdomain="proxy${_sub_domain}"
useProxy=1
fi
# Update the IP address for domain entry
postdata="{\"type\":\"dns_custom_records\",\"attributes\":{\"priority\":0,\"ttl\":600,\"type\":\"TXT\",\"prefix\":\"$mysubdomain\",\"content\":\"$txtvalue\"}}"
_debug postdata "$postdata"
response="$(_post "$postdata" "https://www.one.com/admin/api/domains/$mydomain/dns/custom_records" "" "POST" "application/json")"
response="$(echo "$response" | _normalizeJson)"
_debug response "$response"
_debug subdomain "$subdomain"
_debug maindomain "$maindomain"
if [ $useProxy -eq 1 ]; then
#Check if the CNAME exists
_dns_one_getrecord "CNAME" "$_sub_domain" "$subdomain.$maindomain"
if [ -z "$id" ]; then
_info "$(__red "Add CNAME Proxy record: '$(__green "\"$_sub_domain\" => \"$subdomain.$maindomain\"")'")"
_dns_one_addrecord "CNAME" "$_sub_domain" "$subdomain.$maindomain"
id=$(echo "$response" | sed -n "s/{\"result\":{\"data\":{\"type\":\"dns_custom_records\",\"id\":\"\([^\"]*\)\",\"attributes\":{\"prefix\":\"$mysubdomain\",\"type\":\"TXT\",\"content\":\"$txtvalue\",\"priority\":0,\"ttl\":600}}},\"metadata\":null}/\1/p")
_info "Not valid yet, let's wait 1 hour to take effect."
_sleep 3600
fi
fi
#Check if the TXT exists
_dns_one_getrecord "TXT" "$subdomain" "$txtvalue"
if [ -n "$id" ]; then
_info "$(__green "Txt record with the same value found. Skip adding.")"
return 0
fi
_dns_one_addrecord "TXT" "$subdomain" "$txtvalue"
if [ -z "$id" ]; then
_err "Add txt record error."
_err "Add TXT record error."
return 1
else
_info "Added, OK ($id)"
_info "$(__green "Added, OK ($id)")"
return 0
fi
}
dns_one_rm() {
@ -73,36 +89,45 @@ dns_one_rm() {
return 1
fi
mysubdomain=$_sub_domain
mydomain=$_domain
_debug mysubdomain "$mysubdomain"
_debug mydomain "$mydomain"
subdomain="${_sub_domain}"
maindomain=${_domain}
# get entries
response="$(_get "https://www.one.com/admin/api/domains/$mydomain/dns/custom_records")"
response="$(echo "$response" | _normalizeJson)"
_debug response "$response"
useProxy=0
if [ "${_sub_domain}" = "_acme-challenge" ]; then
subdomain="proxy${_sub_domain}"
useProxy=1
fi
id=$(printf -- "%s" "$response" | sed -n "s/.*{\"type\":\"dns_custom_records\",\"id\":\"\([^\"]*\)\",\"attributes\":{\"prefix\":\"$mysubdomain\",\"type\":\"TXT\",\"content\":\"$txtvalue\",\"priority\":0,\"ttl\":600}.*/\1/p")
_debug subdomain "$subdomain"
_debug maindomain "$maindomain"
if [ $useProxy -eq 1 ]; then
if [ "$ONECOM_KeepCnameProxy" = "1" ]; then
_info "$(__red "Keeping CNAME Proxy record: '$(__green "\"$_sub_domain\" => \"$subdomain.$maindomain\"")'")"
else
#Check if the CNAME exists
_dns_one_getrecord "CNAME" "$_sub_domain" "$subdomain.$maindomain"
if [ -n "$id" ]; then
_info "$(__red "Removing CNAME Proxy record: '$(__green "\"$_sub_domain\" => \"$subdomain.$maindomain\"")'")"
_dns_one_delrecord "$id"
fi
fi
fi
#Check if the TXT exists
_dns_one_getrecord "TXT" "$subdomain" "$txtvalue"
if [ -z "$id" ]; then
_err "Txt record not found."
return 1
fi
# delete entry
response="$(_post "$postdata" "https://www.one.com/admin/api/domains/$mydomain/dns/custom_records/$id" "" "DELETE" "application/json")"
response="$(echo "$response" | _normalizeJson)"
_debug response "$response"
if [ "$response" = '{"result":null,"metadata":null}' ]; then
_info "Removed, OK"
if _dns_one_delrecord "$id"; then
_info "$(__green Removed, OK)"
return 0
else
_err "Removing txt record error."
return 1
fi
}
#_acme-challenge.www.domain.com
@ -138,6 +163,8 @@ _get_root() {
_dns_one_login() {
# get credentials
ONECOM_KeepCnameProxy="${ONECOM_KeepCnameProxy:-$(_readaccountconf_mutable ONECOM_KeepCnameProxy)}"
ONECOM_KeepCnameProxy="${ONECOM_KeepCnameProxy:-0}"
ONECOM_User="${ONECOM_User:-$(_readaccountconf_mutable ONECOM_User)}"
ONECOM_Password="${ONECOM_Password:-$(_readaccountconf_mutable ONECOM_Password)}"
if [ -z "$ONECOM_User" ] || [ -z "$ONECOM_Password" ]; then
@ -149,6 +176,7 @@ _dns_one_login() {
fi
#save the api key and email to the account conf file.
_saveaccountconf_mutable ONECOM_KeepCnameProxy "$ONECOM_KeepCnameProxy"
_saveaccountconf_mutable ONECOM_User "$ONECOM_User"
_saveaccountconf_mutable ONECOM_Password "$ONECOM_Password"
@ -177,3 +205,75 @@ _dns_one_login() {
return 0
}
_dns_one_getrecord() {
type="$1"
name="$2"
value="$3"
if [ -z "$type" ]; then
type="TXT"
fi
if [ -z "$name" ]; then
_err "Record name is empty."
return 1
fi
response="$(_get "https://www.one.com/admin/api/domains/$maindomain/dns/custom_records")"
response="$(echo "$response" | _normalizeJson)"
_debug response "$response"
if [ -z "${value}" ]; then
id=$(printf -- "%s" "$response" | sed -n "s/.*{\"type\":\"dns_custom_records\",\"id\":\"\([^\"]*\)\",\"attributes\":{\"prefix\":\"${name}\",\"type\":\"${type}\",\"content\":\"[^\"]*\",\"priority\":0,\"ttl\":600}.*/\1/p")
response=$(printf -- "%s" "$response" | sed -n "s/.*{\"type\":\"dns_custom_records\",\"id\":\"[^\"]*\",\"attributes\":{\"prefix\":\"${name}\",\"type\":\"${type}\",\"content\":\"\([^\"]*\)\",\"priority\":0,\"ttl\":600}.*/\1/p")
else
id=$(printf -- "%s" "$response" | sed -n "s/.*{\"type\":\"dns_custom_records\",\"id\":\"\([^\"]*\)\",\"attributes\":{\"prefix\":\"${name}\",\"type\":\"${type}\",\"content\":\"${value}\",\"priority\":0,\"ttl\":600}.*/\1/p")
fi
if [ -z "$id" ]; then
return 1
fi
return 0
}
_dns_one_addrecord() {
type="$1"
name="$2"
value="$3"
if [ -z "$type" ]; then
type="TXT"
fi
if [ -z "$name" ]; then
_err "Record name is empty."
return 1
fi
postdata="{\"type\":\"dns_custom_records\",\"attributes\":{\"priority\":0,\"ttl\":600,\"type\":\"${type}\",\"prefix\":\"${name}\",\"content\":\"${value}\"}}"
_debug postdata "$postdata"
response="$(_post "$postdata" "https://www.one.com/admin/api/domains/$maindomain/dns/custom_records" "" "POST" "application/json")"
response="$(echo "$response" | _normalizeJson)"
_debug response "$response"
id=$(echo "$response" | sed -n "s/{\"result\":{\"data\":{\"type\":\"dns_custom_records\",\"id\":\"\([^\"]*\)\",\"attributes\":{\"prefix\":\"$subdomain\",\"type\":\"TXT\",\"content\":\"$txtvalue\",\"priority\":0,\"ttl\":600}}},\"metadata\":null}/\1/p")
if [ -z "$id" ]; then
return 1
else
return 0
fi
}
_dns_one_delrecord() {
id="$1"
if [ -z "$id" ]; then
return 1
fi
response="$(_post "" "https://www.one.com/admin/api/domains/$maindomain/dns/custom_records/$id" "" "DELETE" "application/json")"
response="$(echo "$response" | _normalizeJson)"
_debug response "$response"
if [ "$response" = '{"result":null,"metadata":null}' ]; then
return 0
else
return 1
fi
}

19
dnsapi/dns_openprovider.sh

@ -59,16 +59,17 @@ dns_openprovider_add() {
break
fi
items="$(echo "$items" | sed "s|${item}||")"
tmpitem="$(echo "$item" | sed 's/\*/\\*/g')"
items="$(echo "$items" | sed "s|${tmpitem}||")"
results_retrieved="$(_math "$results_retrieved" + 1)"
new_item="$(echo "$item" | sed -n 's/.*<item>.*\(<name>\(.*\)\.'"$_domain_name"'\.'"$_domain_extension"'<\/name>.*\(<type>.*<\/type>\).*\(<value>.*<\/value>\).*\(<prio>.*<\/prio>\).*\(<ttl>.*<\/ttl>\)\).*<\/item>.*/<item><name>\2<\/name>\3\4\5\6<\/item>/p')"
if [ -z "$new_item" ]; then
# Base record
# Domain apex
new_item="$(echo "$item" | sed -n 's/.*<item>.*\(<name>\(.*\)'"$_domain_name"'\.'"$_domain_extension"'<\/name>.*\(<type>.*<\/type>\).*\(<value>.*<\/value>\).*\(<prio>.*<\/prio>\).*\(<ttl>.*<\/ttl>\)\).*<\/item>.*/<item><name>\2<\/name>\3\4\5\6<\/item>/p')"
fi
if [ -z "$(echo "$new_item" | _egrep_o ".*<type>(A|AAAA|CNAME|MX|SPF|SRV|TXT|TLSA|SSHFP|CAA)<\/type>.*")" ]; then
if [ -z "$(echo "$new_item" | _egrep_o ".*<type>(A|AAAA|CNAME|MX|SPF|SRV|TXT|TLSA|SSHFP|CAA|NS)<\/type>.*")" ]; then
_debug "not an allowed record type, skipping" "$new_item"
continue
fi
@ -86,7 +87,7 @@ dns_openprovider_add() {
_debug "Creating acme record"
acme_record="$(echo "$fulldomain" | sed -e "s/.$_domain_name.$_domain_extension$//")"
_openprovider_request "$(printf '<modifyZoneDnsRequest><domain><name>%s</name><extension>%s</extension></domain><type>master</type><records><array>%s<item><name>%s</name><type>TXT</type><value>%s</value><ttl>86400</ttl></item></array></records></modifyZoneDnsRequest>' "$_domain_name" "$_domain_extension" "$existing_items" "$acme_record" "$txtvalue")"
_openprovider_request "$(printf '<modifyZoneDnsRequest><domain><name>%s</name><extension>%s</extension></domain><type>master</type><records><array>%s<item><name>%s</name><type>TXT</type><value>%s</value><ttl>600</ttl></item></array></records></modifyZoneDnsRequest>' "$_domain_name" "$_domain_extension" "$existing_items" "$acme_record" "$txtvalue")"
return 0
}
@ -136,7 +137,8 @@ dns_openprovider_rm() {
break
fi
items="$(echo "$items" | sed "s|${item}||")"
tmpitem="$(echo "$item" | sed 's/\*/\\*/g')"
items="$(echo "$items" | sed "s|${tmpitem}||")"
results_retrieved="$(_math "$results_retrieved" + 1)"
if ! echo "$item" | grep -v "$fulldomain"; then
@ -147,11 +149,11 @@ dns_openprovider_rm() {
new_item="$(echo "$item" | sed -n 's/.*<item>.*\(<name>\(.*\)\.'"$_domain_name"'\.'"$_domain_extension"'<\/name>.*\(<type>.*<\/type>\).*\(<value>.*<\/value>\).*\(<prio>.*<\/prio>\).*\(<ttl>.*<\/ttl>\)\).*<\/item>.*/<item><name>\2<\/name>\3\4\5\6<\/item>/p')"
if [ -z "$new_item" ]; then
# Base record
# domain apex
new_item="$(echo "$item" | sed -n 's/.*<item>.*\(<name>\(.*\)'"$_domain_name"'\.'"$_domain_extension"'<\/name>.*\(<type>.*<\/type>\).*\(<value>.*<\/value>\).*\(<prio>.*<\/prio>\).*\(<ttl>.*<\/ttl>\)\).*<\/item>.*/<item><name>\2<\/name>\3\4\5\6<\/item>/p')"
fi
if [ -z "$(echo "$new_item" | _egrep_o ".*<type>(A|AAAA|CNAME|MX|SPF|SRV|TXT|TLSA|SSHFP|CAA)<\/type>.*")" ]; then
if [ -z "$(echo "$new_item" | _egrep_o ".*<type>(A|AAAA|CNAME|MX|SPF|SRV|TXT|TLSA|SSHFP|CAA|NS)<\/type>.*")" ]; then
_debug "not an allowed record type, skipping" "$new_item"
continue
fi
@ -205,7 +207,8 @@ _get_root() {
break
fi
items="$(echo "$items" | sed "s|${item}||")"
tmpitem="$(echo "$item" | sed 's/\*/\\*/g')"
items="$(echo "$items" | sed "s|${tmpitem}||")"
results_retrieved="$(_math "$results_retrieved" + 1)"

348
dnsapi/dns_openstack.sh

@ -0,0 +1,348 @@
#!/usr/bin/env sh
# OpenStack Designate API plugin
#
# This requires you to have OpenStackClient and python-desginateclient
# installed.
#
# You will require Keystone V3 credentials loaded into your environment, which
# could be either password or v3applicationcredential type.
#
# Author: Andy Botting <andy@andybotting.com>
######## Public functions #####################
# Usage: dns_openstack_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_openstack_add() {
fulldomain=$1
txtvalue=$2
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
_dns_openstack_credentials || return $?
_dns_openstack_check_setup || return $?
_dns_openstack_find_zone || return $?
_dns_openstack_get_recordset || return $?
_debug _recordset_id "$_recordset_id"
if [ -n "$_recordset_id" ]; then
_dns_openstack_get_records || return $?
_debug _records "$_records"
fi
_dns_openstack_create_recordset || return $?
}
# Usage: dns_openstack_rm _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
# Remove the txt record after validation.
dns_openstack_rm() {
fulldomain=$1
txtvalue=$2
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
_dns_openstack_credentials || return $?
_dns_openstack_check_setup || return $?
_dns_openstack_find_zone || return $?
_dns_openstack_get_recordset || return $?
_debug _recordset_id "$_recordset_id"
if [ -n "$_recordset_id" ]; then
_dns_openstack_get_records || return $?
_debug _records "$_records"
fi
_dns_openstack_delete_recordset || return $?
}
#################### Private functions below ##################################
_dns_openstack_create_recordset() {
if [ -z "$_recordset_id" ]; then
_info "Creating a new recordset"
if ! _recordset_id=$(openstack recordset create -c id -f value --type TXT --record "$txtvalue" "$_zone_id" "$fulldomain."); then
_err "No recordset ID found after create"
return 1
fi
else
_info "Updating existing recordset"
# Build new list of --record <rec> args for update
_record_args="--record $txtvalue"
for _rec in $_records; do
_record_args="$_record_args --record $_rec"
done
# shellcheck disable=SC2086
if ! _recordset_id=$(openstack recordset set -c id -f value $_record_args "$_zone_id" "$fulldomain."); then
_err "Recordset update failed"
return 1
fi
fi
_max_retries=60
_sleep_sec=5
_retry_times=0
while [ "$_retry_times" -lt "$_max_retries" ]; do
_retry_times=$(_math "$_retry_times" + 1)
_debug3 _retry_times "$_retry_times"
_record_status=$(openstack recordset show -c status -f value "$_zone_id" "$_recordset_id")
_info "Recordset status is $_record_status"
if [ "$_record_status" = "ACTIVE" ]; then
return 0
elif [ "$_record_status" = "ERROR" ]; then
return 1
else
_sleep $_sleep_sec
fi
done
_err "Recordset failed to become ACTIVE"
return 1
}
_dns_openstack_delete_recordset() {
if [ "$_records" = "$txtvalue" ]; then
_info "Only one record found, deleting recordset"
if ! openstack recordset delete "$_zone_id" "$fulldomain." >/dev/null; then
_err "Failed to delete recordset"
return 1
fi
else
_info "Found existing records, updating recordset"
# Build new list of --record <rec> args for update
_record_args=""
for _rec in $_records; do
if [ "$_rec" = "$txtvalue" ]; then
continue
fi
_record_args="$_record_args --record $_rec"
done
# shellcheck disable=SC2086
if ! openstack recordset set -c id -f value $_record_args "$_zone_id" "$fulldomain." >/dev/null; then
_err "Recordset update failed"
return 1
fi
fi
}
_dns_openstack_get_root() {
# Take the full fqdn and strip away pieces until we get an exact zone name
# match. For example, _acme-challenge.something.domain.com might need to go
# into something.domain.com or domain.com
_zone_name=$1
_zone_list=$2
while [ "$_zone_name" != "" ]; do
_zone_name="$(echo "$_zone_name" | sed 's/[^.]*\.*//')"
echo "$_zone_list" | while read -r id name; do
if _startswith "$_zone_name." "$name"; then
echo "$id"
fi
done
done | _head_n 1
}
_dns_openstack_find_zone() {
if ! _zone_list="$(openstack zone list -c id -c name -f value)"; then
_err "Can't list zones. Check your OpenStack credentials"
return 1
fi
_debug _zone_list "$_zone_list"
if ! _zone_id="$(_dns_openstack_get_root "$fulldomain" "$_zone_list")"; then
_err "Can't find a matching zone. Check your OpenStack credentials"
return 1
fi
_debug _zone_id "$_zone_id"
}
_dns_openstack_get_records() {
if ! _records=$(openstack recordset show -c records -f value "$_zone_id" "$fulldomain."); then
_err "Failed to get records"
return 1
fi
return 0
}
_dns_openstack_get_recordset() {
if ! _recordset_id=$(openstack recordset list -c id -f value --name "$fulldomain." "$_zone_id"); then
_err "Failed to get recordset"
return 1
fi
return 0
}
_dns_openstack_check_setup() {
if ! _exists openstack; then
_err "OpenStack client not found"
return 1
fi
}
_dns_openstack_credentials() {
_debug "Check OpenStack credentials"
# If we have OS_AUTH_URL already set in the environment, then assume we want
# to use those, otherwise use stored credentials
if [ -n "$OS_AUTH_URL" ]; then
_debug "OS_AUTH_URL env var found, using environment"
else
_debug "OS_AUTH_URL not found, loading stored credentials"
OS_AUTH_URL="${OS_AUTH_URL:-$(_readaccountconf_mutable OS_AUTH_URL)}"
OS_IDENTITY_API_VERSION="${OS_IDENTITY_API_VERSION:-$(_readaccountconf_mutable OS_IDENTITY_API_VERSION)}"
OS_AUTH_TYPE="${OS_AUTH_TYPE:-$(_readaccountconf_mutable OS_AUTH_TYPE)}"
OS_APPLICATION_CREDENTIAL_ID="${OS_APPLICATION_CREDENTIAL_ID:-$(_readaccountconf_mutable OS_APPLICATION_CREDENTIAL_ID)}"
OS_APPLICATION_CREDENTIAL_SECRET="${OS_APPLICATION_CREDENTIAL_SECRET:-$(_readaccountconf_mutable OS_APPLICATION_CREDENTIAL_SECRET)}"
OS_USERNAME="${OS_USERNAME:-$(_readaccountconf_mutable OS_USERNAME)}"
OS_PASSWORD="${OS_PASSWORD:-$(_readaccountconf_mutable OS_PASSWORD)}"
OS_PROJECT_NAME="${OS_PROJECT_NAME:-$(_readaccountconf_mutable OS_PROJECT_NAME)}"
OS_PROJECT_ID="${OS_PROJECT_ID:-$(_readaccountconf_mutable OS_PROJECT_ID)}"
OS_USER_DOMAIN_NAME="${OS_USER_DOMAIN_NAME:-$(_readaccountconf_mutable OS_USER_DOMAIN_NAME)}"
OS_USER_DOMAIN_ID="${OS_USER_DOMAIN_ID:-$(_readaccountconf_mutable OS_USER_DOMAIN_ID)}"
OS_PROJECT_DOMAIN_NAME="${OS_PROJECT_DOMAIN_NAME:-$(_readaccountconf_mutable OS_PROJECT_DOMAIN_NAME)}"
OS_PROJECT_DOMAIN_ID="${OS_PROJECT_DOMAIN_ID:-$(_readaccountconf_mutable OS_PROJECT_DOMAIN_ID)}"
fi
# Check each var and either save or clear it depending on whether its set.
# The helps us clear out old vars in the case where a user may want
# to switch between password and app creds
_debug "OS_AUTH_URL" "$OS_AUTH_URL"
if [ -n "$OS_AUTH_URL" ]; then
export OS_AUTH_URL
_saveaccountconf_mutable OS_AUTH_URL "$OS_AUTH_URL"
else
unset OS_AUTH_URL
_clearaccountconf SAVED_OS_AUTH_URL
fi
_debug "OS_IDENTITY_API_VERSION" "$OS_IDENTITY_API_VERSION"
if [ -n "$OS_IDENTITY_API_VERSION" ]; then
export OS_IDENTITY_API_VERSION
_saveaccountconf_mutable OS_IDENTITY_API_VERSION "$OS_IDENTITY_API_VERSION"
else
unset OS_IDENTITY_API_VERSION
_clearaccountconf SAVED_OS_IDENTITY_API_VERSION
fi
_debug "OS_AUTH_TYPE" "$OS_AUTH_TYPE"
if [ -n "$OS_AUTH_TYPE" ]; then
export OS_AUTH_TYPE
_saveaccountconf_mutable OS_AUTH_TYPE "$OS_AUTH_TYPE"
else
unset OS_AUTH_TYPE
_clearaccountconf SAVED_OS_AUTH_TYPE
fi
_debug "OS_APPLICATION_CREDENTIAL_ID" "$OS_APPLICATION_CREDENTIAL_ID"
if [ -n "$OS_APPLICATION_CREDENTIAL_ID" ]; then
export OS_APPLICATION_CREDENTIAL_ID
_saveaccountconf_mutable OS_APPLICATION_CREDENTIAL_ID "$OS_APPLICATION_CREDENTIAL_ID"
else
unset OS_APPLICATION_CREDENTIAL_ID
_clearaccountconf SAVED_OS_APPLICATION_CREDENTIAL_ID
fi
_secure_debug "OS_APPLICATION_CREDENTIAL_SECRET" "$OS_APPLICATION_CREDENTIAL_SECRET"
if [ -n "$OS_APPLICATION_CREDENTIAL_SECRET" ]; then
export OS_APPLICATION_CREDENTIAL_SECRET
_saveaccountconf_mutable OS_APPLICATION_CREDENTIAL_SECRET "$OS_APPLICATION_CREDENTIAL_SECRET"
else
unset OS_APPLICATION_CREDENTIAL_SECRET
_clearaccountconf SAVED_OS_APPLICATION_CREDENTIAL_SECRET
fi
_debug "OS_USERNAME" "$OS_USERNAME"
if [ -n "$OS_USERNAME" ]; then
export OS_USERNAME
_saveaccountconf_mutable OS_USERNAME "$OS_USERNAME"
else
unset OS_USERNAME
_clearaccountconf SAVED_OS_USERNAME
fi
_secure_debug "OS_PASSWORD" "$OS_PASSWORD"
if [ -n "$OS_PASSWORD" ]; then
export OS_PASSWORD
_saveaccountconf_mutable OS_PASSWORD "$OS_PASSWORD"
else
unset OS_PASSWORD
_clearaccountconf SAVED_OS_PASSWORD
fi
_debug "OS_PROJECT_NAME" "$OS_PROJECT_NAME"
if [ -n "$OS_PROJECT_NAME" ]; then
export OS_PROJECT_NAME
_saveaccountconf_mutable OS_PROJECT_NAME "$OS_PROJECT_NAME"
else
unset OS_PROJECT_NAME
_clearaccountconf SAVED_OS_PROJECT_NAME
fi
_debug "OS_PROJECT_ID" "$OS_PROJECT_ID"
if [ -n "$OS_PROJECT_ID" ]; then
export OS_PROJECT_ID
_saveaccountconf_mutable OS_PROJECT_ID "$OS_PROJECT_ID"
else
unset OS_PROJECT_ID
_clearaccountconf SAVED_OS_PROJECT_ID
fi
_debug "OS_USER_DOMAIN_NAME" "$OS_USER_DOMAIN_NAME"
if [ -n "$OS_USER_DOMAIN_NAME" ]; then
export OS_USER_DOMAIN_NAME
_saveaccountconf_mutable OS_USER_DOMAIN_NAME "$OS_USER_DOMAIN_NAME"
else
unset OS_USER_DOMAIN_NAME
_clearaccountconf SAVED_OS_USER_DOMAIN_NAME
fi
_debug "OS_USER_DOMAIN_ID" "$OS_USER_DOMAIN_ID"
if [ -n "$OS_USER_DOMAIN_ID" ]; then
export OS_USER_DOMAIN_ID
_saveaccountconf_mutable OS_USER_DOMAIN_ID "$OS_USER_DOMAIN_ID"
else
unset OS_USER_DOMAIN_ID
_clearaccountconf SAVED_OS_USER_DOMAIN_ID
fi
_debug "OS_PROJECT_DOMAIN_NAME" "$OS_PROJECT_DOMAIN_NAME"
if [ -n "$OS_PROJECT_DOMAIN_NAME" ]; then
export OS_PROJECT_DOMAIN_NAME
_saveaccountconf_mutable OS_PROJECT_DOMAIN_NAME "$OS_PROJECT_DOMAIN_NAME"
else
unset OS_PROJECT_DOMAIN_NAME
_clearaccountconf SAVED_OS_PROJECT_DOMAIN_NAME
fi
_debug "OS_PROJECT_DOMAIN_ID" "$OS_PROJECT_DOMAIN_ID"
if [ -n "$OS_PROJECT_DOMAIN_ID" ]; then
export OS_PROJECT_DOMAIN_ID
_saveaccountconf_mutable OS_PROJECT_DOMAIN_ID "$OS_PROJECT_DOMAIN_ID"
else
unset OS_PROJECT_DOMAIN_ID
_clearaccountconf SAVED_OS_PROJECT_DOMAIN_ID
fi
if [ "$OS_AUTH_TYPE" = "v3applicationcredential" ]; then
# Application Credential auth
if [ -z "$OS_APPLICATION_CREDENTIAL_ID" ] || [ -z "$OS_APPLICATION_CREDENTIAL_SECRET" ]; then
_err "When using OpenStack application credentials, OS_APPLICATION_CREDENTIAL_ID"
_err "and OS_APPLICATION_CREDENTIAL_SECRET must be set."
_err "Please check your credentials and try again."
return 1
fi
else
# Password auth
if [ -z "$OS_USERNAME" ] || [ -z "$OS_PASSWORD" ]; then
_err "OpenStack username or password not found."
_err "Please check your credentials and try again."
return 1
fi
if [ -z "$OS_PROJECT_NAME" ] && [ -z "$OS_PROJECT_ID" ]; then
_err "When using password authentication, OS_PROJECT_NAME or"
_err "OS_PROJECT_ID must be set."
_err "Please check your credentials and try again."
return 1
fi
fi
return 0
}

2
dnsapi/dns_opnsense.sh

@ -150,7 +150,7 @@ _get_root() {
return 1
fi
_debug h "$h"
id=$(echo "$_domain_response" | _egrep_o "\"[^\"]*\":{\"enabled\":\"1\",\"type\":{\"master\":{\"value\":\"master\",\"selected\":1},\"slave\":{\"value\":\"slave\",\"selected\":0}},\"masterip\":\"[^\"]*\",\"domainname\":\"${h}\"" | cut -d ':' -f 1 | cut -d '"' -f 2)
id=$(echo "$_domain_response" | _egrep_o "\"[^\"]*\":{\"enabled\":\"1\",\"type\":{\"master\":{\"value\":\"master\",\"selected\":1},\"slave\":{\"value\":\"slave\",\"selected\":0}},\"masterip\":\"[^\"]*\"(,\"allownotifyslave\":{\"\":{[^}]*}},|,)\"domainname\":\"${h}\"" | cut -d ':' -f 1 | cut -d '"' -f 2)
if [ -n "$id" ]; then
_debug id "$id"

2
dnsapi/dns_rackspace.sh

@ -73,7 +73,7 @@ _get_root_zone() {
#not valid
return 1
fi
if ! _rackspace_rest GET "$RACKSPACE_Tenant/domains"; then
if ! _rackspace_rest GET "$RACKSPACE_Tenant/domains/search?name=$h"; then
return 1
fi
_debug2 response "$response"

67
dnsapi/dns_regru.sh

@ -5,7 +5,6 @@
#
# REGRU_API_Password="test"
#
_domain=$_domain
REGRU_API_URL="https://api.reg.ru/api/regru2"
@ -27,10 +26,17 @@ dns_regru_add() {
_saveaccountconf_mutable REGRU_API_Username "$REGRU_API_Username"
_saveaccountconf_mutable REGRU_API_Password "$REGRU_API_Password"
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug _domain "$_domain"
_info "Adding TXT record to ${fulldomain}"
response="$(_get "$REGRU_API_URL/zone/add_txt?input_data={%22username%22:%22${REGRU_API_Username}%22,%22password%22:%22${REGRU_API_Password}%22,%22domains%22:[{%22dname%22:%22${_domain}%22}],%22subdomain%22:%22_acme-challenge%22,%22text%22:%22${txtvalue}%22,%22output_content_type%22:%22plain%22}&input_format=json")"
_regru_rest POST "zone/add_txt" "input_data={%22username%22:%22${REGRU_API_Username}%22,%22password%22:%22${REGRU_API_Password}%22,%22domains%22:[{%22dname%22:%22${_domain}%22}],%22subdomain%22:%22_acme-challenge%22,%22text%22:%22${txtvalue}%22,%22output_content_type%22:%22plain%22}&input_format=json"
if _contains "${response}" 'success'; then
if ! _contains "${response}" 'error'; then
return 0
fi
_err "Could not create resource record, check logs"
@ -51,13 +57,64 @@ dns_regru_rm() {
return 1
fi
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug _domain "$_domain"
_info "Deleting resource record $fulldomain"
response="$(_get "$REGRU_API_URL/zone/remove_record?input_data={%22username%22:%22${REGRU_API_Username}%22,%22password%22:%22${REGRU_API_Password}%22,%22domains%22:[{%22dname%22:%22${_domain}%22}],%22subdomain%22:%22_acme-challenge%22,%22content%22:%22${txtvalue}%22,%22record_type%22:%22TXT%22,%22output_content_type%22:%22plain%22}&input_format=json")"
_regru_rest POST "zone/remove_record" "input_data={%22username%22:%22${REGRU_API_Username}%22,%22password%22:%22${REGRU_API_Password}%22,%22domains%22:[{%22dname%22:%22${_domain}%22}],%22subdomain%22:%22_acme-challenge%22,%22content%22:%22${txtvalue}%22,%22record_type%22:%22TXT%22,%22output_content_type%22:%22plain%22}&input_format=json"
if _contains "${response}" 'success'; then
if ! _contains "${response}" 'error'; then
return 0
fi
_err "Could not delete resource record, check logs"
_err "${response}"
return 1
}
#################### Private functions below ##################################
#_acme-challenge.www.domain.com
#returns
# _domain=domain.com
_get_root() {
domain=$1
_regru_rest POST "service/get_list" "username=${REGRU_API_Username}&password=${REGRU_API_Password}&output_format=xml&servtype=domain"
domains_list=$(echo "${response}" | grep dname | sed -r "s/.*dname=\"([^\"]+)\".*/\\1/g")
for ITEM in ${domains_list}; do
case "${domain}" in
*${ITEM}*)
_domain=${ITEM}
_debug _domain "${_domain}"
return 0
;;
esac
done
return 1
}
#returns
# response
_regru_rest() {
m=$1
ep="$2"
data="$3"
_debug "$ep"
export _H1="Content-Type: application/x-www-form-urlencoded"
if [ "$m" != "GET" ]; then
_debug data "$data"
response="$(_post "$data" "$REGRU_API_URL/$ep" "" "$m")"
else
response="$(_get "$REGRU_API_URL/$ep?$data")"
fi
_debug response "${response}"
return 0
}

162
dnsapi/dns_transip.sh

@ -0,0 +1,162 @@
#!/usr/bin/env sh
TRANSIP_Api_Url="https://api.transip.nl/v6"
TRANSIP_Token_Read_Only="false"
TRANSIP_Token_Global_Key="false"
TRANSIP_Token_Expiration="30 minutes"
# You can't reuse a label token, so we leave this empty normally
TRANSIP_Token_Label=""
######## Public functions #####################
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_transip_add() {
fulldomain="$1"
_debug fulldomain="$fulldomain"
txtvalue="$2"
_debug txtvalue="$txtvalue"
_transip_setup "$fulldomain" || return 1
_info "Creating TXT record."
if ! _transip_rest POST "domains/$_domain/dns" "{\"dnsEntry\":{\"name\":\"$_sub_domain\",\"type\":\"TXT\",\"content\":\"$txtvalue\",\"expire\":300}}"; then
_err "Could not add TXT record."
return 1
fi
return 0
}
dns_transip_rm() {
fulldomain=$1
_debug fulldomain="$fulldomain"
txtvalue=$2
_debug txtvalue="$txtvalue"
_transip_setup "$fulldomain" || return 1
_info "Removing TXT record."
if ! _transip_rest DELETE "domains/$_domain/dns" "{\"dnsEntry\":{\"name\":\"$_sub_domain\",\"type\":\"TXT\",\"content\":\"$txtvalue\",\"expire\":300}}"; then
_err "Could not remove TXT record $_sub_domain for $domain"
return 1
fi
return 0
}
#################### Private functions below ##################################
#_acme-challenge.www.domain.com
#returns
# _sub_domain=_acme-challenge.www
# _domain=domain.com
_get_root() {
domain="$1"
i=2
p=1
while true; do
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
if [ -z "$h" ]; then
#not valid
return 1
fi
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_domain="$h"
if _transip_rest GET "domains/$h/dns" && _contains "$response" "dnsEntries"; then
return 0
fi
p=$i
i=$(_math "$i" + 1)
done
_err "Unable to parse this domain"
return 1
}
_transip_rest() {
m="$1"
ep="$2"
data="$3"
_debug ep "$ep"
export _H1="Accept: application/json"
export _H2="Authorization: Bearer $_token"
export _H4="Content-Type: application/json"
if [ "$m" != "GET" ]; then
_debug data "$data"
response="$(_post "$data" "$TRANSIP_Api_Url/$ep" "" "$m")"
retcode=$?
else
response="$(_get "$TRANSIP_Api_Url/$ep")"
retcode=$?
fi
if [ "$retcode" != "0" ]; then
_err "error $ep"
return 1
fi
_debug2 response "$response"
return 0
}
_transip_get_token() {
nonce=$(echo "TRANSIP$(_time)" | _digest sha1 hex | cut -c 1-32)
_debug nonce "$nonce"
data="{\"login\":\"${TRANSIP_Username}\",\"nonce\":\"${nonce}\",\"read_only\":\"${TRANSIP_Token_Read_Only}\",\"expiration_time\":\"${TRANSIP_Token_Expiration}\",\"label\":\"${TRANSIP_Token_Label}\",\"global_key\":\"${TRANSIP_Token_Global_Key}\"}"
_debug data "$data"
#_signature=$(printf "%s" "$data" | openssl dgst -sha512 -sign "$TRANSIP_Key_File" | _base64)
_signature=$(printf "%s" "$data" | _sign "$TRANSIP_Key_File" "sha512")
_debug2 _signature "$_signature"
export _H1="Signature: $_signature"
export _H2="Content-Type: application/json"
response="$(_post "$data" "$TRANSIP_Api_Url/auth" "" "POST")"
retcode=$?
_debug2 response "$response"
if [ "$retcode" != "0" ]; then
_err "Authentication failed."
return 1
fi
if _contains "$response" "token"; then
_token="$(echo "$response" | _normalizeJson | sed -n 's/^{"token":"\(.*\)"}/\1/p')"
_debug _token "$_token"
return 0
fi
return 1
}
_transip_setup() {
fulldomain=$1
# retrieve the transip creds
TRANSIP_Username="${TRANSIP_Username:-$(_readaccountconf_mutable TRANSIP_Username)}"
TRANSIP_Key_File="${TRANSIP_Key_File:-$(_readaccountconf_mutable TRANSIP_Key_File)}"
# check their vals for null
if [ -z "$TRANSIP_Username" ] || [ -z "$TRANSIP_Key_File" ]; then
TRANSIP_Username=""
TRANSIP_Key_File=""
_err "You didn't specify a TransIP username and api key file location"
_err "Please set those values and try again."
return 1
fi
# save the username and api key to the account conf file.
_saveaccountconf_mutable TRANSIP_Username "$TRANSIP_Username"
_saveaccountconf_mutable TRANSIP_Key_File "$TRANSIP_Key_File"
if [ -f "$TRANSIP_Key_File" ]; then
if ! grep "BEGIN PRIVATE KEY" "$TRANSIP_Key_File" >/dev/null 2>&1; then
_err "Key file doesn't seem to be a valid key: ${TRANSIP_Key_File}"
return 1
fi
else
_err "Can't read private key file: ${TRANSIP_Key_File}"
return 1
fi
if [ -z "$_token" ]; then
if ! _transip_get_token; then
_err "Can not get token."
return 1
fi
fi
_get_root "$fulldomain" || return 1
return 0
}

8
dnsapi/dns_unoeuro.sh

@ -5,7 +5,7 @@
#
#UNO_User="UExxxxxx"
Uno_Api="https://api.unoeuro.com/1"
Uno_Api="https://api.simply.com/1"
######## Public functions #####################
@ -24,12 +24,6 @@ dns_unoeuro_add() {
return 1
fi
if ! _contains "$UNO_User" "UE"; then
_err "It seems that the UNO_User=$UNO_User is not a valid username."
_err "Please check and retry."
return 1
fi
#save the api key and email to the account conf file.
_saveaccountconf_mutable UNO_Key "$UNO_Key"
_saveaccountconf_mutable UNO_User "$UNO_User"

2
dnsapi/dns_variomedia.sh

@ -107,7 +107,7 @@ _get_root() {
fi
if _startswith "$response" "\{\"data\":"; then
if _contains "$response" "\"id\": \"$h\""; then
if _contains "$response" "\"id\":\"$h\""; then
_sub_domain="$(echo "$fulldomain" | sed "s/\\.$h\$//")"
_domain=$h
return 0

2
dnsapi/dns_yandex.sh

@ -25,7 +25,7 @@ dns_yandex_add() {
_PDD_get_record_ids || return 1
_debug "Record_ids: $record_ids"
if [ ! -z "$record_ids" ]; then
if [ -n "$record_ids" ]; then
_info "All existing $subdomain records from $domain will be removed at the very end."
fi

51
notify/mail.sh

@ -6,6 +6,7 @@
#MAIL_FROM="yyyy@gmail.com"
#MAIL_TO="yyyy@gmail.com"
#MAIL_NOVALIDATE=""
#MAIL_MSMTP_ACCOUNT=""
mail_send() {
_subject="$1"
@ -76,18 +77,17 @@ mail_send() {
}
_mail_bin() {
if [ -n "$MAIL_BIN" ]; then
_MAIL_BIN="$MAIL_BIN"
elif _exists "sendmail"; then
_MAIL_BIN="sendmail"
elif _exists "ssmtp"; then
_MAIL_BIN="ssmtp"
elif _exists "mutt"; then
_MAIL_BIN="mutt"
elif _exists "mail"; then
_MAIL_BIN="mail"
else
_err "Please install sendmail, ssmtp, mutt or mail first."
_MAIL_BIN=""
for b in "$MAIL_BIN" sendmail ssmtp mutt mail msmtp; do
if _exists "$b"; then
_MAIL_BIN="$b"
break
fi
done
if [ -z "$_MAIL_BIN" ]; then
_err "Please install sendmail, ssmtp, mutt, mail or msmtp first."
return 1
fi
@ -95,30 +95,35 @@ _mail_bin() {
}
_mail_cmnd() {
_MAIL_ARGS=""
case $(basename "$_MAIL_BIN") in
sendmail)
if [ -n "$MAIL_FROM" ]; then
echo "'$_MAIL_BIN' -f '$MAIL_FROM' '$MAIL_TO'"
else
echo "'$_MAIL_BIN' '$MAIL_TO'"
_MAIL_ARGS="-f '$MAIL_FROM'"
fi
;;
ssmtp)
echo "'$_MAIL_BIN' '$MAIL_TO'"
;;
mutt | mail)
echo "'$_MAIL_BIN' -s '$_subject' '$MAIL_TO'"
_MAIL_ARGS="-s '$_subject'"
;;
*)
_err "Command $MAIL_BIN is not supported, use sendmail, ssmtp, mutt or mail."
return 1
msmtp)
if [ -n "$MAIL_FROM" ]; then
_MAIL_ARGS="-f '$MAIL_FROM'"
fi
if [ -n "$MAIL_MSMTP_ACCOUNT" ]; then
_MAIL_ARGS="$_MAIL_ARGS -a '$MAIL_MSMTP_ACCOUNT'"
fi
;;
*) ;;
esac
echo "'$_MAIL_BIN' $_MAIL_ARGS '$MAIL_TO'"
}
_mail_body() {
case $(basename "$_MAIL_BIN") in
sendmail | ssmtp)
sendmail | ssmtp | msmtp)
if [ -n "$MAIL_FROM" ]; then
echo "From: $MAIL_FROM"
fi

86
notify/teams.sh

@ -0,0 +1,86 @@
#!/usr/bin/env sh
#Support Microsoft Teams webhooks
#TEAMS_WEBHOOK_URL=""
#TEAMS_THEME_COLOR=""
#TEAMS_SUCCESS_COLOR=""
#TEAMS_ERROR_COLOR=""
#TEAMS_SKIP_COLOR=""
teams_send() {
_subject="$1"
_content="$2"
_statusCode="$3" #0: success, 1: error 2($RENEW_SKIP): skipped
_debug "_statusCode" "$_statusCode"
_color_success="2cbe4e" # green
_color_danger="cb2431" # red
_color_muted="586069" # gray
TEAMS_WEBHOOK_URL="${TEAMS_WEBHOOK_URL:-$(_readaccountconf_mutable TEAMS_WEBHOOK_URL)}"
if [ -z "$TEAMS_WEBHOOK_URL" ]; then
TEAMS_WEBHOOK_URL=""
_err "You didn't specify a Microsoft Teams webhook url TEAMS_WEBHOOK_URL yet."
return 1
fi
_saveaccountconf_mutable TEAMS_WEBHOOK_URL "$TEAMS_WEBHOOK_URL"
TEAMS_THEME_COLOR="${TEAMS_THEME_COLOR:-$(_readaccountconf_mutable TEAMS_THEME_COLOR)}"
if [ -n "$TEAMS_THEME_COLOR" ]; then
_saveaccountconf_mutable TEAMS_THEME_COLOR "$TEAMS_THEME_COLOR"
fi
TEAMS_SUCCESS_COLOR="${TEAMS_SUCCESS_COLOR:-$(_readaccountconf_mutable TEAMS_SUCCESS_COLOR)}"
if [ -n "$TEAMS_SUCCESS_COLOR" ]; then
_saveaccountconf_mutable TEAMS_SUCCESS_COLOR "$TEAMS_SUCCESS_COLOR"
fi
TEAMS_ERROR_COLOR="${TEAMS_ERROR_COLOR:-$(_readaccountconf_mutable TEAMS_ERROR_COLOR)}"
if [ -n "$TEAMS_ERROR_COLOR" ]; then
_saveaccountconf_mutable TEAMS_ERROR_COLOR "$TEAMS_ERROR_COLOR"
fi
TEAMS_SKIP_COLOR="${TEAMS_SKIP_COLOR:-$(_readaccountconf_mutable TEAMS_SKIP_COLOR)}"
if [ -n "$TEAMS_SKIP_COLOR" ]; then
_saveaccountconf_mutable TEAMS_SKIP_COLOR "$TEAMS_SKIP_COLOR"
fi
export _H1="Content-Type: application/json"
_subject=$(echo "$_subject" | _json_encode)
_content=$(echo "$_content" | _json_encode)
case "$_statusCode" in
0)
_color="${TEAMS_SUCCESS_COLOR:-$_color_success}"
;;
1)
_color="${TEAMS_ERROR_COLOR:-$_color_danger}"
;;
2)
_color="${TEAMS_SKIP_COLOR:-$_color_muted}"
;;
esac
_color=$(echo "$_color" | tr -cd 'a-fA-F0-9')
if [ -z "$_color" ]; then
_color=$(echo "${TEAMS_THEME_COLOR:-$_color_muted}" | tr -cd 'a-fA-F0-9')
fi
_data="{\"title\": \"$_subject\","
if [ -n "$_color" ]; then
_data="$_data\"themeColor\": \"$_color\", "
fi
_data="$_data\"text\": \"$_content\"}"
if response=$(_post "$_data" "$TEAMS_WEBHOOK_URL"); then
if ! _contains "$response" error; then
_info "teams send success."
return 0
fi
fi
_err "teams send error."
_err "$response"
return 1
}
Loading…
Cancel
Save