mirror of
				https://github.com/hiskang/acme.sh
				synced 2025-10-31 02:17:18 +00:00 
			
		
		
		
	
						commit
						6e2669ed1d
					
				
							
								
								
									
										4
									
								
								.github/ISSUE_TEMPLATE.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/ISSUE_TEMPLATE.md
									
									
									
									
										vendored
									
									
								
							| @ -1,4 +1,6 @@ | ||||
| <!-- | ||||
| 请确保已经更新到最新的代码, 然后贴上来 `--debug 2` 的调试输出. 没有调试输出,我帮不了你. | ||||
| 如何调试 https://github.com/Neilpang/acme.sh/wiki/How-to-debug-acme.sh | ||||
| 
 | ||||
| If it is a bug report: | ||||
| - make sure you are able to repro it on the latest released version.  | ||||
| @ -8,13 +10,11 @@ You can install the latest version by: `acme.sh --upgrade` | ||||
| - Refer to the [WIKI](https://wiki.acme.sh). | ||||
| - Debug info [Debug](https://github.com/Neilpang/acme.sh/wiki/How-to-debug-acme.sh). | ||||
| 
 | ||||
| 
 | ||||
| --> | ||||
| 
 | ||||
| Steps to reproduce | ||||
| ------------------ | ||||
| 
 | ||||
| 
 | ||||
| Debug log | ||||
| ----------------- | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										5
									
								
								.github/PULL_REQUEST_TEMPLATE.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								.github/PULL_REQUEST_TEMPLATE.md
									
									
									
									
										vendored
									
									
								
							| @ -1,14 +1,9 @@ | ||||
| <!-- | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| Do NOT send pull request to `master` branch. | ||||
| 
 | ||||
| 
 | ||||
| Please send to `dev` branch instead. | ||||
| 
 | ||||
| 
 | ||||
| Any PR to `master` branch will NOT be merged. | ||||
| 
 | ||||
| 
 | ||||
| --> | ||||
							
								
								
									
										39
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										39
									
								
								.travis.yml
									
									
									
									
									
								
							| @ -1,10 +1,14 @@ | ||||
| language: shell | ||||
| sudo: required | ||||
| dist: trusty | ||||
| 
 | ||||
| os: | ||||
|   - linux | ||||
|   - osx | ||||
| 
 | ||||
| services: | ||||
|   - docker | ||||
| 
 | ||||
| env: | ||||
|   global: | ||||
|     - SHFMT_URL=https://github.com/mvdan/sh/releases/download/v0.4.0/shfmt_v0.4.0_linux_amd64 | ||||
| @ -17,35 +21,24 @@ addons: | ||||
|     - shellcheck | ||||
| 
 | ||||
| install: | ||||
|   - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then  | ||||
|       brew update && brew install openssl; | ||||
|       brew info openssl; | ||||
|       ln -s /usr/local/opt/openssl/lib/libcrypto.1.0.0.dylib /usr/local/lib/; | ||||
|       ln -s /usr/local/opt/openssl/lib/libssl.1.0.0.dylib /usr/local/lib/; | ||||
|       ln -s /usr/local/Cellar/openssl/1.0.2j/bin/openssl /usr/local/openssl; | ||||
|       _old_path="$PATH"; | ||||
|       echo "PATH=$PATH"; | ||||
|       export PATH=""; | ||||
|       export OPENSSL_BIN="/usr/local/openssl"; | ||||
|       openssl version 2>&1 || true; | ||||
|       $OPENSSL_BIN version 2>&1 || true; | ||||
|       export PATH="$_old_path"; | ||||
|   - if [ "$TRAVIS_OS_NAME" = 'osx' ]; then | ||||
|       brew update && brew install socat; | ||||
|       export PATH="/usr/local/opt/openssl@1.1/bin:$PATH" ; | ||||
|     fi | ||||
| 
 | ||||
| script: | ||||
|   - echo "TEST_LOCAL=$TEST_LOCAL" | ||||
|   - echo "NGROK_TOKEN=$(echo "$NGROK_TOKEN" | wc -c)" | ||||
|   - which openssl && openssl version | ||||
|   - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then curl -sSL $SHFMT_URL -o ~/shfmt ; fi | ||||
|   - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then chmod +x ~/shfmt ; fi | ||||
|   - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ~/shfmt -l -w -i 2 . ; fi | ||||
|   - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then git diff --exit-code && echo "shfmt OK" ; fi | ||||
|   - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then shellcheck -V ; fi | ||||
|   - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then shellcheck -e SC2021,SC2126,SC2034 **/*.sh && echo "shellcheck OK" ; fi | ||||
|   - command -V openssl && openssl version | ||||
|   - if [ "$TRAVIS_OS_NAME" = "linux" ]; then curl -sSL $SHFMT_URL -o ~/shfmt ; fi | ||||
|   - if [ "$TRAVIS_OS_NAME" = "linux" ]; then chmod +x ~/shfmt ; fi | ||||
|   - if [ "$TRAVIS_OS_NAME" = "linux" ]; then ~/shfmt -l -w -i 2 . ; fi | ||||
|   - if [ "$TRAVIS_OS_NAME" = "linux" ]; then git diff --exit-code && echo "shfmt OK" ; fi | ||||
|   - if [ "$TRAVIS_OS_NAME" = "linux" ]; then shellcheck -V ; fi | ||||
|   - if [ "$TRAVIS_OS_NAME" = "linux" ]; then shellcheck -e SC2181 **/*.sh && echo "shellcheck OK" ; fi | ||||
|   - cd .. | ||||
|   - git clone https://github.com/Neilpang/acmetest.git && cp -r acme.sh acmetest/ && cd acmetest | ||||
|   - if [[ "$TRAVIS_OS_NAME" == "linux" ]] && [[ "$NGROK_TOKEN" ]]; then sudo NGROK_TOKEN="$NGROK_TOKEN" ./letest.sh ; fi | ||||
|   - if [[ "$TRAVIS_OS_NAME" == "osx" ]] && [[ "$NGROK_TOKEN" ]]; then sudo NGROK_TOKEN="$NGROK_TOKEN" OPENSSL_BIN="$OPENSSL_BIN" ./letest.sh ; fi | ||||
|   - if [ "$TRAVIS_OS_NAME" = "linux" -a "$NGROK_TOKEN" ]; then sudo TEST_LOCAL="$TEST_LOCAL" NGROK_TOKEN="$NGROK_TOKEN" ./rundocker.sh testplat ubuntu:latest ; fi | ||||
|   - if [ "$TRAVIS_OS_NAME" = "osx" -a "$NGROK_TOKEN" ]; then sudo TEST_LOCAL="$TEST_LOCAL" NGROK_TOKEN="$NGROK_TOKEN" ACME_OPENSSL_BIN="$ACME_OPENSSL_BIN" ./letest.sh ; fi | ||||
| 
 | ||||
| 
 | ||||
| matrix: | ||||
|  | ||||
							
								
								
									
										63
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,63 @@ | ||||
| FROM alpine:3.6 | ||||
| 
 | ||||
| RUN apk update -f \ | ||||
|   && apk --no-cache add -f \ | ||||
|   openssl \ | ||||
|   curl \ | ||||
|   socat \ | ||||
|   && rm -rf /var/cache/apk/* | ||||
| 
 | ||||
| ENV LE_CONFIG_HOME /acme.sh | ||||
| 
 | ||||
| ENV AUTO_UPGRADE 1 | ||||
| 
 | ||||
| #Install | ||||
| ADD ./ /install_acme.sh/ | ||||
| RUN cd /install_acme.sh && ([ -f /install_acme.sh/acme.sh ] && /install_acme.sh/acme.sh --install || curl https://get.acme.sh | sh) && rm -rf /install_acme.sh/ | ||||
| 
 | ||||
| 
 | ||||
| RUN ln -s  /root/.acme.sh/acme.sh  /usr/local/bin/acme.sh && crontab -l | grep acme.sh | sed 's#> /dev/null##' | crontab - | ||||
| 
 | ||||
| RUN for verb in help \  | ||||
|   version \ | ||||
|   install \ | ||||
|   uninstall \ | ||||
|   upgrade \ | ||||
|   issue \ | ||||
|   signcsr \ | ||||
|   deploy \ | ||||
|   install-cert \ | ||||
|   renew \ | ||||
|   renew-all \ | ||||
|   revoke \ | ||||
|   remove \ | ||||
|   list \ | ||||
|   showcsr \ | ||||
|   install-cronjob \ | ||||
|   uninstall-cronjob \ | ||||
|   cron \ | ||||
|   toPkcs \ | ||||
|   toPkcs8 \ | ||||
|   update-account \ | ||||
|   register-account \ | ||||
|   create-account-key \ | ||||
|   create-domain-key \ | ||||
|   createCSR \ | ||||
|   deactivate \ | ||||
|   deactivate-account \ | ||||
|   ; do \ | ||||
|     printf -- "%b" "#!/usr/bin/env sh\n/root/.acme.sh/acme.sh --${verb} --config-home /acme.sh \"\$@\"" >/usr/local/bin/--${verb} && chmod +x /usr/local/bin/--${verb} \ | ||||
|   ; done | ||||
| 
 | ||||
| RUN printf "%b" '#!'"/usr/bin/env sh\n \ | ||||
| if [ \"\$1\" = \"daemon\" ];  then \n \ | ||||
|  trap \"echo stop && killall crond && exit 0\" SIGTERM SIGINT \n \ | ||||
|  crond && while true; do sleep 1; done;\n \ | ||||
| else \n \ | ||||
|  exec -- \"\$@\"\n \ | ||||
| fi" >/entry.sh && chmod +x /entry.sh | ||||
| 
 | ||||
| VOLUME /acme.sh | ||||
| 
 | ||||
| ENTRYPOINT ["/entry.sh"] | ||||
| CMD ["--help"] | ||||
							
								
								
									
										151
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										151
									
								
								README.md
									
									
									
									
									
								
							| @ -1,4 +1,6 @@ | ||||
| # An ACME Shell script: acme.sh [](https://travis-ci.org/Neilpang/acme.sh) | ||||
| 
 | ||||
| [](https://gitter.im/acme-sh/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) | ||||
| - An ACME protocol client written purely in Shell (Unix shell) language. | ||||
| - Full ACME protocol implementation. | ||||
| - Simple, powerful and very easy to use. You only need 3 minutes to learn it. | ||||
| @ -7,14 +9,33 @@ | ||||
| - Purely written in Shell with no dependencies on python or the official Let's Encrypt client. | ||||
| - Just one script to issue, renew and install your certificates automatically. | ||||
| - DOES NOT require `root/sudoer` access. | ||||
| - Docker friendly | ||||
| - IPv6 support | ||||
| 
 | ||||
| It's probably the `easiest&smallest&smartest` shell script to automatically issue & renew the free certificates from Let's Encrypt. | ||||
| It's probably the `easiest & smartest` shell script to automatically issue & renew the free certificates from Let's Encrypt. | ||||
| 
 | ||||
| Wiki: https://github.com/Neilpang/acme.sh/wiki | ||||
| 
 | ||||
| For Docker Fans: [acme.sh :two_hearts: Docker ](https://github.com/Neilpang/acme.sh/wiki/Run-acme.sh-in-docker) | ||||
| 
 | ||||
| Twitter: [@neilpangxa](https://twitter.com/neilpangxa) | ||||
| 
 | ||||
| 
 | ||||
| # [中文说明](https://github.com/Neilpang/acme.sh/wiki/%E8%AF%B4%E6%98%8E) | ||||
| 
 | ||||
| # Who are using **acme.sh** | ||||
| - [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)) | ||||
| - [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) | ||||
| - [discourse.org](https://meta.discourse.org/t/setting-up-lets-encrypt/40709) | ||||
| - [Centminmod](http://centminmod.com/letsencrypt-acmetool-https.html) | ||||
| - [splynx](https://forum.splynx.com/t/free-ssl-cert-for-splynx-lets-encrypt/297) | ||||
| - [archlinux](https://aur.archlinux.org/packages/acme.sh-git/) | ||||
| - [opnsense.org](https://github.com/opnsense/plugins/tree/master/security/acme-client/src/opnsense/scripts/OPNsense/AcmeClient) | ||||
| - [more...](https://github.com/Neilpang/acme.sh/wiki/Blogs-and-tutorials) | ||||
| 
 | ||||
| # Tested OS | ||||
| 
 | ||||
| @ -39,8 +60,9 @@ Wiki: https://github.com/Neilpang/acme.sh/wiki | ||||
| |17|-----| OpenWRT: Tested and working. See [wiki page](https://github.com/Neilpang/acme.sh/wiki/How-to-run-on-OpenWRT) | ||||
| |18|[](https://github.com/Neilpang/letest#here-are-the-latest-status)|SunOS/Solaris | ||||
| |19|[](https://github.com/Neilpang/letest#here-are-the-latest-status)|Gentoo Linux | ||||
| |20|[](https://travis-ci.org/Neilpang/acme.sh)|Mac OSX | ||||
| 
 | ||||
| For all build statuses, check our [daily build project](https://github.com/Neilpang/acmetest): | ||||
| For all build statuses, check our [weekly build project](https://github.com/Neilpang/acmetest): | ||||
| 
 | ||||
| https://github.com/Neilpang/acmetest | ||||
| 
 | ||||
| @ -50,7 +72,9 @@ https://github.com/Neilpang/acmetest | ||||
| - Webroot mode | ||||
| - Standalone mode | ||||
| - Apache mode | ||||
| - Nginx mode ( Beta ) | ||||
| - DNS mode | ||||
| - [Stateless mode](https://github.com/Neilpang/acme.sh/wiki/Stateless-Mode) | ||||
| 
 | ||||
| 
 | ||||
| # 1. How to install | ||||
| @ -115,13 +139,25 @@ root@v1:~# acme.sh -h | ||||
| acme.sh --issue -d example.com -w /home/wwwroot/example.com | ||||
| ``` | ||||
| 
 | ||||
| or: | ||||
| 
 | ||||
| ```bash | ||||
| acme.sh --issue -d example.com -w /home/username/public_html | ||||
| ``` | ||||
| 
 | ||||
| or: | ||||
| 
 | ||||
| ```bash | ||||
| acme.sh --issue -d example.com -w /var/www/html | ||||
| ``` | ||||
| 
 | ||||
| **Example 2:** Multiple domains in the same cert. | ||||
| 
 | ||||
| ```bash | ||||
| acme.sh --issue -d example.com -d www.example.com -d cp.example.com -w /home/wwwroot/example.com | ||||
| ``` | ||||
| 
 | ||||
| The parameter `/home/wwwroot/example.com` is the web root folder. You **MUST** have `write access` to this folder. | ||||
| The parameter `/home/wwwroot/example.com` or `/home/username/public_html` or `/var/www/html` is the web root folder where you host your website files. You **MUST** have `write access` to this folder. | ||||
| 
 | ||||
| Second argument **"example.com"** is the main domain you want to issue the cert for. | ||||
| You must have at least one domain there. | ||||
| @ -142,26 +178,28 @@ You **MUST** use this command to copy the certs to the target files, **DO NOT** | ||||
| 
 | ||||
| **Apache** example: | ||||
| ```bash | ||||
| acme.sh --installcert -d example.com \ | ||||
| --certpath      /path/to/certfile/in/apache/cert.pem  \ | ||||
| --keypath       /path/to/keyfile/in/apache/key.pem  \ | ||||
| --fullchainpath /path/to/fullchain/certfile/apache/fullchain.pem \ | ||||
| --reloadcmd     "service apache2 restart" | ||||
| acme.sh --install-cert -d example.com \ | ||||
| --cert-file      /path/to/certfile/in/apache/cert.pem  \ | ||||
| --key-file       /path/to/keyfile/in/apache/key.pem  \ | ||||
| --fullchain-file /path/to/fullchain/certfile/apache/fullchain.pem \ | ||||
| --reloadcmd     "service apache2 force-reload" | ||||
| ``` | ||||
| 
 | ||||
| **Nginx** example: | ||||
| ```bash | ||||
| acme.sh --installcert -d example.com \ | ||||
| --keypath       /path/to/keyfile/in/nginx/key.pem  \ | ||||
| --fullchainpath /path/to/fullchain/nginx/cert.pem \ | ||||
| --reloadcmd     "service nginx restart" | ||||
| acme.sh --install-cert -d example.com \ | ||||
| --key-file       /path/to/keyfile/in/nginx/key.pem  \ | ||||
| --fullchain-file /path/to/fullchain/nginx/cert.pem \ | ||||
| --reloadcmd     "service nginx force-reload" | ||||
| ``` | ||||
| 
 | ||||
| Only the domain is required, all the other parameters are optional. | ||||
| 
 | ||||
| The ownership and permission info of existing files are preserved. You may want to precreate the files to have defined ownership and permission. | ||||
| 
 | ||||
| Install/copy the issued cert/key to the production Apache or Nginx path. | ||||
| 
 | ||||
| The cert will be `renewed every **60** days by default` (which is configurable). Once the cert is renewed, the Apache/Nginx service will be restarted automatically by the command: `service apache2 restart` or `service nginx restart`. | ||||
| The cert will be renewed every **60** days by default (which is configurable). Once the cert is renewed, the Apache/Nginx service will be reloaded automatically by the command: `service apache2 force-reload` or `service nginx force-reload`. | ||||
| 
 | ||||
| 
 | ||||
| # 4. Use Standalone server to issue cert | ||||
| @ -208,8 +246,27 @@ acme.sh --issue --apache -d example.com -d www.example.com -d cp.example.com | ||||
| 
 | ||||
| More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert | ||||
| 
 | ||||
| # 7. Use Nginx mode | ||||
| 
 | ||||
| # 7. Use DNS mode: | ||||
| **(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`. | ||||
| 
 | ||||
| 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. | ||||
| 
 | ||||
| Just set string "nginx" as the second argument. | ||||
| 
 | ||||
| It will configure nginx server automatically to verify the domain and then restore the nginx config to the original version. | ||||
| 
 | ||||
| So, the config is not changed. | ||||
| 
 | ||||
| ``` | ||||
| acme.sh --issue --nginx -d example.com -d www.example.com -d cp.example.com | ||||
| ``` | ||||
| 
 | ||||
| More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert | ||||
| 
 | ||||
| # 8. Use DNS mode: | ||||
| 
 | ||||
| Support the `dns-01` challenge. | ||||
| 
 | ||||
| @ -239,8 +296,11 @@ acme.sh --renew -d example.com | ||||
| 
 | ||||
| Ok, it's finished. | ||||
| 
 | ||||
| **Take care, this is dns manual mode, it can not be renewed automatically. you will have to add a new txt record to your domain by your hand when you renew your cert.** | ||||
| 
 | ||||
| # 8. Automatic DNS API integration | ||||
| **Please use dns api mode instead.** | ||||
| 
 | ||||
| # 9. Automatic DNS API integration | ||||
| 
 | ||||
| If your DNS provider supports API access, we can use that API to automatically issue the certs. | ||||
| 
 | ||||
| @ -252,16 +312,44 @@ You don't have to do anything manually! | ||||
| 1. DNSPod.cn API | ||||
| 1. CloudXNS.com API | ||||
| 1. GoDaddy.com API | ||||
| 1. OVH, kimsufi, soyoustart and runabove API | ||||
| 1. AWS Route 53 | ||||
| 1. PowerDNS.com API | ||||
| 1. lexicon DNS API: https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api | ||||
|    (DigitalOcean, DNSimple, DNSMadeEasy, DNSPark, EasyDNS, Namesilo, NS1, PointHQ, Rage4 and Vultr etc.) | ||||
| 1. OVH, kimsufi, soyoustart and runabove API | ||||
| 1. nsupdate API | ||||
| 1. LuaDNS.com API | ||||
| 1. DNSMadeEasy.com API | ||||
| 1. nsupdate API | ||||
| 1. AWS Route 53 | ||||
| 1. aliyun.com(阿里云) API | ||||
| 1. ISPConfig 3.1 API | ||||
| 1. Alwaysdata.com API | ||||
| 1. Linode.com API | ||||
| 1. FreeDNS (https://freedns.afraid.org/) | ||||
| 1. cyon.ch | ||||
| 1. Domain-Offensive/Resellerinterface/Domainrobot API | ||||
| 1. Gandi LiveDNS API | ||||
| 1. Knot DNS API | ||||
| 1. DigitalOcean API (native) | ||||
| 1. ClouDNS.net API | ||||
| 1. Infoblox NIOS API (https://www.infoblox.com/) | ||||
| 1. VSCALE (https://vscale.io/) | ||||
| 1. Dynu API (https://www.dynu.com) | ||||
| 1. DNSimple API | ||||
| 1. NS1.com API | ||||
| 1. DuckDNS.org API | ||||
| 1. Name.com API | ||||
| 1. Dyn Managed DNS API | ||||
| 1. Yandex PDD API (https://pdd.yandex.ru) | ||||
| 1. Hurricane Electric DNS service (https://dns.he.net) | ||||
| 1. UnoEuro API (https://www.unoeuro.com/) | ||||
| 1. INWX (https://www.inwx.de/) | ||||
| 1. Servercow (https://servercow.de) | ||||
| 
 | ||||
| 
 | ||||
| And:  | ||||
| 
 | ||||
| 1. lexicon DNS API: https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api | ||||
|    (DigitalOcean, DNSimple, DNSMadeEasy, DNSPark, EasyDNS, Namesilo, NS1, PointHQ, Rage4 and Vultr etc.) | ||||
| 
 | ||||
| 
 | ||||
|     | ||||
| **More APIs coming soon...** | ||||
| 
 | ||||
| @ -270,7 +358,7 @@ If your DNS provider is not on the supported list above, you can write your own | ||||
| For more details: [How to use DNS API](dnsapi) | ||||
| 
 | ||||
| 
 | ||||
| # 9. Issue ECC certificates | ||||
| # 10. Issue ECC certificates | ||||
| 
 | ||||
| `Let's Encrypt` can now issue **ECDSA** certificates. | ||||
| 
 | ||||
| @ -280,7 +368,7 @@ Just set the `length` parameter with a prefix `ec-`. | ||||
| 
 | ||||
| For example: | ||||
| 
 | ||||
| ### Single domain ECC cerfiticate | ||||
| ### Single domain ECC certificate | ||||
| 
 | ||||
| ```bash | ||||
| acme.sh --issue -w /home/wwwroot/example.com -d example.com --keylength ec-256 | ||||
| @ -301,7 +389,7 @@ Valid values are: | ||||
| 3. **ec-521 (secp521r1,  "ECDSA P-521", which is not supported by Let's Encrypt yet.)** | ||||
| 
 | ||||
| 
 | ||||
| # 10. How to renew the issued certs | ||||
| # 11. How to renew the issued certs | ||||
| 
 | ||||
| No, you don't need to renew the certs manually. All the certs will be renewed automatically every **60** days. | ||||
| 
 | ||||
| @ -318,9 +406,9 @@ acme.sh --renew -d example.com --force --ecc | ||||
| ``` | ||||
| 
 | ||||
| 
 | ||||
| # 11. How to upgrade `acme.sh` | ||||
| # 12. How to upgrade `acme.sh` | ||||
| 
 | ||||
| acme.sh is in constant developement, so it's strongly recommended to use the latest code. | ||||
| acme.sh is in constant development, so it's strongly recommended to use the latest code. | ||||
| 
 | ||||
| You can update acme.sh to the latest code: | ||||
| 
 | ||||
| @ -343,26 +431,26 @@ acme.sh --upgrade --auto-upgrade 0 | ||||
| ``` | ||||
| 
 | ||||
| 
 | ||||
| # 12. Issue a cert from an existing CSR | ||||
| # 13. Issue a cert from an existing CSR | ||||
| 
 | ||||
| https://github.com/Neilpang/acme.sh/wiki/Issue-a-cert-from-existing-CSR | ||||
| 
 | ||||
| 
 | ||||
| # Under the Hood | ||||
| # 14. Under the Hood | ||||
| 
 | ||||
| Speak ACME language using shell, directly to "Let's Encrypt". | ||||
| 
 | ||||
| TODO: | ||||
| 
 | ||||
| 
 | ||||
| # Acknowledgments | ||||
| # 15. Acknowledgments | ||||
| 
 | ||||
| 1. Acme-tiny: https://github.com/diafygi/acme-tiny | ||||
| 2. ACME protocol: https://github.com/ietf-wg-acme/acme | ||||
| 3. Certbot: https://github.com/certbot/certbot | ||||
| 
 | ||||
| 
 | ||||
| # License & Others | ||||
| # 16. License & Others | ||||
| 
 | ||||
| License is GPLv3 | ||||
| 
 | ||||
| @ -371,8 +459,9 @@ Please Star and Fork me. | ||||
| [Issues](https://github.com/Neilpang/acme.sh/issues) and [pull requests](https://github.com/Neilpang/acme.sh/pulls) are welcome. | ||||
| 
 | ||||
| 
 | ||||
| # Donate | ||||
| # 17. Donate | ||||
| Your donation makes **acme.sh** better: | ||||
| 
 | ||||
| 1. PayPal: donate@acme.sh | ||||
| 1. PayPal/Alipay(支付宝)/Wechat(微信): [https://donate.acme.sh/](https://donate.acme.sh/) | ||||
|    | ||||
| [Donate List](https://github.com/Neilpang/acme.sh/wiki/Donate-list) | ||||
|  | ||||
							
								
								
									
										118
									
								
								deploy/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								deploy/README.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,118 @@ | ||||
| # Using deploy api | ||||
| 
 | ||||
| Before you can deploy your cert, you must [issue the cert first](https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert). | ||||
| 
 | ||||
| Here are the scripts to deploy the certs/key to the server/services. | ||||
| 
 | ||||
| ## 1. Deploy the certs to your cpanel host | ||||
| 
 | ||||
| If you want to deploy using cpanel UAPI see 7. | ||||
| 
 | ||||
| (cpanel deploy hook is not finished yet, this is just an example.) | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| Then you can deploy now: | ||||
| 
 | ||||
| ```sh | ||||
| export DEPLOY_CPANEL_USER=myusername | ||||
| export DEPLOY_CPANEL_PASSWORD=PASSWORD | ||||
| acme.sh --deploy -d example.com --deploy-hook cpanel | ||||
| ``` | ||||
| 
 | ||||
| ## 2. Deploy ssl cert on kong proxy engine based on api | ||||
| 
 | ||||
| Before you can deploy your cert, you must [issue the cert first](https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert). | ||||
| Currently supports Kong-v0.10.x. | ||||
| 
 | ||||
| ```sh | ||||
| acme.sh --deploy -d ftp.example.com --deploy-hook kong | ||||
| ``` | ||||
| 
 | ||||
| ## 3. Deploy the cert to remote server through SSH access | ||||
| 
 | ||||
| (TODO) | ||||
| 
 | ||||
| ## 4. Deploy the cert to local vsftpd server | ||||
| 
 | ||||
| ```sh | ||||
| acme.sh --deploy -d ftp.example.com --deploy-hook vsftpd | ||||
| ``` | ||||
| 
 | ||||
| The default vsftpd conf file is `/etc/vsftpd.conf`,  if your vsftpd conf is not in the default location, you can specify one: | ||||
| 
 | ||||
| ```sh | ||||
| export DEPLOY_VSFTPD_CONF="/etc/vsftpd.conf" | ||||
| 
 | ||||
| acme.sh --deploy -d ftp.example.com --deploy-hook vsftpd | ||||
| ``` | ||||
| 
 | ||||
| The default command to restart vsftpd server is `service vsftpd restart`, if it doesn't work, you can specify one: | ||||
| 
 | ||||
| ```sh | ||||
| export DEPLOY_VSFTPD_RELOAD="/etc/init.d/vsftpd restart" | ||||
| 
 | ||||
| acme.sh --deploy -d ftp.example.com --deploy-hook vsftpd | ||||
| ``` | ||||
| 
 | ||||
| ## 5. Deploy the cert to local exim4 server | ||||
| 
 | ||||
| ```sh | ||||
| acme.sh --deploy -d ftp.example.com --deploy-hook exim4 | ||||
| ``` | ||||
| 
 | ||||
| The default exim4 conf file is `/etc/exim/exim.conf`,  if your exim4 conf is not in the default location, you can specify one: | ||||
| 
 | ||||
| ```sh | ||||
| export DEPLOY_EXIM4_CONF="/etc/exim4/exim4.conf.template" | ||||
| 
 | ||||
| acme.sh --deploy -d ftp.example.com --deploy-hook exim4 | ||||
| ``` | ||||
| 
 | ||||
| The default command to restart exim4 server is `service exim4 restart`, if it doesn't work, you can specify one: | ||||
| 
 | ||||
| ```sh | ||||
| export DEPLOY_EXIM4_RELOAD="/etc/init.d/exim4 restart" | ||||
| 
 | ||||
| acme.sh --deploy -d ftp.example.com --deploy-hook exim4 | ||||
| ``` | ||||
| 
 | ||||
| ## 6. Deploy the cert to OSX Keychain | ||||
| 
 | ||||
| ```sh | ||||
| acme.sh --deploy -d ftp.example.com --deploy-hook keychain | ||||
| ``` | ||||
| 
 | ||||
| ## 7. Deploy to cpanel host using UAPI | ||||
| 
 | ||||
| This hook is using UAPI and works in cPanel & WHM version 56 or newer. | ||||
| ``` | ||||
| acme.sh  --deploy  -d example.com  --deploy-hook cpanel_uapi | ||||
| ``` | ||||
| DEPLOY_CPANEL_USER is required only if you run the script as root and it should contain cpanel username. | ||||
| ```sh | ||||
| export DEPLOY_CPANEL_USER=username | ||||
| acme.sh  --deploy  -d example.com  --deploy-hook cpanel_uapi | ||||
| ``` | ||||
| Please note, that the cpanel_uapi hook will deploy only the first domain when your certificate will automatically renew. Therefore you should issue a separete certificate for each domain.  | ||||
| 
 | ||||
| ## 8. Deploy the cert to your FRITZ!Box router | ||||
| 
 | ||||
| You must specify the credentials that have administrative privileges on the FRITZ!Box in order to deploy the certificate, plus the URL of your FRITZ!Box, through the following environment variables: | ||||
| ```sh | ||||
| $ export DEPLOY_FRITZBOX_USERNAME=my_username | ||||
| $ export DEPLOY_FRITZBOX_PASSWORD=the_password | ||||
| $ export DEPLOY_FRITZBOX_URL=https://fritzbox.example.com | ||||
| ``` | ||||
| 
 | ||||
| After the first deployment, these values will be stored in your $HOME/.acme.sh/account.conf. You may now deploy the certificate like this: | ||||
| 
 | ||||
| ```sh | ||||
| acme.sh --deploy -d fritzbox.example.com --deploy-hook fritzbox | ||||
| ``` | ||||
| 
 | ||||
| ## 9. Deploy the cert to strongswan | ||||
| 
 | ||||
| ```sh | ||||
| acme.sh --deploy -d ftp.example.com --deploy-hook strongswan | ||||
| ``` | ||||
							
								
								
									
										26
									
								
								deploy/apache.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								deploy/apache.sh
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,26 @@ | ||||
| #!/usr/bin/env sh | ||||
| 
 | ||||
| #Here is a script to deploy cert to apache server. | ||||
| 
 | ||||
| #returns 0 means success, otherwise error. | ||||
| 
 | ||||
| ########  Public functions ##################### | ||||
| 
 | ||||
| #domain keyfile certfile cafile fullchain | ||||
| apache_deploy() { | ||||
|   _cdomain="$1" | ||||
|   _ckey="$2" | ||||
|   _ccert="$3" | ||||
|   _cca="$4" | ||||
|   _cfullchain="$5" | ||||
| 
 | ||||
|   _debug _cdomain "$_cdomain" | ||||
|   _debug _ckey "$_ckey" | ||||
|   _debug _ccert "$_ccert" | ||||
|   _debug _cca "$_cca" | ||||
|   _debug _cfullchain "$_cfullchain" | ||||
| 
 | ||||
|   _err "Deploy cert to apache server, Not implemented yet" | ||||
|   return 1 | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										64
									
								
								deploy/cpanel_uapi.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								deploy/cpanel_uapi.sh
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,64 @@ | ||||
| #!/usr/bin/env sh | ||||
| # Here is the script to deploy the cert to your cpanel using the cpanel API. | ||||
| # Uses command line uapi.  --user option is needed only if run as root. | ||||
| # Returns 0 when success. | ||||
| # Written by Santeri Kannisto <santeri.kannisto@2globalnomads.info> | ||||
| # Public domain, 2017 | ||||
| 
 | ||||
| #export DEPLOY_CPANEL_USER=myusername | ||||
| 
 | ||||
| ########  Public functions ##################### | ||||
| 
 | ||||
| #domain keyfile certfile cafile fullchain | ||||
| 
 | ||||
| cpanel_uapi_deploy() { | ||||
|   _cdomain="$1" | ||||
|   _ckey="$2" | ||||
|   _ccert="$3" | ||||
|   _cca="$4" | ||||
|   _cfullchain="$5" | ||||
| 
 | ||||
|   _debug _cdomain "$_cdomain" | ||||
|   _debug _ckey "$_ckey" | ||||
|   _debug _ccert "$_ccert" | ||||
|   _debug _cca "$_cca" | ||||
|   _debug _cfullchain "$_cfullchain" | ||||
| 
 | ||||
|   if ! _exists uapi; then | ||||
|     _err "The command uapi is not found." | ||||
|     return 1 | ||||
|   fi | ||||
|   if ! _exists php; then | ||||
|     _err "The command php is not found." | ||||
|     return 1 | ||||
|   fi | ||||
|   # read cert and key files and urlencode both | ||||
|   _certstr=$(cat "$_ccert") | ||||
|   _keystr=$(cat "$_ckey") | ||||
|   _cert=$(php -r "echo urlencode(\"$_certstr\");") | ||||
|   _key=$(php -r "echo urlencode(\"$_keystr\");") | ||||
| 
 | ||||
|   _debug _cert "$_cert" | ||||
|   _debug _key "$_key" | ||||
| 
 | ||||
|   if [ "$(id -u)" = 0 ]; then | ||||
|     if [ -z "$DEPLOY_CPANEL_USER" ]; then | ||||
|       _err "It seems that you are root, please define the target user name: export DEPLOY_CPANEL_USER=username" | ||||
|       return 1 | ||||
|     fi | ||||
|     _savedomainconf DEPLOY_CPANEL_USER "$DEPLOY_CPANEL_USER" | ||||
|     _response=$(uapi --user="$DEPLOY_CPANEL_USER" SSL install_ssl domain="$_cdomain" cert="$_cert" key="$_key") | ||||
|   else | ||||
|     _response=$(uapi SSL install_ssl domain="$_cdomain" cert="$_cert" key="$_key") | ||||
|   fi | ||||
|   error_response="status: 0" | ||||
|   if test "${_response#*$error_response}" != "$_response"; then | ||||
|     _err "Error in deploying certificate:" | ||||
|     _err "$_response" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _debug response "$_response" | ||||
|   _info "Certificate successfully deployed" | ||||
|   return 0 | ||||
| } | ||||
							
								
								
									
										26
									
								
								deploy/dovecot.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								deploy/dovecot.sh
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,26 @@ | ||||
| #!/usr/bin/env sh | ||||
| 
 | ||||
| #Here is a script to deploy cert to dovecot server. | ||||
| 
 | ||||
| #returns 0 means success, otherwise error. | ||||
| 
 | ||||
| ########  Public functions ##################### | ||||
| 
 | ||||
| #domain keyfile certfile cafile fullchain | ||||
| dovecot_deploy() { | ||||
|   _cdomain="$1" | ||||
|   _ckey="$2" | ||||
|   _ccert="$3" | ||||
|   _cca="$4" | ||||
|   _cfullchain="$5" | ||||
| 
 | ||||
|   _debug _cdomain "$_cdomain" | ||||
|   _debug _ckey "$_ckey" | ||||
|   _debug _ccert "$_ccert" | ||||
|   _debug _cca "$_cca" | ||||
|   _debug _cfullchain "$_cfullchain" | ||||
| 
 | ||||
|   _err "Not implemented yet" | ||||
|   return 1 | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										114
									
								
								deploy/exim4.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								deploy/exim4.sh
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,114 @@ | ||||
| #!/usr/bin/env sh | ||||
| 
 | ||||
| #Here is a script to deploy cert to exim4 server. | ||||
| 
 | ||||
| #returns 0 means success, otherwise error. | ||||
| 
 | ||||
| #DEPLOY_EXIM4_CONF="/etc/exim/exim.conf" | ||||
| #DEPLOY_EXIM4_RELOAD="service exim4 restart" | ||||
| 
 | ||||
| ########  Public functions ##################### | ||||
| 
 | ||||
| #domain keyfile certfile cafile fullchain | ||||
| exim4_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" | ||||
| 
 | ||||
|   _ssl_path="/etc/acme.sh/exim4" | ||||
|   if ! mkdir -p "$_ssl_path"; then | ||||
|     _err "Can not create folder:$_ssl_path" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _info "Copying key and cert" | ||||
|   _real_key="$_ssl_path/exim4.key" | ||||
|   if ! cat "$_ckey" >"$_real_key"; then | ||||
|     _err "Error: write key file to: $_real_key" | ||||
|     return 1 | ||||
|   fi | ||||
|   _real_fullchain="$_ssl_path/exim4.pem" | ||||
|   if ! cat "$_cfullchain" >"$_real_fullchain"; then | ||||
|     _err "Error: write key file to: $_real_fullchain" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   DEFAULT_EXIM4_RELOAD="service exim4 restart" | ||||
|   _reload="${DEPLOY_EXIM4_RELOAD:-$DEFAULT_EXIM4_RELOAD}" | ||||
| 
 | ||||
|   if [ -z "$IS_RENEW" ]; then | ||||
|     DEFAULT_EXIM4_CONF="/etc/exim/exim.conf" | ||||
|     if [ ! -f "$DEFAULT_EXIM4_CONF" ]; then | ||||
|       DEFAULT_EXIM4_CONF="/etc/exim4/exim4.conf.template" | ||||
|     fi | ||||
|     _exim4_conf="${DEPLOY_EXIM4_CONF:-$DEFAULT_EXIM4_CONF}" | ||||
|     _debug _exim4_conf "$_exim4_conf" | ||||
|     if [ ! -f "$_exim4_conf" ]; then | ||||
|       if [ -z "$DEPLOY_EXIM4_CONF" ]; then | ||||
|         _err "exim4 conf is not found, please define DEPLOY_EXIM4_CONF" | ||||
|         return 1 | ||||
|       else | ||||
|         _err "It seems that the specified exim4 conf is not valid, please check." | ||||
|         return 1 | ||||
|       fi | ||||
|     fi | ||||
|     if [ ! -w "$_exim4_conf" ]; then | ||||
|       _err "The file $_exim4_conf is not writable, please change the permission." | ||||
|       return 1 | ||||
|     fi | ||||
|     _backup_conf="$DOMAIN_BACKUP_PATH/exim4.conf.bak" | ||||
|     _info "Backup $_exim4_conf to $_backup_conf" | ||||
|     cp "$_exim4_conf" "$_backup_conf" | ||||
| 
 | ||||
|     _info "Modify exim4 conf: $_exim4_conf" | ||||
|     if _setopt "$_exim4_conf" "tls_certificate" "=" "$_real_fullchain" \ | ||||
|       && _setopt "$_exim4_conf" "tls_privatekey" "=" "$_real_key"; then | ||||
|       _info "Set config success!" | ||||
|     else | ||||
|       _err "Config exim4 server error, please report bug to us." | ||||
|       _info "Restoring exim4 conf" | ||||
|       if cat "$_backup_conf" >"$_exim4_conf"; then | ||||
|         _info "Restore conf success" | ||||
|         eval "$_reload" | ||||
|       else | ||||
|         _err "Oops, error restore exim4 conf, please report bug to us." | ||||
|       fi | ||||
|       return 1 | ||||
|     fi | ||||
|   fi | ||||
| 
 | ||||
|   _info "Run reload: $_reload" | ||||
|   if eval "$_reload"; then | ||||
|     _info "Reload success!" | ||||
|     if [ "$DEPLOY_EXIM4_CONF" ]; then | ||||
|       _savedomainconf DEPLOY_EXIM4_CONF "$DEPLOY_EXIM4_CONF" | ||||
|     else | ||||
|       _cleardomainconf DEPLOY_EXIM4_CONF | ||||
|     fi | ||||
|     if [ "$DEPLOY_EXIM4_RELOAD" ]; then | ||||
|       _savedomainconf DEPLOY_EXIM4_RELOAD "$DEPLOY_EXIM4_RELOAD" | ||||
|     else | ||||
|       _cleardomainconf DEPLOY_EXIM4_RELOAD | ||||
|     fi | ||||
|     return 0 | ||||
|   else | ||||
|     _err "Reload error, restoring" | ||||
|     if cat "$_backup_conf" >"$_exim4_conf"; then | ||||
|       _info "Restore conf success" | ||||
|       eval "$_reload" | ||||
|     else | ||||
|       _err "Oops, error restore exim4 conf, please report bug to us." | ||||
|     fi | ||||
|     return 1 | ||||
|   fi | ||||
|   return 0 | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										108
									
								
								deploy/fritzbox.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								deploy/fritzbox.sh
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,108 @@ | ||||
| #!/usr/bin/env sh | ||||
| 
 | ||||
| #Here is a script to deploy cert to an AVM FRITZ!Box router. | ||||
| 
 | ||||
| #returns 0 means success, otherwise error. | ||||
| 
 | ||||
| #DEPLOY_FRITZBOX_USERNAME="username" | ||||
| #DEPLOY_FRITZBOX_PASSWORD="password" | ||||
| #DEPLOY_FRITZBOX_URL="https://fritz.box" | ||||
| 
 | ||||
| # Kudos to wikrie at Github for his FRITZ!Box update script: | ||||
| # https://gist.github.com/wikrie/f1d5747a714e0a34d0582981f7cb4cfb | ||||
| 
 | ||||
| ########  Public functions ##################### | ||||
| 
 | ||||
| #domain keyfile certfile cafile fullchain | ||||
| fritzbox_deploy() { | ||||
|   _cdomain="$1" | ||||
|   _ckey="$2" | ||||
|   _ccert="$3" | ||||
|   _cca="$4" | ||||
|   _cfullchain="$5" | ||||
| 
 | ||||
|   _debug _cdomain "$_cdomain" | ||||
|   _debug _ckey "$_ckey" | ||||
|   _debug _ccert "$_ccert" | ||||
|   _debug _cca "$_cca" | ||||
|   _debug _cfullchain "$_cfullchain" | ||||
| 
 | ||||
|   if ! _exists iconv; then | ||||
|     _err "iconv not found" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _fritzbox_username="${DEPLOY_FRITZBOX_USERNAME}" | ||||
|   _fritzbox_password="${DEPLOY_FRITZBOX_PASSWORD}" | ||||
|   _fritzbox_url="${DEPLOY_FRITZBOX_URL}" | ||||
| 
 | ||||
|   _debug _fritzbox_url "$_fritzbox_url" | ||||
|   _debug _fritzbox_username "$_fritzbox_username" | ||||
|   _secure_debug _fritzbox_password "$_fritzbox_password" | ||||
|   if [ -z "$_fritzbox_username" ]; then | ||||
|     _err "FRITZ!Box username is not found, please define DEPLOY_FRITZBOX_USERNAME." | ||||
|     return 1 | ||||
|   fi | ||||
|   if [ -z "$_fritzbox_password" ]; then | ||||
|     _err "FRITZ!Box password is not found, please define DEPLOY_FRITZBOX_PASSWORD." | ||||
|     return 1 | ||||
|   fi | ||||
|   if [ -z "$_fritzbox_url" ]; then | ||||
|     _err "FRITZ!Box url is not found, please define DEPLOY_FRITZBOX_URL." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _saveaccountconf DEPLOY_FRITZBOX_USERNAME "${_fritzbox_username}" | ||||
|   _saveaccountconf DEPLOY_FRITZBOX_PASSWORD "${_fritzbox_password}" | ||||
|   _saveaccountconf DEPLOY_FRITZBOX_URL "${_fritzbox_url}" | ||||
| 
 | ||||
|   # Do not check for a valid SSL certificate, because initially the cert is not valid, so it could not install the LE generated certificate | ||||
|   export HTTPS_INSECURE=1 | ||||
| 
 | ||||
|   _info "Log in to the FRITZ!Box" | ||||
|   _fritzbox_challenge="$(_get "${_fritzbox_url}/login_sid.lua" | sed -e 's/^.*<Challenge>//' -e 's/<\/Challenge>.*$//')" | ||||
|   _fritzbox_hash="$(printf "%s-%s" "${_fritzbox_challenge}" "${_fritzbox_password}" | iconv -f ASCII -t UTF16LE | md5sum | awk '{print $1}')" | ||||
|   _fritzbox_sid="$(_get "${_fritzbox_url}/login_sid.lua?sid=0000000000000000&username=${_fritzbox_username}&response=${_fritzbox_challenge}-${_fritzbox_hash}" | sed -e 's/^.*<SID>//' -e 's/<\/SID>.*$//')" | ||||
| 
 | ||||
|   if [ -z "${_fritzbox_sid}" ] || [ "${_fritzbox_sid}" = "0000000000000000" ]; then | ||||
|     _err "Logging in to the FRITZ!Box failed. Please check username, password and URL." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _info "Generate form POST request" | ||||
|   _post_request="$(_mktemp)" | ||||
|   _post_boundary="---------------------------$(date +%Y%m%d%H%M%S)" | ||||
|   # _CERTPASSWORD_ is unset because Let's Encrypt certificates don't have a password. But if they ever do, here's the place to use it! | ||||
|   _CERTPASSWORD_= | ||||
|   { | ||||
|     printf -- "--" | ||||
|     printf -- "%s\r\n" "${_post_boundary}" | ||||
|     printf "Content-Disposition: form-data; name=\"sid\"\r\n\r\n%s\r\n" "${_fritzbox_sid}" | ||||
|     printf -- "--" | ||||
|     printf -- "%s\r\n" "${_post_boundary}" | ||||
|     printf "Content-Disposition: form-data; name=\"BoxCertPassword\"\r\n\r\n%s\r\n" "${_CERTPASSWORD_}" | ||||
|     printf -- "--" | ||||
|     printf -- "%s\r\n" "${_post_boundary}" | ||||
|     printf "Content-Disposition: form-data; name=\"BoxCertImportFile\"; filename=\"BoxCert.pem\"\r\n" | ||||
|     printf "Content-Type: application/octet-stream\r\n\r\n" | ||||
|     cat "${_ckey}" "${_cfullchain}" | ||||
|     printf "\r\n" | ||||
|     printf -- "--" | ||||
|     printf -- "%s--" "${_post_boundary}" | ||||
|   } >>"${_post_request}" | ||||
| 
 | ||||
|   _info "Upload certificate to the FRITZ!Box" | ||||
| 
 | ||||
|   export _H1="Content-type: multipart/form-data boundary=${_post_boundary}" | ||||
|   _post "$(cat "${_post_request}")" "${_fritzbox_url}/cgi-bin/firmwarecfg" | grep SSL | ||||
| 
 | ||||
|   retval=$? | ||||
|   if [ $retval = 0 ]; then | ||||
|     _info "Upload successful" | ||||
|   else | ||||
|     _err "Upload failed" | ||||
|   fi | ||||
|   rm "${_post_request}" | ||||
| 
 | ||||
|   return $retval | ||||
| } | ||||
							
								
								
									
										26
									
								
								deploy/haproxy.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								deploy/haproxy.sh
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,26 @@ | ||||
| #!/usr/bin/env sh | ||||
| 
 | ||||
| #Here is a script to deploy cert to haproxy server. | ||||
| 
 | ||||
| #returns 0 means success, otherwise error. | ||||
| 
 | ||||
| ########  Public functions ##################### | ||||
| 
 | ||||
| #domain keyfile certfile cafile fullchain | ||||
| haproxy_deploy() { | ||||
|   _cdomain="$1" | ||||
|   _ckey="$2" | ||||
|   _ccert="$3" | ||||
|   _cca="$4" | ||||
|   _cfullchain="$5" | ||||
| 
 | ||||
|   _debug _cdomain "$_cdomain" | ||||
|   _debug _ckey "$_ckey" | ||||
|   _debug _ccert "$_ccert" | ||||
|   _debug _cca "$_cca" | ||||
|   _debug _cfullchain "$_cfullchain" | ||||
| 
 | ||||
|   _err "deploy cert to haproxy server, Not implemented yet" | ||||
|   return 1 | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										31
									
								
								deploy/keychain.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								deploy/keychain.sh
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,31 @@ | ||||
| #!/usr/bin/env sh | ||||
| 
 | ||||
| #Here is a sample custom api script. | ||||
| #This file name is "myapi.sh" | ||||
| #So, here must be a method   myapi_deploy() | ||||
| #Which will be called by acme.sh to deploy the cert | ||||
| #returns 0 means success, otherwise error. | ||||
| 
 | ||||
| ########  Public functions ##################### | ||||
| 
 | ||||
| #domain keyfile certfile cafile fullchain | ||||
| keychain_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" | ||||
| 
 | ||||
|   /usr/bin/security import "$_ckey" -k "/Library/Keychains/System.keychain" | ||||
|   /usr/bin/security import "$_ccert" -k "/Library/Keychains/System.keychain" | ||||
|   /usr/bin/security import "$_cca" -k "/Library/Keychains/System.keychain" | ||||
|   /usr/bin/security import "$_cfullchain" -k "/Library/Keychains/System.keychain" | ||||
| 
 | ||||
|   return 0 | ||||
| } | ||||
							
								
								
									
										77
									
								
								deploy/kong.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										77
									
								
								deploy/kong.sh
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,77 @@ | ||||
| #!/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 | ||||
| # Note that we deploy full chain | ||||
| # Written by Geoffroi Genot <ggenot@voxbone.com> | ||||
| 
 | ||||
| ########  Public functions ##################### | ||||
| 
 | ||||
| #domain keyfile certfile cafile fullchain | ||||
| kong_deploy() { | ||||
|   _cdomain="$1" | ||||
|   _ckey="$2" | ||||
|   _ccert="$3" | ||||
|   _cca="$4" | ||||
|   _cfullchain="$5" | ||||
|   _info "Deploying certificate on Kong instance" | ||||
|   if [ -z "$KONG_URL" ]; then | ||||
|     _debug "KONG_URL Not set, using default http://localhost:8001" | ||||
|     KONG_URL="http://localhost:8001" | ||||
|   fi | ||||
| 
 | ||||
|   _debug _cdomain "$_cdomain" | ||||
|   _debug _ckey "$_ckey" | ||||
|   _debug _ccert "$_ccert" | ||||
|   _debug _cca "$_cca" | ||||
|   _debug _cfullchain "$_cfullchain" | ||||
| 
 | ||||
|   #Get ssl_uuid linked to the domain | ||||
|   ssl_uuid=$(_get "$KONG_URL/certificates/$_cdomain" | _normalizeJson | _egrep_o '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}') | ||||
|   if [ -z "$ssl_uuid" ]; then | ||||
|     _debug "Unable to get Kong ssl_uuid for domain $_cdomain" | ||||
|     _debug "Make sure that KONG_URL is correctly configured" | ||||
|     _debug "Make sure that a Kong certificate match the sni" | ||||
|     _debug "Kong url: $KONG_URL" | ||||
|     _info "No existing certificate, creating..." | ||||
|     #return 1 | ||||
|   fi | ||||
|   #Save kong url if it's succesful (First run case) | ||||
|   _saveaccountconf KONG_URL "$KONG_URL" | ||||
|   #Generate DEIM | ||||
|   delim="-----MultipartDelimiter$(date "+%s%N")" | ||||
|   nl="\015\012" | ||||
|   #Set Header | ||||
|   _H1="Content-Type: multipart/form-data; boundary=$delim" | ||||
|   #Generate data for request (Multipart/form-data with mixed content) | ||||
|   if [ -z "$ssl_uuid" ]; then | ||||
|     #set sni to domain | ||||
|     content="--$delim${nl}Content-Disposition: form-data; name=\"snis\"${nl}${nl}$_cdomain" | ||||
|   fi | ||||
|   #add key | ||||
|   content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"key\"; filename=\"$(basename "$_ckey")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_ckey")" | ||||
|   #Add cert | ||||
|   content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"cert\"; filename=\"$(basename "$_cfullchain")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_cfullchain")" | ||||
|   #Close multipart | ||||
|   content="$content${nl}--$delim--${nl}" | ||||
|   #Convert CRLF | ||||
|   content=$(printf %b "$content") | ||||
|   #DEBUG | ||||
|   _debug header "$_H1" | ||||
|   _debug content "$content" | ||||
|   #Check if sslcreated (if not => POST else => PATCH) | ||||
| 
 | ||||
|   if [ -z "$ssl_uuid" ]; then | ||||
|     #Post certificate to Kong | ||||
|     response=$(_post "$content" "$KONG_URL/certificates" "" "POST") | ||||
|   else | ||||
|     #patch | ||||
|     response=$(_post "$content" "$KONG_URL/certificates/$ssl_uuid" "" "PATCH") | ||||
|   fi | ||||
|   if ! [ "$(echo "$response" | _egrep_o "created_at")" = "created_at" ]; then | ||||
|     _err "An error occurred with cert upload. Check response:" | ||||
|     _err "$response" | ||||
|     return 1 | ||||
|   fi | ||||
|   _debug response "$response" | ||||
|   _info "Certificate successfully deployed" | ||||
| } | ||||
							
								
								
									
										0
									
								
								deploy/myapi.sh
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										0
									
								
								deploy/myapi.sh
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
								
								
									
										26
									
								
								deploy/mysqld.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								deploy/mysqld.sh
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,26 @@ | ||||
| #!/usr/bin/env sh | ||||
| 
 | ||||
| #Here is a script to deploy cert to mysqld server. | ||||
| 
 | ||||
| #returns 0 means success, otherwise error. | ||||
| 
 | ||||
| ########  Public functions ##################### | ||||
| 
 | ||||
| #domain keyfile certfile cafile fullchain | ||||
| mysqld_deploy() { | ||||
|   _cdomain="$1" | ||||
|   _ckey="$2" | ||||
|   _ccert="$3" | ||||
|   _cca="$4" | ||||
|   _cfullchain="$5" | ||||
| 
 | ||||
|   _debug _cdomain "$_cdomain" | ||||
|   _debug _ckey "$_ckey" | ||||
|   _debug _ccert "$_ccert" | ||||
|   _debug _cca "$_cca" | ||||
|   _debug _cfullchain "$_cfullchain" | ||||
| 
 | ||||
|   _err "deploy cert to mysqld server, Not implemented yet" | ||||
|   return 1 | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										26
									
								
								deploy/nginx.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								deploy/nginx.sh
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,26 @@ | ||||
| #!/usr/bin/env sh | ||||
| 
 | ||||
| #Here is a script to deploy cert to nginx server. | ||||
| 
 | ||||
| #returns 0 means success, otherwise error. | ||||
| 
 | ||||
| ########  Public functions ##################### | ||||
| 
 | ||||
| #domain keyfile certfile cafile fullchain | ||||
| nginx_deploy() { | ||||
|   _cdomain="$1" | ||||
|   _ckey="$2" | ||||
|   _ccert="$3" | ||||
|   _cca="$4" | ||||
|   _cfullchain="$5" | ||||
| 
 | ||||
|   _debug _cdomain "$_cdomain" | ||||
|   _debug _ckey "$_ckey" | ||||
|   _debug _ccert "$_ccert" | ||||
|   _debug _cca "$_cca" | ||||
|   _debug _cfullchain "$_cfullchain" | ||||
| 
 | ||||
|   _err "deploy cert to nginx server, Not implemented yet" | ||||
|   return 1 | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										26
									
								
								deploy/opensshd.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								deploy/opensshd.sh
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,26 @@ | ||||
| #!/usr/bin/env sh | ||||
| 
 | ||||
| #Here is a script to deploy cert to opensshd server. | ||||
| 
 | ||||
| #returns 0 means success, otherwise error. | ||||
| 
 | ||||
| ########  Public functions ##################### | ||||
| 
 | ||||
| #domain keyfile certfile cafile fullchain | ||||
| opensshd_deploy() { | ||||
|   _cdomain="$1" | ||||
|   _ckey="$2" | ||||
|   _ccert="$3" | ||||
|   _cca="$4" | ||||
|   _cfullchain="$5" | ||||
| 
 | ||||
|   _debug _cdomain "$_cdomain" | ||||
|   _debug _ckey "$_ckey" | ||||
|   _debug _ccert "$_ccert" | ||||
|   _debug _cca "$_cca" | ||||
|   _debug _cfullchain "$_cfullchain" | ||||
| 
 | ||||
|   _err "deploy cert to opensshd server, Not implemented yet" | ||||
|   return 1 | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										26
									
								
								deploy/pureftpd.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								deploy/pureftpd.sh
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,26 @@ | ||||
| #!/usr/bin/env sh | ||||
| 
 | ||||
| #Here is a script to deploy cert to pureftpd server. | ||||
| 
 | ||||
| #returns 0 means success, otherwise error. | ||||
| 
 | ||||
| ########  Public functions ##################### | ||||
| 
 | ||||
| #domain keyfile certfile cafile fullchain | ||||
| pureftpd_deploy() { | ||||
|   _cdomain="$1" | ||||
|   _ckey="$2" | ||||
|   _ccert="$3" | ||||
|   _cca="$4" | ||||
|   _cfullchain="$5" | ||||
| 
 | ||||
|   _debug _cdomain "$_cdomain" | ||||
|   _debug _ckey "$_ckey" | ||||
|   _debug _ccert "$_ccert" | ||||
|   _debug _cca "$_cca" | ||||
|   _debug _cfullchain "$_cfullchain" | ||||
| 
 | ||||
|   _err "deploy cert to pureftpd server, Not implemented yet" | ||||
|   return 1 | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										32
									
								
								deploy/strongswan.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								deploy/strongswan.sh
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,32 @@ | ||||
| #!/usr/bin/env sh | ||||
| 
 | ||||
| #Here is a sample custom api script. | ||||
| #This file name is "myapi.sh" | ||||
| #So, here must be a method   myapi_deploy() | ||||
| #Which will be called by acme.sh to deploy the cert | ||||
| #returns 0 means success, otherwise error. | ||||
| 
 | ||||
| ########  Public functions ##################### | ||||
| 
 | ||||
| #domain keyfile certfile cafile fullchain | ||||
| strongswan_deploy() { | ||||
|   _cdomain="$1" | ||||
|   _ckey="$2" | ||||
|   _ccert="$3" | ||||
|   _cca="$4" | ||||
|   _cfullchain="$5" | ||||
| 
 | ||||
|   _debug _cdomain "$_cdomain" | ||||
|   _debug _ckey "$_ckey" | ||||
|   _debug _ccert "$_ccert" | ||||
|   _debug _cca "$_cca" | ||||
|   _debug _cfullchain "$_cfullchain" | ||||
| 
 | ||||
|   cat "$_ckey" >"/etc/ipsec.d/private/$(basename "$_ckey")" | ||||
|   cat "$_ccert" >"/etc/ipsec.d/certs/$(basename "$_ccert")" | ||||
|   cat "$_cca" >"/etc/ipsec.d/cacerts/$(basename "$_cca")" | ||||
|   cat "$_cfullchain" >"/etc/ipsec.d/cacerts/$(basename "$_cfullchain")" | ||||
| 
 | ||||
|   ipsec reload | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										100
									
								
								deploy/unifi.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								deploy/unifi.sh
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,100 @@ | ||||
| #!/usr/bin/env sh | ||||
| 
 | ||||
| #Here is a script to deploy cert to unifi server. | ||||
| 
 | ||||
| #returns 0 means success, otherwise error. | ||||
| 
 | ||||
| #DEPLOY_UNIFI_KEYSTORE="/usr/lib/unifi/data/keystore" | ||||
| #DEPLOY_UNIFI_KEYPASS="aircontrolenterprise" | ||||
| #DEPLOY_UNIFI_RELOAD="service unifi restart" | ||||
| 
 | ||||
| ########  Public functions ##################### | ||||
| 
 | ||||
| #domain keyfile certfile cafile fullchain | ||||
| unifi_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 keytool; then | ||||
|     _err "keytool not found" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   DEFAULT_UNIFI_KEYSTORE="/usr/lib/unifi/data/keystore" | ||||
|   _unifi_keystore="${DEPLOY_UNIFI_KEYSTORE:-$DEFAULT_UNIFI_KEYSTORE}" | ||||
|   DEFAULT_UNIFI_KEYPASS="aircontrolenterprise" | ||||
|   _unifi_keypass="${DEPLOY_UNIFI_KEYPASS:-$DEFAULT_UNIFI_KEYPASS}" | ||||
|   DEFAULT_UNIFI_RELOAD="service unifi restart" | ||||
|   _reload="${DEPLOY_UNIFI_RELOAD:-$DEFAULT_UNIFI_RELOAD}" | ||||
| 
 | ||||
|   _debug _unifi_keystore "$_unifi_keystore" | ||||
|   if [ ! -f "$_unifi_keystore" ]; then | ||||
|     if [ -z "$DEPLOY_UNIFI_KEYSTORE" ]; then | ||||
|       _err "unifi keystore is not found, please define DEPLOY_UNIFI_KEYSTORE" | ||||
|       return 1 | ||||
|     else | ||||
|       _err "It seems that the specified unifi keystore is not valid, please check." | ||||
|       return 1 | ||||
|     fi | ||||
|   fi | ||||
|   if [ ! -w "$_unifi_keystore" ]; then | ||||
|     _err "The file $_unifi_keystore is not writable, please change the permission." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _info "Generate import pkcs12" | ||||
|   _import_pkcs12="$(_mktemp)" | ||||
|   _toPkcs "$_import_pkcs12" "$_ckey" "$_ccert" "$_cca" "$_unifi_keypass" unifi root | ||||
|   if [ "$?" != "0" ]; then | ||||
|     _err "Oops, error creating import pkcs12, please report bug to us." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _info "Modify unifi keystore: $_unifi_keystore" | ||||
|   if keytool -importkeystore \ | ||||
|     -deststorepass "$_unifi_keypass" -destkeypass "$_unifi_keypass" -destkeystore "$_unifi_keystore" \ | ||||
|     -srckeystore "$_import_pkcs12" -srcstoretype PKCS12 -srcstorepass "$_unifi_keypass" \ | ||||
|     -alias unifi -noprompt; then | ||||
|     _info "Import keystore success!" | ||||
|     rm "$_import_pkcs12" | ||||
|   else | ||||
|     _err "Import unifi keystore error, please report bug to us." | ||||
|     rm "$_import_pkcs12" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _info "Run reload: $_reload" | ||||
|   if eval "$_reload"; then | ||||
|     _info "Reload success!" | ||||
|     if [ "$DEPLOY_UNIFI_KEYSTORE" ]; then | ||||
|       _savedomainconf DEPLOY_UNIFI_KEYSTORE "$DEPLOY_UNIFI_KEYSTORE" | ||||
|     else | ||||
|       _cleardomainconf DEPLOY_UNIFI_KEYSTORE | ||||
|     fi | ||||
|     if [ "$DEPLOY_UNIFI_KEYPASS" ]; then | ||||
|       _savedomainconf DEPLOY_UNIFI_KEYPASS "$DEPLOY_UNIFI_KEYPASS" | ||||
|     else | ||||
|       _cleardomainconf DEPLOY_UNIFI_KEYPASS | ||||
|     fi | ||||
|     if [ "$DEPLOY_UNIFI_RELOAD" ]; then | ||||
|       _savedomainconf DEPLOY_UNIFI_RELOAD "$DEPLOY_UNIFI_RELOAD" | ||||
|     else | ||||
|       _cleardomainconf DEPLOY_UNIFI_RELOAD | ||||
|     fi | ||||
|     return 0 | ||||
|   else | ||||
|     _err "Reload error" | ||||
|     return 1 | ||||
|   fi | ||||
|   return 0 | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										110
									
								
								deploy/vsftpd.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								deploy/vsftpd.sh
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,110 @@ | ||||
| #!/usr/bin/env sh | ||||
| 
 | ||||
| #Here is a script to deploy cert to vsftpd server. | ||||
| 
 | ||||
| #returns 0 means success, otherwise error. | ||||
| 
 | ||||
| #DEPLOY_VSFTPD_CONF="/etc/vsftpd.conf" | ||||
| #DEPLOY_VSFTPD_RELOAD="service vsftpd restart" | ||||
| 
 | ||||
| ########  Public functions ##################### | ||||
| 
 | ||||
| #domain keyfile certfile cafile fullchain | ||||
| vsftpd_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" | ||||
| 
 | ||||
|   _ssl_path="/etc/acme.sh/vsftpd" | ||||
|   if ! mkdir -p "$_ssl_path"; then | ||||
|     _err "Can not create folder:$_ssl_path" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _info "Copying key and cert" | ||||
|   _real_key="$_ssl_path/vsftpd.key" | ||||
|   if ! cat "$_ckey" >"$_real_key"; then | ||||
|     _err "Error: write key file to: $_real_key" | ||||
|     return 1 | ||||
|   fi | ||||
|   _real_fullchain="$_ssl_path/vsftpd.chain.pem" | ||||
|   if ! cat "$_cfullchain" >"$_real_fullchain"; then | ||||
|     _err "Error: write key file to: $_real_fullchain" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   DEFAULT_VSFTPD_RELOAD="service vsftpd restart" | ||||
|   _reload="${DEPLOY_VSFTPD_RELOAD:-$DEFAULT_VSFTPD_RELOAD}" | ||||
| 
 | ||||
|   if [ -z "$IS_RENEW" ]; then | ||||
|     DEFAULT_VSFTPD_CONF="/etc/vsftpd.conf" | ||||
|     _vsftpd_conf="${DEPLOY_VSFTPD_CONF:-$DEFAULT_VSFTPD_CONF}" | ||||
|     if [ ! -f "$_vsftpd_conf" ]; then | ||||
|       if [ -z "$DEPLOY_VSFTPD_CONF" ]; then | ||||
|         _err "vsftpd conf is not found, please define DEPLOY_VSFTPD_CONF" | ||||
|         return 1 | ||||
|       else | ||||
|         _err "It seems that the specified vsftpd conf is not valid, please check." | ||||
|         return 1 | ||||
|       fi | ||||
|     fi | ||||
|     if [ ! -w "$_vsftpd_conf" ]; then | ||||
|       _err "The file $_vsftpd_conf is not writable, please change the permission." | ||||
|       return 1 | ||||
|     fi | ||||
|     _backup_conf="$DOMAIN_BACKUP_PATH/vsftpd.conf.bak" | ||||
|     _info "Backup $_vsftpd_conf to $_backup_conf" | ||||
|     cp "$_vsftpd_conf" "$_backup_conf" | ||||
| 
 | ||||
|     _info "Modify vsftpd conf: $_vsftpd_conf" | ||||
|     if _setopt "$_vsftpd_conf" "rsa_cert_file" "=" "$_real_fullchain" \ | ||||
|       && _setopt "$_vsftpd_conf" "rsa_private_key_file" "=" "$_real_key" \ | ||||
|       && _setopt "$_vsftpd_conf" "ssl_enable" "=" "YES"; then | ||||
|       _info "Set config success!" | ||||
|     else | ||||
|       _err "Config vsftpd server error, please report bug to us." | ||||
|       _info "Restoring vsftpd conf" | ||||
|       if cat "$_backup_conf" >"$_vsftpd_conf"; then | ||||
|         _info "Restore conf success" | ||||
|         eval "$_reload" | ||||
|       else | ||||
|         _err "Oops, error restore vsftpd conf, please report bug to us." | ||||
|       fi | ||||
|       return 1 | ||||
|     fi | ||||
|   fi | ||||
| 
 | ||||
|   _info "Run reload: $_reload" | ||||
|   if eval "$_reload"; then | ||||
|     _info "Reload success!" | ||||
|     if [ "$DEPLOY_VSFTPD_CONF" ]; then | ||||
|       _savedomainconf DEPLOY_VSFTPD_CONF "$DEPLOY_VSFTPD_CONF" | ||||
|     else | ||||
|       _cleardomainconf DEPLOY_VSFTPD_CONF | ||||
|     fi | ||||
|     if [ "$DEPLOY_VSFTPD_RELOAD" ]; then | ||||
|       _savedomainconf DEPLOY_VSFTPD_RELOAD "$DEPLOY_VSFTPD_RELOAD" | ||||
|     else | ||||
|       _cleardomainconf DEPLOY_VSFTPD_RELOAD | ||||
|     fi | ||||
|     return 0 | ||||
|   else | ||||
|     _err "Reload error, restoring" | ||||
|     if cat "$_backup_conf" >"$_vsftpd_conf"; then | ||||
|       _info "Restore conf success" | ||||
|       eval "$_reload" | ||||
|     else | ||||
|       _err "Oops, error restore vsftpd conf, please report bug to us." | ||||
|     fi | ||||
|     return 1 | ||||
|   fi | ||||
|   return 0 | ||||
| } | ||||
							
								
								
									
										418
									
								
								dnsapi/README.md
									
									
									
									
									
								
							
							
						
						
									
										418
									
								
								dnsapi/README.md
									
									
									
									
									
								
							| @ -140,7 +140,7 @@ Finally, make the DNS server and update Key available to `acme.sh` | ||||
| 
 | ||||
| ``` | ||||
| export NSUPDATE_SERVER="dns.example.com" | ||||
| export NSUPDATE_KEY="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa==" | ||||
| export NSUPDATE_KEY="/path/to/your/nsupdate.key" | ||||
| ``` | ||||
| 
 | ||||
| Ok, let's issue a cert now: | ||||
| @ -240,7 +240,418 @@ acme.sh --issue --dns dns_ispconfig -d example.com -d www.example.com | ||||
| 
 | ||||
| The `ISPC_User`, `ISPC_Password`, `ISPC_Api`and `ISPC_Api_Insecure` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. | ||||
| 
 | ||||
| # 13. Use custom API | ||||
| ## 13. Use Alwaysdata domain API | ||||
| 
 | ||||
| First you need to login to your Alwaysdata account to get your API Key. | ||||
| 
 | ||||
| ```sh | ||||
| export AD_API_KEY="myalwaysdataapikey" | ||||
| ``` | ||||
| 
 | ||||
| Ok, let's issue a cert now: | ||||
| 
 | ||||
| ```sh | ||||
| acme.sh --issue --dns dns_ad -d example.com -d www.example.com | ||||
| ``` | ||||
| 
 | ||||
| The `AD_API_KEY` will be saved in `~/.acme.sh/account.conf` and will be reused | ||||
| when needed. | ||||
| 
 | ||||
| ## 14. Use Linode domain API | ||||
| 
 | ||||
| First you need to login to your Linode account to get your API Key. | ||||
| [https://manager.linode.com/profile/api](https://manager.linode.com/profile/api) | ||||
| 
 | ||||
| Then add an API key with label *ACME* and copy the new key. | ||||
| 
 | ||||
| ```sh | ||||
| export LINODE_API_KEY="..." | ||||
| ``` | ||||
| 
 | ||||
| Due to the reload time of any changes in the DNS records, we have to use the `dnssleep` option to wait at least 15 minutes for the changes to take effect. | ||||
| 
 | ||||
| Ok, let's issue a cert now: | ||||
| 
 | ||||
| ```sh | ||||
| acme.sh --issue --dns dns_linode --dnssleep 900 -d example.com -d www.example.com | ||||
| ``` | ||||
| 
 | ||||
| The `LINODE_API_KEY` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. | ||||
| 
 | ||||
| ## 15. Use FreeDNS | ||||
| 
 | ||||
| FreeDNS (https://freedns.afraid.org/) does not provide an API to update DNS records (other than IPv4 and IPv6 | ||||
| dynamic DNS addresses).  The acme.sh plugin therefore retrieves and updates domain TXT records by logging | ||||
| into the FreeDNS website to read the HTML and posting updates as HTTP.  The plugin needs to know your | ||||
| userid and password for the FreeDNS website. | ||||
| 
 | ||||
| ```sh | ||||
| export FREEDNS_User="..." | ||||
| export FREEDNS_Password="..." | ||||
| ``` | ||||
| 
 | ||||
| You need only provide this the first time you run the acme.sh client with FreeDNS validation and then again | ||||
| whenever you change your password at the FreeDNS site.  The acme.sh FreeDNS plugin does not store your userid | ||||
| or password but rather saves an authentication token returned by FreeDNS in `~/.acme.sh/account.conf` and | ||||
| reuses that when needed. | ||||
| 
 | ||||
| Now you can issue a certificate. | ||||
| 
 | ||||
| ```sh | ||||
| acme.sh --issue --dns dns_freedns -d example.com -d www.example.com | ||||
| ``` | ||||
| 
 | ||||
| Note that you cannot use acme.sh automatic DNS validation for FreeDNS public domains or for a subdomain that | ||||
| you create under a FreeDNS public domain.  You must own the top level domain in order to automatically | ||||
| validate with acme.sh at FreeDNS. | ||||
| 
 | ||||
| ## 16. Use cyon.ch | ||||
| 
 | ||||
| You only need to set your cyon.ch login credentials. | ||||
| If you also have 2 Factor Authentication (OTP) enabled, you need to set your secret token too and have `oathtool` installed. | ||||
| 
 | ||||
| ``` | ||||
| export CY_Username="your_cyon_username" | ||||
| export CY_Password="your_cyon_password" | ||||
| export CY_OTP_Secret="your_otp_secret" # Only required if using 2FA | ||||
| ``` | ||||
| 
 | ||||
| To issue a cert: | ||||
| ``` | ||||
| acme.sh --issue --dns dns_cyon -d example.com -d www.example.com | ||||
| ``` | ||||
| 
 | ||||
| The `CY_Username`, `CY_Password` and `CY_OTP_Secret` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. | ||||
| 
 | ||||
| ## 17. Use Domain-Offensive/Resellerinterface/Domainrobot API | ||||
| 
 | ||||
| You will need your login credentials (Partner ID+Password) to the Resellerinterface, and export them before you run `acme.sh`: | ||||
| ``` | ||||
| export DO_PID="KD-1234567" | ||||
| export DO_PW="cdfkjl3n2" | ||||
| ``` | ||||
| 
 | ||||
| Ok, let's issue a cert now: | ||||
| ``` | ||||
| acme.sh --issue --dns dns_do -d example.com -d www.example.com | ||||
| ``` | ||||
| 
 | ||||
| ## 18. Use Gandi LiveDNS API | ||||
| 
 | ||||
| You must enable the new Gandi LiveDNS API first and the create your api key, See: http://doc.livedns.gandi.net/ | ||||
| 
 | ||||
| ``` | ||||
| export GANDI_LIVEDNS_KEY="fdmlfsdklmfdkmqsdfk" | ||||
| ``` | ||||
| 
 | ||||
| Ok, let's issue a cert now: | ||||
| ``` | ||||
| acme.sh --issue --dns dns_gandi_livedns -d example.com -d www.example.com | ||||
| ``` | ||||
| 
 | ||||
| ## 19. Use Knot (knsupdate) DNS API to automatically issue cert | ||||
| 
 | ||||
| First, generate a TSIG key for updating the zone. | ||||
| 
 | ||||
| ``` | ||||
| keymgr tsig generate acme_key algorithm hmac-sha512 > /etc/knot/acme.key | ||||
| ``` | ||||
| 
 | ||||
| Include this key in your knot configuration file. | ||||
| 
 | ||||
| ``` | ||||
| include: /etc/knot/acme.key | ||||
| ``` | ||||
| 
 | ||||
| Next, configure your zone to allow dynamic updates. | ||||
| 
 | ||||
| Dynamic updates for the zone are allowed via proper ACL rule with the `update` action. For in-depth instructions, please see [Knot DNS's documentation](https://www.knot-dns.cz/documentation/). | ||||
| 
 | ||||
| ``` | ||||
| acl: | ||||
|   - id: acme_acl | ||||
|     address: 192.168.1.0/24 | ||||
|     key: acme_key | ||||
|     action: update | ||||
| 
 | ||||
| zone: | ||||
|   - domain: example.com | ||||
|     file: example.com.zone | ||||
|     acl: acme_acl | ||||
| ``` | ||||
| 
 | ||||
| Finally, make the DNS server and TSIG Key available to `acme.sh` | ||||
| 
 | ||||
| ``` | ||||
| export KNOT_SERVER="dns.example.com" | ||||
| export KNOT_KEY=`grep \# /etc/knot/acme.key | cut -d' ' -f2` | ||||
| ``` | ||||
| 
 | ||||
| Ok, let's issue a cert now: | ||||
| ``` | ||||
| acme.sh --issue --dns dns_knot -d example.com -d www.example.com | ||||
| ``` | ||||
| 
 | ||||
| The `KNOT_SERVER` and `KNOT_KEY` settings will be saved in `~/.acme.sh/account.conf` and will be reused when needed. | ||||
| 
 | ||||
| ## 20. Use DigitalOcean API (native) | ||||
| 
 | ||||
| You need to obtain a read and write capable API key from your DigitalOcean account. See: https://www.digitalocean.com/help/api/ | ||||
| 
 | ||||
| ``` | ||||
| export DO_API_KEY="75310dc4ca779ac39a19f6355db573b49ce92ae126553ebd61ac3a3ae34834cc" | ||||
| ``` | ||||
| 
 | ||||
| Ok, let's issue a cert now: | ||||
| ``` | ||||
| acme.sh --issue --dns dns_dgon -d example.com -d www.example.com | ||||
| ``` | ||||
| 
 | ||||
| ## 21. Use ClouDNS.net API | ||||
| 
 | ||||
| You need to set the HTTP API user ID and password credentials. See: https://www.cloudns.net/wiki/article/42/ | ||||
| 
 | ||||
| ``` | ||||
| export CLOUDNS_AUTH_ID=XXXXX | ||||
| export CLOUDNS_AUTH_PASSWORD="YYYYYYYYY" | ||||
| ``` | ||||
| 
 | ||||
| Ok, let's issue a cert now: | ||||
| ``` | ||||
| acme.sh --issue --dns dns_cloudns -d example.com -d www.example.com | ||||
| ``` | ||||
| The `CLOUDNS_AUTH_ID` and `CLOUDNS_AUTH_PASSWORD` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. | ||||
| 
 | ||||
| ## 22. Use Infoblox API | ||||
| 
 | ||||
| First you need to create/obtain API credentials on your Infoblox appliance. | ||||
| 
 | ||||
| ``` | ||||
| export Infoblox_Creds="username:password" | ||||
| export Infoblox_Server="ip or fqdn of infoblox appliance" | ||||
| ``` | ||||
| 
 | ||||
| Ok, let's issue a cert now: | ||||
| ``` | ||||
| acme.sh --issue --dns dns_infoblox -d example.com -d www.example.com | ||||
| ``` | ||||
| 
 | ||||
| Note: This script will automatically create and delete the ephemeral txt record. | ||||
| The `Infoblox_Creds` and `Infoblox_Server` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. | ||||
| 
 | ||||
| 
 | ||||
| ## 23. Use VSCALE API | ||||
| 
 | ||||
| First you need to create/obtain API tokens on your [settings panel](https://vscale.io/panel/settings/tokens/). | ||||
| 
 | ||||
| ``` | ||||
| VSCALE_API_KEY="sdfsdfsdfljlbjkljlkjsdfoiwje" | ||||
| ``` | ||||
| 
 | ||||
| Ok, let's issue a cert now: | ||||
| ``` | ||||
| acme.sh --issue --dns dns_vscale -d example.com -d www.example.com | ||||
| ``` | ||||
| 
 | ||||
| ##  24. Use Dynu API | ||||
| 
 | ||||
| First you need to create/obtain API credentials from your Dynu account. See: https://www.dynu.com/resources/api/documentation | ||||
| 
 | ||||
| ``` | ||||
| export Dynu_ClientId="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" | ||||
| export Dynu_Secret="yyyyyyyyyyyyyyyyyyyyyyyyy" | ||||
| ``` | ||||
| 
 | ||||
| Ok, let's issue a cert now: | ||||
| ``` | ||||
| acme.sh --issue --dns dns_dynu -d example.com -d www.example.com | ||||
| ``` | ||||
| 
 | ||||
| The `Dynu_ClientId` and `Dynu_Secret` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. | ||||
| 
 | ||||
| ## 25. Use DNSimple API | ||||
| 
 | ||||
| First you need to login to your DNSimple account and generate a new oauth token. | ||||
| 
 | ||||
| https://dnsimple.com/a/{your account id}/account/access_tokens | ||||
| 
 | ||||
| Note that this is an _account_ token and not a user token. The account token is | ||||
| needed to infer the `account_id` used in requests. A user token will not be able | ||||
| to determine the correct account to use. | ||||
| 
 | ||||
| ``` | ||||
| export DNSimple_OAUTH_TOKEN="sdfsdfsdfljlbjkljlkjsdfoiwje" | ||||
| ``` | ||||
| 
 | ||||
| To issue the cert just specify the `dns_dnsimple` API. | ||||
| 
 | ||||
| ``` | ||||
| acme.sh --issue --dns dns_dnsimple -d example.com | ||||
| ``` | ||||
| 
 | ||||
| The `DNSimple_OAUTH_TOKEN` will be saved in `~/.acme.sh/account.conf` and will | ||||
| be reused when needed. | ||||
| 
 | ||||
| If you have any issues with this integration please report them to | ||||
| https://github.com/pho3nixf1re/acme.sh/issues. | ||||
| 
 | ||||
| ## 26. Use NS1.com API | ||||
| 
 | ||||
| ``` | ||||
| export NS1_Key="fdmlfsdklmfdkmqsdfk" | ||||
| ``` | ||||
| 
 | ||||
| Ok, let's issue a cert now: | ||||
| ``` | ||||
| acme.sh --issue --dns dns_nsone -d example.com -d www.example.com | ||||
| ``` | ||||
| 
 | ||||
| ## 27. Use DuckDNS.org API | ||||
| 
 | ||||
| ``` | ||||
| export DuckDNS_Token="aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" | ||||
| ``` | ||||
| 
 | ||||
| Please note that since DuckDNS uses StartSSL as their cert provider, thus  | ||||
| --insecure may need to be used when issuing certs: | ||||
| ``` | ||||
| acme.sh --insecure --issue --dns dns_duckdns -d mydomain.duckdns.org | ||||
| ``` | ||||
| 
 | ||||
| For issues, please report to https://github.com/raidenii/acme.sh/issues. | ||||
| 
 | ||||
| ## 28. Use Name.com API | ||||
| 
 | ||||
| You'll need to fill out the form at https://www.name.com/reseller/apply to apply | ||||
| for API username and token. | ||||
| 
 | ||||
| ``` | ||||
| export Namecom_Username="testuser" | ||||
| export Namecom_Token="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" | ||||
| ``` | ||||
| 
 | ||||
| And now you can issue certs with: | ||||
| 
 | ||||
| ``` | ||||
| acme.sh --issue --dns dns_namecom -d example.com -d www.example.com | ||||
| ``` | ||||
| 
 | ||||
| For issues, please report to https://github.com/raidenii/acme.sh/issues. | ||||
| 
 | ||||
| ## 29. Use Dyn Managed DNS API to automatically issue cert | ||||
| 
 | ||||
| First, login to your Dyn Managed DNS account: https://portal.dynect.net/login/ | ||||
| 
 | ||||
| It is recommended to add a new user specific for API access. | ||||
| 
 | ||||
| The minimum "Zones & Records Permissions" required are: | ||||
| ``` | ||||
| RecordAdd | ||||
| RecordUpdate | ||||
| RecordDelete | ||||
| RecordGet | ||||
| ZoneGet | ||||
| ZoneAddNode | ||||
| ZoneRemoveNode | ||||
| ZonePublish | ||||
| ``` | ||||
| 
 | ||||
| Pass the API user credentials to the environment: | ||||
| ``` | ||||
| export DYN_Customer="customer" | ||||
| export DYN_Username="apiuser" | ||||
| export DYN_Password="secret" | ||||
| ``` | ||||
| 
 | ||||
| Ok, let's issue a cert now: | ||||
| ``` | ||||
| acme.sh --issue --dns dns_dyn -d example.com -d www.example.com | ||||
| ``` | ||||
| 
 | ||||
| The `DYN_Customer`, `DYN_Username` and `DYN_Password` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. | ||||
| 
 | ||||
| ## 30. Use pdd.yandex.ru API | ||||
| 
 | ||||
| ``` | ||||
| export PDD_Token="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" | ||||
| ``` | ||||
| 
 | ||||
| Follow these instructions to get the token for your domain https://tech.yandex.com/domain/doc/concepts/access-docpage/ | ||||
| ``` | ||||
| acme.sh --issue --dns dns_yandex -d mydomain.example.org | ||||
| ``` | ||||
| 
 | ||||
| For issues, please report to https://github.com/non7top/acme.sh/issues. | ||||
| 
 | ||||
| ## 31. Use Hurricane Electric | ||||
| 
 | ||||
| Hurricane Electric doesn't have an API so just set your login credentials like so: | ||||
| 
 | ||||
| ``` | ||||
| export HE_Username="yourusername" | ||||
| export HE_Password="password" | ||||
| ``` | ||||
| 
 | ||||
| Then you can issue your certificate: | ||||
| 
 | ||||
| ``` | ||||
| acme.sh --issue --dns dns_he -d example.com -d www.example.com | ||||
| ``` | ||||
| 
 | ||||
| The `HE_Username` and `HE_Password` settings will be saved in `~/.acme.sh/account.conf` and will be reused when needed. | ||||
| 
 | ||||
| Please report any issues to https://github.com/angel333/acme.sh or to <me@ondrejsimek.com>. | ||||
| 
 | ||||
| ## 32. Use UnoEuro API to automatically issue cert | ||||
| 
 | ||||
| First you need to login to your UnoEuro account to get your API key. | ||||
| 
 | ||||
| ``` | ||||
| export UNO_Key="sdfsdfsdfljlbjkljlkjsdfoiwje" | ||||
| export UNO_User="UExxxxxx" | ||||
| ``` | ||||
| 
 | ||||
| Ok, let's issue a cert now: | ||||
| ``` | ||||
| acme.sh --issue --dns dns_unoeuro -d example.com -d www.example.com | ||||
| ``` | ||||
| 
 | ||||
| The `UNO_Key` and `UNO_User` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. | ||||
| 
 | ||||
| ## 33. Use INWX | ||||
| 
 | ||||
| [INWX](https://www.inwx.de/) offers an [xmlrpc api](https://www.inwx.de/de/help/apidoc)  with your standard login credentials, set them like so: | ||||
| 
 | ||||
| ``` | ||||
| export INWX_User="yourusername" | ||||
| export INWX_Password="password" | ||||
| ``` | ||||
| 
 | ||||
| Then you can issue your certificates with: | ||||
| 
 | ||||
| ``` | ||||
| acme.sh --issue --dns dns_inwx -d example.com -d www.example.com | ||||
| ``` | ||||
| 
 | ||||
| The `INWX_User` and `INWX_Password` settings will be saved in `~/.acme.sh/account.conf` and will be reused when needed. | ||||
| 
 | ||||
| ## 34. User Servercow API v1 | ||||
| 
 | ||||
| Create a new user from the servercow control center. Don't forget to activate **DNS API** for this user. | ||||
| 
 | ||||
| ``` | ||||
| export SERVERCOW_API_Username=username | ||||
| export SERVERCOW_API_Password=password | ||||
| ``` | ||||
| 
 | ||||
| Now you cann issue a cert: | ||||
| 
 | ||||
| ``` | ||||
| acme.sh --issue --dns dns_servercow -d example.com -d www.example.com | ||||
| ``` | ||||
| Both, `SERVERCOW_API_Username` and `SERVERCOW_API_Password` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. | ||||
| 
 | ||||
| # Use custom API | ||||
| 
 | ||||
| If your API is not supported yet, you can write your own DNS API. | ||||
| 
 | ||||
| @ -256,7 +667,8 @@ acme.sh --issue --dns dns_myapi -d example.com -d www.example.com | ||||
| 
 | ||||
| For more details, please check our sample script: [dns_myapi.sh](dns_myapi.sh) | ||||
| 
 | ||||
| See:  https://github.com/Neilpang/acme.sh/wiki/DNS-API-Dev-Guide | ||||
| 
 | ||||
| ## 14. Use lexicon DNS API | ||||
| # Use lexicon DNS API | ||||
| 
 | ||||
| https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api | ||||
|  | ||||
							
								
								
									
										147
									
								
								dnsapi/dns_ad.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										147
									
								
								dnsapi/dns_ad.sh
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,147 @@ | ||||
| #!/usr/bin/env sh | ||||
| 
 | ||||
| # | ||||
| #AD_API_KEY="sdfsdfsdfljlbjkljlkjsdfoiwje" | ||||
| 
 | ||||
| #This is the Alwaysdata api wrapper for acme.sh | ||||
| # | ||||
| #Author: Paul Koppen | ||||
| #Report Bugs here: https://github.com/wpk-/acme.sh | ||||
| 
 | ||||
| AD_API_URL="https://$AD_API_KEY:@api.alwaysdata.com/v1" | ||||
| 
 | ||||
| ########  Public functions ##################### | ||||
| 
 | ||||
| #Usage: dns_myapi_add   _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" | ||||
| dns_ad_add() { | ||||
|   fulldomain=$1 | ||||
|   txtvalue=$2 | ||||
| 
 | ||||
|   if [ -z "$AD_API_KEY" ]; then | ||||
|     AD_API_KEY="" | ||||
|     _err "You didn't specify the AD api key yet." | ||||
|     _err "Please create you key and try again." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _saveaccountconf AD_API_KEY "$AD_API_KEY" | ||||
| 
 | ||||
|   _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" | ||||
| 
 | ||||
|   _ad_tmpl_json="{\"domain\":$_domain_id,\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"value\":\"$txtvalue\"}" | ||||
| 
 | ||||
|   if _ad_rest POST "record/" "$_ad_tmpl_json" && [ -z "$response" ]; then | ||||
|     _info "txt record updated success." | ||||
|     return 0 | ||||
|   fi | ||||
| 
 | ||||
|   return 1 | ||||
| } | ||||
| 
 | ||||
| #fulldomain txtvalue | ||||
| dns_ad_rm() { | ||||
|   fulldomain=$1 | ||||
|   txtvalue=$2 | ||||
| 
 | ||||
|   _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" | ||||
|   _ad_rest GET "record/?domain=$_domain_id&name=$_sub_domain" | ||||
| 
 | ||||
|   if [ -n "$response" ]; then | ||||
|     record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\s*[0-9]+" | 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 _ad_rest DELETE "record/$record_id/" && [ -z "$response" ]; then | ||||
|       _info "txt record deleted success." | ||||
|       return 0 | ||||
|     fi | ||||
|     _debug response "$response" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   return 1 | ||||
| } | ||||
| 
 | ||||
| ####################  Private functions below ################################## | ||||
| #_acme-challenge.www.domain.com | ||||
| #returns | ||||
| # _sub_domain=_acme-challenge.www | ||||
| # _domain=domain.com | ||||
| # _domain_id=12345 | ||||
| _get_root() { | ||||
|   domain=$1 | ||||
|   i=2 | ||||
|   p=1 | ||||
| 
 | ||||
|   if _ad_rest GET "domain/"; then | ||||
|     response="$(echo "$response" | tr -d "\n" | sed 's/{/\n&/g')" | ||||
|     while true; do | ||||
|       h=$(printf "%s" "$domain" | cut -d . -f $i-100) | ||||
|       _debug h "$h" | ||||
|       if [ -z "$h" ]; then | ||||
|         #not valid | ||||
|         return 1 | ||||
|       fi | ||||
| 
 | ||||
|       hostedzone="$(echo "$response" | _egrep_o "{.*\"name\":\s*\"$h\".*}")" | ||||
|       if [ "$hostedzone" ]; then | ||||
|         _domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o "\"id\":\s*[0-9]+" | _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 | ||||
|   fi | ||||
|   return 1 | ||||
| } | ||||
| 
 | ||||
| #method uri qstr data | ||||
| _ad_rest() { | ||||
|   mtd="$1" | ||||
|   ep="$2" | ||||
|   data="$3" | ||||
| 
 | ||||
|   _debug mtd "$mtd" | ||||
|   _debug ep "$ep" | ||||
| 
 | ||||
|   export _H1="Accept: application/json" | ||||
|   export _H2="Content-Type: application/json" | ||||
| 
 | ||||
|   if [ "$mtd" != "GET" ]; then | ||||
|     # both POST and DELETE. | ||||
|     _debug data "$data" | ||||
|     response="$(_post "$data" "$AD_API_URL/$ep" "" "$mtd")" | ||||
|   else | ||||
|     response="$(_get "$AD_API_URL/$ep")" | ||||
|   fi | ||||
| 
 | ||||
|   if [ "$?" != "0" ]; then | ||||
|     _err "error $ep" | ||||
|     return 1 | ||||
|   fi | ||||
|   _debug2 response "$response" | ||||
|   return 0 | ||||
| } | ||||
							
								
								
									
										4
									
								
								dnsapi/dns_ali.sh
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										4
									
								
								dnsapi/dns_ali.sh
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @ -35,7 +35,7 @@ dns_ali_rm() { | ||||
|   _clean | ||||
| } | ||||
| 
 | ||||
| ####################  Private functions bellow ################################## | ||||
| ####################  Private functions below ################################## | ||||
| 
 | ||||
| _get_root() { | ||||
|   domain=$1 | ||||
| @ -67,7 +67,7 @@ _get_root() { | ||||
| } | ||||
| 
 | ||||
| _ali_rest() { | ||||
|   signature=$(printf "%s" "GET&%2F&$(_ali_urlencode "$query")" | _hmac "sha1" "$(_hex "$Ali_Secret&")" | _base64) | ||||
|   signature=$(printf "%s" "GET&%2F&$(_ali_urlencode "$query")" | _hmac "sha1" "$(printf "%s" "$Ali_Secret&" | _hex_dump | tr -d " ")" | _base64) | ||||
|   signature=$(_ali_urlencode "$signature") | ||||
|   url="$Ali_API?$query&Signature=$signature" | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										78
									
								
								dnsapi/dns_aws.sh
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										78
									
								
								dnsapi/dns_aws.sh
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @ -27,8 +27,10 @@ dns_aws_add() { | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   if [ -z "$AWS_SESSION_TOKEN" ]; then | ||||
|     _saveaccountconf AWS_ACCESS_KEY_ID "$AWS_ACCESS_KEY_ID" | ||||
|     _saveaccountconf AWS_SECRET_ACCESS_KEY "$AWS_SECRET_ACCESS_KEY" | ||||
|   fi | ||||
| 
 | ||||
|   _debug "First detect the root zone" | ||||
|   if ! _get_root "$fulldomain"; then | ||||
| @ -42,20 +44,39 @@ dns_aws_add() { | ||||
|   _aws_tmpl_xml="<ChangeResourceRecordSetsRequest xmlns=\"https://route53.amazonaws.com/doc/2013-04-01/\"><ChangeBatch><Changes><Change><Action>UPSERT</Action><ResourceRecordSet><Name>$fulldomain</Name><Type>TXT</Type><TTL>300</TTL><ResourceRecords><ResourceRecord><Value>\"$txtvalue\"</Value></ResourceRecord></ResourceRecords></ResourceRecordSet></Change></Changes></ChangeBatch></ChangeResourceRecordSetsRequest>" | ||||
| 
 | ||||
|   if aws_rest POST "2013-04-01$_domain_id/rrset/" "" "$_aws_tmpl_xml" && _contains "$response" "ChangeResourceRecordSetsResponse"; then | ||||
|     _info "txt record updated sucess." | ||||
|     _info "txt record updated success." | ||||
|     return 0 | ||||
|   fi | ||||
| 
 | ||||
|   return 1 | ||||
| } | ||||
| 
 | ||||
| #fulldomain | ||||
| #fulldomain txtvalue | ||||
| dns_aws_rm() { | ||||
|   fulldomain=$1 | ||||
|   txtvalue=$2 | ||||
| 
 | ||||
|   _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" | ||||
| 
 | ||||
|   _aws_tmpl_xml="<ChangeResourceRecordSetsRequest xmlns=\"https://route53.amazonaws.com/doc/2013-04-01/\"><ChangeBatch><Changes><Change><Action>DELETE</Action><ResourceRecordSet><ResourceRecords><ResourceRecord><Value>\"$txtvalue\"</Value></ResourceRecord></ResourceRecords><Name>$fulldomain.</Name><Type>TXT</Type><TTL>300</TTL></ResourceRecordSet></Change></Changes></ChangeBatch></ChangeResourceRecordSetsRequest>" | ||||
| 
 | ||||
|   if aws_rest POST "2013-04-01$_domain_id/rrset/" "" "$_aws_tmpl_xml" && _contains "$response" "ChangeResourceRecordSetsResponse"; then | ||||
|     _info "txt record deleted success." | ||||
|     return 0 | ||||
|   fi | ||||
| 
 | ||||
|   return 1 | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| ####################  Private functions bellow ################################## | ||||
| ####################  Private functions below ################################## | ||||
| 
 | ||||
| _get_root() { | ||||
|   domain=$1 | ||||
| @ -66,26 +87,40 @@ _get_root() { | ||||
|     _debug "response" "$response" | ||||
|     while true; do | ||||
|       h=$(printf "%s" "$domain" | cut -d . -f $i-100) | ||||
|       _debug2 "Checking domain: $h" | ||||
|       if [ -z "$h" ]; then | ||||
|         if _contains "$response" "<IsTruncated>true</IsTruncated>" && _contains "$response" "<NextMarker>"; then | ||||
|           _debug "IsTruncated" | ||||
|           _nextMarker="$(echo "$response" | _egrep_o "<NextMarker>.*</NextMarker>" | cut -d '>' -f 2 | cut -d '<' -f 1)" | ||||
|           _debug "NextMarker" "$_nextMarker" | ||||
|           if aws_rest GET "2013-04-01/hostedzone" "marker=$_nextMarker"; then | ||||
|             _debug "Truncated request OK" | ||||
|             i=2 | ||||
|             p=1 | ||||
|             continue | ||||
|           else | ||||
|             _err "Truncated request error." | ||||
|           fi | ||||
|         fi | ||||
|         #not valid | ||||
|         _err "Invalid domain" | ||||
|         return 1 | ||||
|       fi | ||||
| 
 | ||||
|       if _contains "$response" "<Name>$h.</Name>"; then | ||||
|         hostedzone="$(echo "$response" | _egrep_o "<HostedZone>.*<Name>$h.</Name>.*</HostedZone>")" | ||||
|         hostedzone="$(echo "$response" | sed 's/<HostedZone>/#&/g' | tr '#' '\n' | _egrep_o "<HostedZone><Id>[^<]*<.Id><Name>$h.<.Name>.*<PrivateZone>false<.PrivateZone>.*<.HostedZone>")" | ||||
|         _debug hostedzone "$hostedzone" | ||||
|         if [ -z "$hostedzone" ]; then | ||||
|           _err "Error, can not get hostedzone." | ||||
|           return 1 | ||||
|         fi | ||||
|         _domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o "<Id>.*</Id>" | head -n 1 | _egrep_o ">.*<" | tr -d "<>") | ||||
|         if [ "$hostedzone" ]; then | ||||
|           _domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o "<Id>.*<.Id>" | head -n 1 | _egrep_o ">.*<" | tr -d "<>") | ||||
|           if [ "$_domain_id" ]; then | ||||
|             _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) | ||||
|             _domain=$h | ||||
|             return 0 | ||||
|           fi | ||||
|           _err "Can not find domain id: $h" | ||||
|           return 1 | ||||
|         fi | ||||
|       fi | ||||
|       p=$i | ||||
|       i=$(_math "$i" + 1) | ||||
|     done | ||||
| @ -116,13 +151,17 @@ aws_rest() { | ||||
| 
 | ||||
|   #RequestDate="20161120T141056Z" ############## | ||||
| 
 | ||||
|   _H1="x-amz-date: $RequestDate" | ||||
|   export _H1="x-amz-date: $RequestDate" | ||||
| 
 | ||||
|   aws_host="$AWS_HOST" | ||||
|   CanonicalHeaders="host:$aws_host\nx-amz-date:$RequestDate\n" | ||||
|   _debug2 CanonicalHeaders "$CanonicalHeaders" | ||||
| 
 | ||||
|   SignedHeaders="host;x-amz-date" | ||||
|   if [ -n "$AWS_SESSION_TOKEN" ]; then | ||||
|     export _H3="x-amz-security-token: $AWS_SESSION_TOKEN" | ||||
|     CanonicalHeaders="${CanonicalHeaders}x-amz-security-token:$AWS_SESSION_TOKEN\n" | ||||
|     SignedHeaders="${SignedHeaders};x-amz-security-token" | ||||
|   fi | ||||
|   _debug2 CanonicalHeaders "$CanonicalHeaders" | ||||
|   _debug2 SignedHeaders "$SignedHeaders" | ||||
| 
 | ||||
|   RequestPayload="$data" | ||||
| @ -156,10 +195,10 @@ aws_rest() { | ||||
| 
 | ||||
|   #kSecret="wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY" ############################ | ||||
| 
 | ||||
|   _debug2 kSecret "$kSecret" | ||||
|   _secure_debug2 kSecret "$kSecret" | ||||
| 
 | ||||
|   kSecretH="$(_hex "$kSecret")" | ||||
|   _debug2 kSecretH "$kSecretH" | ||||
|   kSecretH="$(printf "%s" "$kSecret" | _hex_dump | tr -d " ")" | ||||
|   _secure_debug2 kSecretH "$kSecretH" | ||||
| 
 | ||||
|   kDateH="$(printf "$RequestDateOnly%s" | _hmac "$Hash" "$kSecretH" hex)" | ||||
|   _debug2 kDateH "$kDateH" | ||||
| @ -170,7 +209,7 @@ aws_rest() { | ||||
|   kServiceH="$(printf "$Service%s" | _hmac "$Hash" "$kRegionH" hex)" | ||||
|   _debug2 kServiceH "$kServiceH" | ||||
| 
 | ||||
|   kSigningH="$(printf "aws4_request%s" | _hmac "$Hash" "$kServiceH" hex)" | ||||
|   kSigningH="$(printf "%s" "aws4_request" | _hmac "$Hash" "$kServiceH" hex)" | ||||
|   _debug2 kSigningH "$kSigningH" | ||||
| 
 | ||||
|   signature="$(printf "$StringToSign%s" | _hmac "$Hash" "$kSigningH" hex)" | ||||
| @ -179,10 +218,13 @@ aws_rest() { | ||||
|   Authorization="$Algorithm Credential=$AWS_ACCESS_KEY_ID/$CredentialScope, SignedHeaders=$SignedHeaders, Signature=$signature" | ||||
|   _debug2 Authorization "$Authorization" | ||||
| 
 | ||||
|   _H3="Authorization: $Authorization" | ||||
|   _debug _H3 "$_H3" | ||||
|   _H2="Authorization: $Authorization" | ||||
|   _debug _H2 "$_H2" | ||||
| 
 | ||||
|   url="$AWS_URL/$ep" | ||||
|   if [ "$qsr" ]; then | ||||
|     url="$AWS_URL/$ep?$qsr" | ||||
|   fi | ||||
| 
 | ||||
|   if [ "$mtd" = "GET" ]; then | ||||
|     response="$(_get "$url")" | ||||
|  | ||||
| @ -14,6 +14,8 @@ dns_cf_add() { | ||||
|   fulldomain=$1 | ||||
|   txtvalue=$2 | ||||
| 
 | ||||
|   CF_Key="${CF_Key:-$(_readaccountconf_mutable CF_Key)}" | ||||
|   CF_Email="${CF_Email:-$(_readaccountconf_mutable CF_Email)}" | ||||
|   if [ -z "$CF_Key" ] || [ -z "$CF_Email" ]; then | ||||
|     CF_Key="" | ||||
|     CF_Email="" | ||||
| @ -29,8 +31,8 @@ dns_cf_add() { | ||||
|   fi | ||||
| 
 | ||||
|   #save the api key and email to the account conf file. | ||||
|   _saveaccountconf CF_Key "$CF_Key" | ||||
|   _saveaccountconf CF_Email "$CF_Email" | ||||
|   _saveaccountconf_mutable CF_Key "$CF_Key" | ||||
|   _saveaccountconf_mutable CF_Email "$CF_Email" | ||||
| 
 | ||||
|   _debug "First detect the root zone" | ||||
|   if ! _get_root "$fulldomain"; then | ||||
| @ -55,9 +57,7 @@ dns_cf_add() { | ||||
|     _info "Adding record" | ||||
|     if _cf_rest POST "zones/$_domain_id/dns_records" "{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"ttl\":120}"; then | ||||
|       if printf -- "%s" "$response" | grep "$fulldomain" >/dev/null; then | ||||
|         _info "Added, sleeping 10 seconds" | ||||
|         sleep 10 | ||||
|         #todo: check if the record takes effect | ||||
|         _info "Added, OK" | ||||
|         return 0 | ||||
|       else | ||||
|         _err "Add txt record error." | ||||
| @ -72,9 +72,7 @@ dns_cf_add() { | ||||
| 
 | ||||
|     _cf_rest PUT "zones/$_domain_id/dns_records/$record_id" "{\"id\":\"$record_id\",\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"zone_id\":\"$_domain_id\",\"zone_name\":\"$_domain\"}" | ||||
|     if [ "$?" = "0" ]; then | ||||
|       _info "Updated, sleeping 10 seconds" | ||||
|       sleep 10 | ||||
|       #todo: check if the record takes effect | ||||
|       _info "Updated, OK" | ||||
|       return 0 | ||||
|     fi | ||||
|     _err "Update error" | ||||
| @ -83,13 +81,59 @@ dns_cf_add() { | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #fulldomain | ||||
| #fulldomain txtvalue | ||||
| dns_cf_rm() { | ||||
|   fulldomain=$1 | ||||
|   txtvalue=$2 | ||||
| 
 | ||||
|   CF_Key="${CF_Key:-$(_readaccountconf_mutable CF_Key)}" | ||||
|   CF_Email="${CF_Email:-$(_readaccountconf_mutable CF_Email)}" | ||||
|   if [ -z "$CF_Key" ] || [ -z "$CF_Email" ]; then | ||||
|     CF_Key="" | ||||
|     CF_Email="" | ||||
|     _err "You don't specify cloudflare api key and email yet." | ||||
|     _err "Please create you key and try again." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _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" | ||||
|   _cf_rest GET "zones/${_domain_id}/dns_records?type=TXT&name=$fulldomain&content=$txtvalue" | ||||
| 
 | ||||
|   if ! printf "%s" "$response" | grep \"success\":true >/dev/null; then | ||||
|     _err "Error" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   count=$(printf "%s\n" "$response" | _egrep_o "\"count\":[^,]*" | 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 ! _cf_rest DELETE "zones/$_domain_id/dns_records/$record_id"; then | ||||
|       _err "Delete record error." | ||||
|       return 1 | ||||
|     fi | ||||
|     _contains "$response" '"success":true' | ||||
|   fi | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| ####################  Private functions bellow ################################## | ||||
| ####################  Private functions below ################################## | ||||
| #_acme-challenge.www.domain.com | ||||
| #returns | ||||
| # _sub_domain=_acme-challenge.www | ||||
| @ -101,6 +145,7 @@ _get_root() { | ||||
|   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 | ||||
| @ -110,8 +155,8 @@ _get_root() { | ||||
|       return 1 | ||||
|     fi | ||||
| 
 | ||||
|     if printf "%s" "$response" | grep "\"name\":\"$h\"" >/dev/null; then | ||||
|       _domain_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | head -n 1 | cut -d : -f 2 | tr -d \") | ||||
|     if _contains "$response" "\"name\":\"$h\"" >/dev/null; then | ||||
|       _domain_id=$(printf "%s\n" "$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 | ||||
| @ -131,11 +176,11 @@ _cf_rest() { | ||||
|   data="$3" | ||||
|   _debug "$ep" | ||||
| 
 | ||||
|   _H1="X-Auth-Email: $CF_Email" | ||||
|   _H2="X-Auth-Key: $CF_Key" | ||||
|   _H3="Content-Type: application/json" | ||||
|   export _H1="X-Auth-Email: $CF_Email" | ||||
|   export _H2="X-Auth-Key: $CF_Key" | ||||
|   export _H3="Content-Type: application/json" | ||||
| 
 | ||||
|   if [ "$data" ]; then | ||||
|   if [ "$m" != "GET" ]; then | ||||
|     _debug data "$data" | ||||
|     response="$(_post "$data" "$CF_Api/$ep" "" "$m")" | ||||
|   else | ||||
|  | ||||
							
								
								
									
										184
									
								
								dnsapi/dns_cloudns.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										184
									
								
								dnsapi/dns_cloudns.sh
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,184 @@ | ||||
| #!/usr/bin/env sh | ||||
| 
 | ||||
| # Author: Boyan Peychev <boyan at cloudns dot net> | ||||
| # Repository: https://github.com/ClouDNS/acme.sh/ | ||||
| 
 | ||||
| #CLOUDNS_AUTH_ID=XXXXX | ||||
| #CLOUDNS_AUTH_PASSWORD="YYYYYYYYY" | ||||
| CLOUDNS_API="https://api.cloudns.net" | ||||
| 
 | ||||
| ########  Public functions ##################### | ||||
| 
 | ||||
| #Usage: dns_cloudns_add   _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" | ||||
| dns_cloudns_add() { | ||||
|   _info "Using cloudns" | ||||
| 
 | ||||
|   if ! _dns_cloudns_init_check; then | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   zone="$(_dns_cloudns_get_zone_name "$1")" | ||||
|   if [ -z "$zone" ]; then | ||||
|     _err "Missing DNS zone at ClouDNS. Please log into your control panel and create the required DNS zone for the initial setup." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   host="$(echo "$1" | sed "s/\.$zone\$//")" | ||||
|   record=$2 | ||||
|   record_id=$(_dns_cloudns_get_record_id "$zone" "$host") | ||||
| 
 | ||||
|   _debug zone "$zone" | ||||
|   _debug host "$host" | ||||
|   _debug record "$record" | ||||
|   _debug record_id "$record_id" | ||||
| 
 | ||||
|   if [ -z "$record_id" ]; then | ||||
|     _info "Adding the TXT record for $1" | ||||
|     _dns_cloudns_http_api_call "dns/add-record.json" "domain-name=$zone&record-type=TXT&host=$host&record=$record&ttl=60" | ||||
|     if ! _contains "$response" "\"status\":\"Success\""; then | ||||
|       _err "Record cannot be added." | ||||
|       return 1 | ||||
|     fi | ||||
|     _info "Added." | ||||
|   else | ||||
|     _info "Updating the TXT record for $1" | ||||
|     _dns_cloudns_http_api_call "dns/mod-record.json" "domain-name=$zone&record-id=$record_id&record-type=TXT&host=$host&record=$record&ttl=60" | ||||
|     if ! _contains "$response" "\"status\":\"Success\""; then | ||||
|       _err "The TXT record for $1 cannot be updated." | ||||
|       return 1 | ||||
|     fi | ||||
|     _info "Updated." | ||||
|   fi | ||||
| 
 | ||||
|   return 0 | ||||
| } | ||||
| 
 | ||||
| #Usage: dns_cloudns_rm   _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" | ||||
| dns_cloudns_rm() { | ||||
|   _info "Using cloudns" | ||||
| 
 | ||||
|   if ! _dns_cloudns_init_check; then | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   if [ -z "$zone" ]; then | ||||
|     zone="$(_dns_cloudns_get_zone_name "$1")" | ||||
|     if [ -z "$zone" ]; then | ||||
|       _err "Missing DNS zone at ClouDNS. Please log into your control panel and create the required DNS zone for the initial setup." | ||||
|       return 1 | ||||
|     fi | ||||
|   fi | ||||
| 
 | ||||
|   host="$(echo "$1" | sed "s/\.$zone\$//")" | ||||
|   record=$2 | ||||
|   record_id=$(_dns_cloudns_get_record_id "$zone" "$host") | ||||
| 
 | ||||
|   _debug zone "$zone" | ||||
|   _debug host "$host" | ||||
|   _debug record "$record" | ||||
|   _debug record_id "$record_id" | ||||
| 
 | ||||
|   if [ ! -z "$record_id" ]; then | ||||
|     _info "Deleting the TXT record for $1" | ||||
|     _dns_cloudns_http_api_call "dns/delete-record.json" "domain-name=$zone&record-id=$record_id" | ||||
|     if ! _contains "$response" "\"status\":\"Success\""; then | ||||
|       _err "The TXT record for $1 cannot be deleted." | ||||
|       return 1 | ||||
|     fi | ||||
|     _info "Deleted." | ||||
|   fi | ||||
|   return 0 | ||||
| } | ||||
| 
 | ||||
| ####################  Private functions below ################################## | ||||
| _dns_cloudns_init_check() { | ||||
|   if [ ! -z "$CLOUDNS_INIT_CHECK_COMPLETED" ]; then | ||||
|     return 0 | ||||
|   fi | ||||
| 
 | ||||
|   CLOUDNS_AUTH_ID="${CLOUDNS_AUTH_ID:-$(_readaccountconf_mutable CLOUDNS_AUTH_ID)}" | ||||
|   CLOUDNS_AUTH_PASSWORD="${CLOUDNS_AUTH_PASSWORD:-$(_readaccountconf_mutable CLOUDNS_AUTH_PASSWORD)}" | ||||
|   if [ -z "$CLOUDNS_AUTH_ID" ] || [ -z "$CLOUDNS_AUTH_PASSWORD" ]; then | ||||
|     CLOUDNS_AUTH_ID="" | ||||
|     CLOUDNS_AUTH_PASSWORD="" | ||||
|     _err "You don't specify cloudns api id and password yet." | ||||
|     _err "Please create you id and password and try again." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   if [ -z "$CLOUDNS_AUTH_ID" ]; then | ||||
|     _err "CLOUDNS_AUTH_ID is not configured" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   if [ -z "$CLOUDNS_AUTH_PASSWORD" ]; then | ||||
|     _err "CLOUDNS_AUTH_PASSWORD is not configured" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _dns_cloudns_http_api_call "dns/login.json" "" | ||||
| 
 | ||||
|   if ! _contains "$response" "\"status\":\"Success\""; then | ||||
|     _err "Invalid CLOUDNS_AUTH_ID or CLOUDNS_AUTH_PASSWORD. Please check your login credentials." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   #save the api id and password to the account conf file. | ||||
|   _saveaccountconf_mutable CLOUDNS_AUTH_ID "$CLOUDNS_AUTH_ID" | ||||
|   _saveaccountconf_mutable CLOUDNS_AUTH_PASSWORD "$CLOUDNS_AUTH_PASSWORD" | ||||
| 
 | ||||
|   CLOUDNS_INIT_CHECK_COMPLETED=1 | ||||
| 
 | ||||
|   return 0 | ||||
| } | ||||
| 
 | ||||
| _dns_cloudns_get_zone_name() { | ||||
|   i=2 | ||||
|   while true; do | ||||
|     zoneForCheck=$(printf "%s" "$1" | cut -d . -f $i-100) | ||||
| 
 | ||||
|     if [ -z "$zoneForCheck" ]; then | ||||
|       return 1 | ||||
|     fi | ||||
| 
 | ||||
|     _debug zoneForCheck "$zoneForCheck" | ||||
| 
 | ||||
|     _dns_cloudns_http_api_call "dns/get-zone-info.json" "domain-name=$zoneForCheck" | ||||
| 
 | ||||
|     if ! _contains "$response" "\"status\":\"Failed\""; then | ||||
|       echo "$zoneForCheck" | ||||
|       return 0 | ||||
|     fi | ||||
| 
 | ||||
|     i=$(_math "$i" + 1) | ||||
|   done | ||||
|   return 1 | ||||
| } | ||||
| 
 | ||||
| _dns_cloudns_get_record_id() { | ||||
|   _dns_cloudns_http_api_call "dns/records.json" "domain-name=$1&host=$2&type=TXT" | ||||
|   if _contains "$response" "\"id\":"; then | ||||
|     echo "$response" | cut -d '"' -f 2 | ||||
|     return 0 | ||||
|   fi | ||||
|   return 1 | ||||
| } | ||||
| 
 | ||||
| _dns_cloudns_http_api_call() { | ||||
|   method=$1 | ||||
| 
 | ||||
|   _debug CLOUDNS_AUTH_ID "$CLOUDNS_AUTH_ID" | ||||
|   _debug CLOUDNS_AUTH_PASSWORD "$CLOUDNS_AUTH_PASSWORD" | ||||
| 
 | ||||
|   if [ -z "$2" ]; then | ||||
|     data="auth-id=$CLOUDNS_AUTH_ID&auth-password=$CLOUDNS_AUTH_PASSWORD" | ||||
|   else | ||||
|     data="auth-id=$CLOUDNS_AUTH_ID&auth-password=$CLOUDNS_AUTH_PASSWORD&$2" | ||||
|   fi | ||||
| 
 | ||||
|   response="$(_get "$CLOUDNS_API/$method?$data")" | ||||
| 
 | ||||
|   _debug2 response "$response" | ||||
| 
 | ||||
|   return 0 | ||||
| } | ||||
| @ -58,7 +58,15 @@ dns_cx_add() { | ||||
| #fulldomain | ||||
| dns_cx_rm() { | ||||
|   fulldomain=$1 | ||||
| 
 | ||||
|   REST_API="$CX_Api" | ||||
|   if _get_root "$fulldomain"; then | ||||
|     record_id="" | ||||
|     existing_records "$_domain" "$_sub_domain" | ||||
|     if ! [ "$record_id" = "" ]; then | ||||
|       _rest DELETE "record/$record_id/$_domain_id" "{}" | ||||
|       _info "Deleted record ${fulldomain}" | ||||
|     fi | ||||
|   fi | ||||
| } | ||||
| 
 | ||||
| #usage:  root  sub | ||||
| @ -69,12 +77,12 @@ existing_records() { | ||||
|   _debug "Getting txt records" | ||||
|   root=$1 | ||||
|   sub=$2 | ||||
| 
 | ||||
|   count=0 | ||||
|   if ! _rest GET "record/$_domain_id?:domain_id?host_id=0&offset=0&row_num=100"; then | ||||
|     return 1 | ||||
|   fi | ||||
|   count=0 | ||||
|   seg=$(printf "%s\n" "$response" | _egrep_o "{[^\{]*host\":\"$_sub_domain\"[^\}]*\}") | ||||
| 
 | ||||
|   seg=$(printf "%s\n" "$response" | _egrep_o '"record_id":[^{]*host":"'"$_sub_domain"'"[^}]*\}') | ||||
|   _debug seg "$seg" | ||||
|   if [ -z "$seg" ]; then | ||||
|     return 0 | ||||
| @ -82,7 +90,7 @@ existing_records() { | ||||
| 
 | ||||
|   if printf "%s" "$response" | grep '"type":"TXT"' >/dev/null; then | ||||
|     count=1 | ||||
|     record_id=$(printf "%s\n" "$seg" | _egrep_o "\"record_id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \") | ||||
|     record_id=$(printf "%s\n" "$seg" | _egrep_o '"record_id":"[^"]*"' | cut -d : -f 2 | tr -d \" | _head_n 1) | ||||
|     _debug record_id "$record_id" | ||||
|     return 0 | ||||
|   fi | ||||
| @ -123,7 +131,7 @@ update_record() { | ||||
|   return 1 | ||||
| } | ||||
| 
 | ||||
| ####################  Private functions bellow ################################## | ||||
| ####################  Private functions below ################################## | ||||
| #_acme-challenge.www.domain.com | ||||
| #returns | ||||
| # _sub_domain=_acme-challenge.www | ||||
| @ -147,9 +155,9 @@ _get_root() { | ||||
|     fi | ||||
| 
 | ||||
|     if _contains "$response" "$h."; then | ||||
|       seg=$(printf "%s" "$response" | _egrep_o "\{[^\{]*\"$h\.\"[^\}]*\}") | ||||
|       seg=$(printf "%s\n" "$response" | _egrep_o '"id":[^{]*"'"$h"'."[^}]*}') | ||||
|       _debug seg "$seg" | ||||
|       _domain_id=$(printf "%s" "$seg" | _egrep_o "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \") | ||||
|       _domain_id=$(printf "%s\n" "$seg" | _egrep_o "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \") | ||||
|       _debug _domain_id "$_domain_id" | ||||
|       if [ "$_domain_id" ]; then | ||||
|         _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) | ||||
| @ -170,7 +178,7 @@ _get_root() { | ||||
| _rest() { | ||||
|   m=$1 | ||||
|   ep="$2" | ||||
|   _debug "$ep" | ||||
|   _debug ep "$ep" | ||||
|   url="$REST_API/$ep" | ||||
|   _debug url "$url" | ||||
| 
 | ||||
| @ -185,10 +193,10 @@ _rest() { | ||||
|   hmac=$(printf "%s" "$sec" | _digest md5 hex) | ||||
|   _debug hmac "$hmac" | ||||
| 
 | ||||
|   _H1="API-KEY: $CX_Key" | ||||
|   _H2="API-REQUEST-DATE: $cdate" | ||||
|   _H3="API-HMAC: $hmac" | ||||
|   _H4="Content-Type: application/json" | ||||
|   export _H1="API-KEY: $CX_Key" | ||||
|   export _H2="API-REQUEST-DATE: $cdate" | ||||
|   export _H3="API-HMAC: $hmac" | ||||
|   export _H4="Content-Type: application/json" | ||||
| 
 | ||||
|   if [ "$data" ]; then | ||||
|     response="$(_post "$data" "$url" "" "$m")" | ||||
| @ -201,8 +209,7 @@ _rest() { | ||||
|     return 1 | ||||
|   fi | ||||
|   _debug2 response "$response" | ||||
|   if ! _contains "$response" '"message":"success"'; then | ||||
|     return 1 | ||||
|   fi | ||||
|   return 0 | ||||
| 
 | ||||
|   _contains "$response" '"code":1' | ||||
| 
 | ||||
| } | ||||
|  | ||||
							
								
								
									
										328
									
								
								dnsapi/dns_cyon.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										328
									
								
								dnsapi/dns_cyon.sh
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,328 @@ | ||||
| #!/usr/bin/env sh | ||||
| 
 | ||||
| ######## | ||||
| # Custom cyon.ch DNS API for use with [acme.sh](https://github.com/Neilpang/acme.sh) | ||||
| # | ||||
| # Usage: acme.sh --issue --dns dns_cyon -d www.domain.com | ||||
| # | ||||
| # Dependencies: | ||||
| # ------------- | ||||
| # - oathtool (When using 2 Factor Authentication) | ||||
| # | ||||
| # Issues: | ||||
| # ------- | ||||
| # Any issues / questions / suggestions can be posted here: | ||||
| # https://github.com/noplanman/cyon-api/issues | ||||
| # | ||||
| # Author: Armando Lüscher <armando@noplanman.ch> | ||||
| ######## | ||||
| 
 | ||||
| dns_cyon_add() { | ||||
|   _cyon_load_credentials \ | ||||
|     && _cyon_load_parameters "$@" \ | ||||
|     && _cyon_print_header "add" \ | ||||
|     && _cyon_login \ | ||||
|     && _cyon_change_domain_env \ | ||||
|     && _cyon_add_txt \ | ||||
|     && _cyon_logout | ||||
| } | ||||
| 
 | ||||
| dns_cyon_rm() { | ||||
|   _cyon_load_credentials \ | ||||
|     && _cyon_load_parameters "$@" \ | ||||
|     && _cyon_print_header "delete" \ | ||||
|     && _cyon_login \ | ||||
|     && _cyon_change_domain_env \ | ||||
|     && _cyon_delete_txt \ | ||||
|     && _cyon_logout | ||||
| } | ||||
| 
 | ||||
| ######################### | ||||
| ### PRIVATE FUNCTIONS ### | ||||
| ######################### | ||||
| 
 | ||||
| _cyon_load_credentials() { | ||||
|   # Convert loaded password to/from base64 as needed. | ||||
|   if [ "${CY_Password_B64}" ]; then | ||||
|     CY_Password="$(printf "%s" "${CY_Password_B64}" | _dbase64 "multiline")" | ||||
|   elif [ "${CY_Password}" ]; then | ||||
|     CY_Password_B64="$(printf "%s" "${CY_Password}" | _base64)" | ||||
|   fi | ||||
| 
 | ||||
|   if [ -z "${CY_Username}" ] || [ -z "${CY_Password}" ]; then | ||||
|     # Dummy entries to satisfy script checker. | ||||
|     CY_Username="" | ||||
|     CY_Password="" | ||||
|     CY_OTP_Secret="" | ||||
| 
 | ||||
|     _err "" | ||||
|     _err "You haven't set your cyon.ch login credentials yet." | ||||
|     _err "Please set the required cyon environment variables." | ||||
|     _err "" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   # Save the login credentials to the account.conf file. | ||||
|   _debug "Save credentials to account.conf" | ||||
|   _saveaccountconf CY_Username "${CY_Username}" | ||||
|   _saveaccountconf CY_Password_B64 "$CY_Password_B64" | ||||
|   if [ ! -z "${CY_OTP_Secret}" ]; then | ||||
|     _saveaccountconf CY_OTP_Secret "$CY_OTP_Secret" | ||||
|   else | ||||
|     _clearaccountconf CY_OTP_Secret | ||||
|   fi | ||||
| } | ||||
| 
 | ||||
| _cyon_is_idn() { | ||||
|   _idn_temp="$(printf "%s" "${1}" | tr -d "0-9a-zA-Z.,-_")" | ||||
|   _idn_temp2="$(printf "%s" "${1}" | grep -o "xn--")" | ||||
|   [ "$_idn_temp" ] || [ "$_idn_temp2" ] | ||||
| } | ||||
| 
 | ||||
| _cyon_load_parameters() { | ||||
|   # Read the required parameters to add the TXT entry. | ||||
|   # shellcheck disable=SC2018,SC2019 | ||||
|   fulldomain="$(printf "%s" "${1}" | tr "A-Z" "a-z")" | ||||
|   fulldomain_idn="${fulldomain}" | ||||
| 
 | ||||
|   # Special case for IDNs, as cyon needs a domain environment change, | ||||
|   # which uses the "pretty" instead of the punycode version. | ||||
|   if _cyon_is_idn "${fulldomain}"; then | ||||
|     if ! _exists idn; then | ||||
|       _err "Please install idn to process IDN names." | ||||
|       _err "" | ||||
|       return 1 | ||||
|     fi | ||||
| 
 | ||||
|     fulldomain="$(idn -u "${fulldomain}")" | ||||
|     fulldomain_idn="$(idn -a "${fulldomain}")" | ||||
|   fi | ||||
| 
 | ||||
|   _debug fulldomain "${fulldomain}" | ||||
|   _debug fulldomain_idn "${fulldomain_idn}" | ||||
| 
 | ||||
|   txtvalue="${2}" | ||||
|   _debug txtvalue "${txtvalue}" | ||||
| 
 | ||||
|   # This header is required for curl calls. | ||||
|   _H1="X-Requested-With: XMLHttpRequest" | ||||
|   export _H1 | ||||
| } | ||||
| 
 | ||||
| _cyon_print_header() { | ||||
|   if [ "${1}" = "add" ]; then | ||||
|     _info "" | ||||
|     _info "+---------------------------------------------+" | ||||
|     _info "| Adding DNS TXT entry to your cyon.ch domain |" | ||||
|     _info "+---------------------------------------------+" | ||||
|     _info "" | ||||
|     _info "  * Full Domain: ${fulldomain}" | ||||
|     _info "  * TXT Value:   ${txtvalue}" | ||||
|     _info "" | ||||
|   elif [ "${1}" = "delete" ]; then | ||||
|     _info "" | ||||
|     _info "+-------------------------------------------------+" | ||||
|     _info "| Deleting DNS TXT entry from your cyon.ch domain |" | ||||
|     _info "+-------------------------------------------------+" | ||||
|     _info "" | ||||
|     _info "  * Full Domain: ${fulldomain}" | ||||
|     _info "" | ||||
|   fi | ||||
| } | ||||
| 
 | ||||
| _cyon_get_cookie_header() { | ||||
|   printf "Cookie: %s" "$(grep "cyon=" "$HTTP_HEADER" | grep "^Set-Cookie:" | _tail_n 1 | _egrep_o 'cyon=[^;]*;' | tr -d ';')" | ||||
| } | ||||
| 
 | ||||
| _cyon_login() { | ||||
|   _info "  - Logging in..." | ||||
| 
 | ||||
|   username_encoded="$(printf "%s" "${CY_Username}" | _url_encode)" | ||||
|   password_encoded="$(printf "%s" "${CY_Password}" | _url_encode)" | ||||
| 
 | ||||
|   login_url="https://my.cyon.ch/auth/index/dologin-async" | ||||
|   login_data="$(printf "%s" "username=${username_encoded}&password=${password_encoded}&pathname=%2F")" | ||||
| 
 | ||||
|   login_response="$(_post "$login_data" "$login_url")" | ||||
|   _debug login_response "${login_response}" | ||||
| 
 | ||||
|   # Bail if login fails. | ||||
|   if [ "$(printf "%s" "${login_response}" | _cyon_get_response_success)" != "success" ]; then | ||||
|     _err "    $(printf "%s" "${login_response}" | _cyon_get_response_message)" | ||||
|     _err "" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _info "    success" | ||||
| 
 | ||||
|   # NECESSARY!! Load the main page after login, to get the new cookie. | ||||
|   _H2="$(_cyon_get_cookie_header)" | ||||
|   export _H2 | ||||
| 
 | ||||
|   _get "https://my.cyon.ch/" >/dev/null | ||||
| 
 | ||||
|   # 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 | ||||
|     _info "  - Authorising with OTP code..." | ||||
| 
 | ||||
|     if ! _exists oathtool; then | ||||
|       _err "Please install oathtool to use 2 Factor Authentication." | ||||
|       _err "" | ||||
|       return 1 | ||||
|     fi | ||||
| 
 | ||||
|     # Get OTP code with the defined secret. | ||||
|     otp_code="$(oathtool --base32 --totp "${CY_OTP_Secret}" 2>/dev/null)" | ||||
| 
 | ||||
|     login_otp_url="https://my.cyon.ch/auth/multi-factor/domultifactorauth-async" | ||||
|     login_otp_data="totpcode=${otp_code}&pathname=%2F&rememberme=0" | ||||
| 
 | ||||
|     login_otp_response="$(_post "$login_otp_data" "$login_otp_url")" | ||||
|     _debug login_otp_response "${login_otp_response}" | ||||
| 
 | ||||
|     # Bail if OTP authentication fails. | ||||
|     if [ "$(printf "%s" "${login_otp_response}" | _cyon_get_response_success)" != "success" ]; then | ||||
|       _err "    $(printf "%s" "${login_otp_response}" | _cyon_get_response_message)" | ||||
|       _err "" | ||||
|       return 1 | ||||
|     fi | ||||
| 
 | ||||
|     _info "    success" | ||||
|   fi | ||||
| 
 | ||||
|   _info "" | ||||
| } | ||||
| 
 | ||||
| _cyon_logout() { | ||||
|   _info "  - Logging out..." | ||||
| 
 | ||||
|   _get "https://my.cyon.ch/auth/index/dologout" >/dev/null | ||||
| 
 | ||||
|   _info "    success" | ||||
|   _info "" | ||||
| } | ||||
| 
 | ||||
| _cyon_change_domain_env() { | ||||
|   _info "  - Changing domain environment..." | ||||
| 
 | ||||
|   # Get the "example.com" part of the full domain name. | ||||
|   domain_env="$(printf "%s" "${fulldomain}" | sed -E -e 's/.*\.(.*\..*)$/\1/')" | ||||
|   _debug "Changing domain environment to ${domain_env}" | ||||
| 
 | ||||
|   gloo_item_key="$(_get "https://my.cyon.ch/domain/" | tr '\n' ' ' | sed -E -e "s/.*data-domain=\"${domain_env}\"[^<]*data-itemkey=\"([^\"]*).*/\1/")" | ||||
|   _debug gloo_item_key "${gloo_item_key}" | ||||
| 
 | ||||
|   domain_env_url="https://my.cyon.ch/user/environment/setdomain/d/${domain_env}/gik/${gloo_item_key}" | ||||
| 
 | ||||
|   domain_env_response="$(_get "${domain_env_url}")" | ||||
|   _debug domain_env_response "${domain_env_response}" | ||||
| 
 | ||||
|   if ! _cyon_check_if_2fa_missed "${domain_env_response}"; then return 1; fi | ||||
| 
 | ||||
|   domain_env_success="$(printf "%s" "${domain_env_response}" | _egrep_o '"authenticated":\w*' | cut -d : -f 2)" | ||||
| 
 | ||||
|   # Bail if domain environment change fails. | ||||
|   if [ "${domain_env_success}" != "true" ]; then | ||||
|     _err "    $(printf "%s" "${domain_env_response}" | _cyon_get_response_message)" | ||||
|     _err "" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _info "    success" | ||||
|   _info "" | ||||
| } | ||||
| 
 | ||||
| _cyon_add_txt() { | ||||
|   _info "  - Adding DNS TXT entry..." | ||||
| 
 | ||||
|   add_txt_url="https://my.cyon.ch/domain/dnseditor/add-record-async" | ||||
|   add_txt_data="zone=${fulldomain_idn}.&ttl=900&type=TXT&value=${txtvalue}" | ||||
| 
 | ||||
|   add_txt_response="$(_post "$add_txt_data" "$add_txt_url")" | ||||
|   _debug add_txt_response "${add_txt_response}" | ||||
| 
 | ||||
|   if ! _cyon_check_if_2fa_missed "${add_txt_response}"; then return 1; fi | ||||
| 
 | ||||
|   add_txt_message="$(printf "%s" "${add_txt_response}" | _cyon_get_response_message)" | ||||
|   add_txt_status="$(printf "%s" "${add_txt_response}" | _cyon_get_response_status)" | ||||
| 
 | ||||
|   # Bail if adding TXT entry fails. | ||||
|   if [ "${add_txt_status}" != "true" ]; then | ||||
|     _err "    ${add_txt_message}" | ||||
|     _err "" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _info "    success (TXT|${fulldomain_idn}.|${txtvalue})" | ||||
|   _info "" | ||||
| } | ||||
| 
 | ||||
| _cyon_delete_txt() { | ||||
|   _info "  - Deleting DNS TXT entry..." | ||||
| 
 | ||||
|   list_txt_url="https://my.cyon.ch/domain/dnseditor/list-async" | ||||
| 
 | ||||
|   list_txt_response="$(_get "${list_txt_url}" | sed -e 's/data-hash/\\ndata-hash/g')" | ||||
|   _debug list_txt_response "${list_txt_response}" | ||||
| 
 | ||||
|   if ! _cyon_check_if_2fa_missed "${list_txt_response}"; then return 1; fi | ||||
| 
 | ||||
|   # Find and delete all acme challenge entries for the $fulldomain. | ||||
|   _dns_entries="$(printf "%b\n" "${list_txt_response}" | sed -n 's/data-hash=\\"\([^"]*\)\\" data-identifier=\\"\([^"]*\)\\".*/\1 \2/p')" | ||||
| 
 | ||||
|   printf "%s" "${_dns_entries}" | while read -r _hash _identifier; do | ||||
|     dns_type="$(printf "%s" "$_identifier" | cut -d'|' -f1)" | ||||
|     dns_domain="$(printf "%s" "$_identifier" | cut -d'|' -f2)" | ||||
| 
 | ||||
|     if [ "${dns_type}" != "TXT" ] || [ "${dns_domain}" != "${fulldomain_idn}." ]; then | ||||
|       continue | ||||
|     fi | ||||
| 
 | ||||
|     hash_encoded="$(printf "%s" "${_hash}" | _url_encode)" | ||||
|     identifier_encoded="$(printf "%s" "${_identifier}" | _url_encode)" | ||||
| 
 | ||||
|     delete_txt_url="https://my.cyon.ch/domain/dnseditor/delete-record-async" | ||||
|     delete_txt_data="$(printf "%s" "hash=${hash_encoded}&identifier=${identifier_encoded}")" | ||||
| 
 | ||||
|     delete_txt_response="$(_post "$delete_txt_data" "$delete_txt_url")" | ||||
|     _debug delete_txt_response "${delete_txt_response}" | ||||
| 
 | ||||
|     if ! _cyon_check_if_2fa_missed "${delete_txt_response}"; then return 1; fi | ||||
| 
 | ||||
|     delete_txt_message="$(printf "%s" "${delete_txt_response}" | _cyon_get_response_message)" | ||||
|     delete_txt_status="$(printf "%s" "${delete_txt_response}" | _cyon_get_response_status)" | ||||
| 
 | ||||
|     # Skip if deleting TXT entry fails. | ||||
|     if [ "${delete_txt_status}" != "true" ]; then | ||||
|       _err "    ${delete_txt_message} (${_identifier})" | ||||
|     else | ||||
|       _info "    success (${_identifier})" | ||||
|     fi | ||||
|   done | ||||
| 
 | ||||
|   _info "    done" | ||||
|   _info "" | ||||
| } | ||||
| 
 | ||||
| _cyon_get_response_message() { | ||||
|   _egrep_o '"message":"[^"]*"' | cut -d : -f 2 | tr -d '"' | ||||
| } | ||||
| 
 | ||||
| _cyon_get_response_status() { | ||||
|   _egrep_o '"status":\w*' | cut -d : -f 2 | ||||
| } | ||||
| 
 | ||||
| _cyon_get_response_success() { | ||||
|   _egrep_o '"onSuccess":"[^"]*"' | cut -d : -f 2 | tr -d '"' | ||||
| } | ||||
| 
 | ||||
| _cyon_check_if_2fa_missed() { | ||||
|   # Did we miss the 2FA? | ||||
|   if test "${1#*multi_factor_form}" != "${1}"; then | ||||
|     _err "    Missed OTP authentication!" | ||||
|     _err "" | ||||
|     return 1 | ||||
|   fi | ||||
| } | ||||
							
								
								
									
										205
									
								
								dnsapi/dns_dgon.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										205
									
								
								dnsapi/dns_dgon.sh
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,205 @@ | ||||
| #!/usr/bin/env sh | ||||
| 
 | ||||
| ## Will be called by acme.sh to add the txt record to your api system. | ||||
| ## returns 0 means success, otherwise error. | ||||
| 
 | ||||
| ## Author: thewer <github at thewer.com> | ||||
| ## GitHub: https://github.com/gitwer/acme.sh | ||||
| 
 | ||||
| ## | ||||
| ## Environment Variables Required: | ||||
| ## | ||||
| ## DO_API_KEY="75310dc4ca779ac39a19f6355db573b49ce92ae126553ebd61ac3a3ae34834cc" | ||||
| ## | ||||
| 
 | ||||
| #####################  Public functions  ##################### | ||||
| 
 | ||||
| ## Create the text record for validation. | ||||
| ## Usage: fulldomain txtvalue | ||||
| ## EG: "_acme-challenge.www.other.domain.com" "XKrxpRBosdq0HG9i01zxXp5CPBs" | ||||
| dns_dgon_add() { | ||||
|   fulldomain="$(echo "$1" | _lower_case)" | ||||
|   txtvalue=$2 | ||||
|   _info "Using digitalocean dns validation - add record" | ||||
|   _debug fulldomain "$fulldomain" | ||||
|   _debug txtvalue "$txtvalue" | ||||
| 
 | ||||
|   ## save the env vars (key and domain split location) for later automated use | ||||
|   _saveaccountconf DO_API_KEY "$DO_API_KEY" | ||||
| 
 | ||||
|   ## split the domain for DO API | ||||
|   if ! _get_base_domain "$fulldomain"; then | ||||
|     _err "domain not found in your account for addition" | ||||
|     return 1 | ||||
|   fi | ||||
|   _debug _sub_domain "$_sub_domain" | ||||
|   _debug _domain "$_domain" | ||||
| 
 | ||||
|   ## Set the header with our post type and key auth key | ||||
|   export _H1="Content-Type: application/json" | ||||
|   export _H2="Authorization: Bearer $DO_API_KEY" | ||||
|   PURL='https://api.digitalocean.com/v2/domains/'$_domain'/records' | ||||
|   PBODY='{"type":"TXT","name":"'$_sub_domain'","data":"'$txtvalue'"}' | ||||
| 
 | ||||
|   _debug PURL "$PURL" | ||||
|   _debug PBODY "$PBODY" | ||||
| 
 | ||||
|   ## the create request - post | ||||
|   ## args: BODY, URL, [need64, httpmethod] | ||||
|   response="$(_post "$PBODY" "$PURL")" | ||||
| 
 | ||||
|   ## check response | ||||
|   if [ "$?" != "0" ]; then | ||||
|     _err "error in response: $response" | ||||
|     return 1 | ||||
|   fi | ||||
|   _debug2 response "$response" | ||||
| 
 | ||||
|   ## finished correctly | ||||
|   return 0 | ||||
| } | ||||
| 
 | ||||
| ## Remove the txt record after validation. | ||||
| ## Usage: fulldomain txtvalue | ||||
| ## EG: "_acme-challenge.www.other.domain.com" "XKrxpRBosdq0HG9i01zxXp5CPBs" | ||||
| dns_dgon_rm() { | ||||
|   fulldomain="$(echo "$1" | _lower_case)" | ||||
|   txtvalue=$2 | ||||
|   _info "Using digitalocean dns validation - remove record" | ||||
|   _debug fulldomain "$fulldomain" | ||||
|   _debug txtvalue "$txtvalue" | ||||
| 
 | ||||
|   ## split the domain for DO API | ||||
|   if ! _get_base_domain "$fulldomain"; then | ||||
|     _err "domain not found in your account for removal" | ||||
|     return 1 | ||||
|   fi | ||||
|   _debug _sub_domain "$_sub_domain" | ||||
|   _debug _domain "$_domain" | ||||
| 
 | ||||
|   ## Set the header with our post type and key auth key | ||||
|   export _H1="Content-Type: application/json" | ||||
|   export _H2="Authorization: Bearer $DO_API_KEY" | ||||
|   ## get URL for the list of domains | ||||
|   ## may get: "links":{"pages":{"last":".../v2/domains/DOM/records?page=2","next":".../v2/domains/DOM/records?page=2"}} | ||||
|   GURL="https://api.digitalocean.com/v2/domains/$_domain/records" | ||||
| 
 | ||||
|   ## while we dont have a record ID we keep going | ||||
|   while [ -z "$record" ]; do | ||||
|     ## 1) get the URL | ||||
|     ## the create request - get | ||||
|     ## args: URL, [onlyheader, timeout] | ||||
|     domain_list="$(_get "$GURL")" | ||||
|     ## 2) find record | ||||
|     ## check for what we are looing for: "type":"A","name":"$_sub_domain" | ||||
|     record="$(echo "$domain_list" | _egrep_o "\"id\"\s*\:\s*\"*\d+\"*[^}]*\"name\"\s*\:\s*\"$_sub_domain\"[^}]*\"data\"\s*\:\s*\"$txtvalue\"")" | ||||
|     ## 3) check record and get next page | ||||
|     if [ -z "$record" ]; then | ||||
|       ## find the next page if we dont have a match | ||||
|       nextpage="$(echo "$domain_list" | _egrep_o "\"links\".*" | _egrep_o "\"next\".*" | _egrep_o "http.*page\=\d+")" | ||||
|       if [ -z "$nextpage" ]; then | ||||
|         _err "no record and no nextpage in digital ocean DNS removal" | ||||
|         return 1 | ||||
|       fi | ||||
|       _debug2 nextpage "$nextpage" | ||||
|       GURL="$nextpage" | ||||
|     fi | ||||
|     ## we break out of the loop when we have a record | ||||
|   done | ||||
| 
 | ||||
|   ## we found the record | ||||
|   rec_id="$(echo "$record" | _egrep_o "id\"\s*\:\s*\"*\d+" | _egrep_o "\d+")" | ||||
|   _debug rec_id "$rec_id" | ||||
| 
 | ||||
|   ## delete the record | ||||
|   ## delete URL for removing the one we dont want | ||||
|   DURL="https://api.digitalocean.com/v2/domains/$_domain/records/$rec_id" | ||||
| 
 | ||||
|   ## the create request - delete | ||||
|   ## args: BODY, URL, [need64, httpmethod] | ||||
|   response="$(_post "" "$DURL" "" "DELETE")" | ||||
| 
 | ||||
|   ## check response (sort of) | ||||
|   if [ "$?" != "0" ]; then | ||||
|     _err "error in remove response: $response" | ||||
|     return 1 | ||||
|   fi | ||||
|   _debug2 response "$response" | ||||
| 
 | ||||
|   ## finished correctly | ||||
|   return 0 | ||||
| } | ||||
| 
 | ||||
| #####################  Private functions below  ##################### | ||||
| 
 | ||||
| ## Split the domain provided into the "bade domain" and the "start prefix". | ||||
| ## This function searches for the longest subdomain in your account | ||||
| ## for the full domain given and splits it into the base domain (zone) | ||||
| ## and the prefix/record to be added/removed | ||||
| ## USAGE: fulldomain | ||||
| ## EG: "_acme-challenge.two.three.four.domain.com" | ||||
| ## returns | ||||
| ## _sub_domain="_acme-challenge.two" | ||||
| ## _domain="three.four.domain.com" *IF* zone "three.four.domain.com" exists | ||||
| ## if only "domain.com" exists it will return | ||||
| ## _sub_domain="_acme-challenge.two.three.four" | ||||
| ## _domain="domain.com" | ||||
| _get_base_domain() { | ||||
|   # args | ||||
|   fulldomain="$(echo "$1" | tr '[:upper:]' '[:lower:]')" | ||||
|   _debug fulldomain "$fulldomain" | ||||
| 
 | ||||
|   # domain max legal length = 253 | ||||
|   MAX_DOM=255 | ||||
| 
 | ||||
|   ## get a list of domains for the account to check thru | ||||
|   ## Set the headers | ||||
|   export _H1="Content-Type: application/json" | ||||
|   export _H2="Authorization: Bearer $DO_API_KEY" | ||||
|   _debug DO_API_KEY "$DO_API_KEY" | ||||
|   ## get URL for the list of domains | ||||
|   ## havent seen this request paginated, tested with 18 domains (more requires manual requests with DO) | ||||
|   DOMURL="https://api.digitalocean.com/v2/domains" | ||||
| 
 | ||||
|   ## get the domain list (DO gives basically a full XFER!) | ||||
|   domain_list="$(_get "$DOMURL")" | ||||
| 
 | ||||
|   ## check response | ||||
|   if [ "$?" != "0" ]; then | ||||
|     _err "error in domain_list response: $domain_list" | ||||
|     return 1 | ||||
|   fi | ||||
|   _debug2 domain_list "$domain_list" | ||||
| 
 | ||||
|   ## for each shortening of our $fulldomain, check if it exists in the $domain_list | ||||
|   ## can never start on 1 (aka whole $fulldomain) as $fulldomain starts with "_acme-challenge" | ||||
|   i=2 | ||||
|   while [ $i -gt 0 ]; do | ||||
|     ## get next longest domain | ||||
|     _domain=$(printf "%s" "$fulldomain" | cut -d . -f "$i"-"$MAX_DOM") | ||||
|     ## check we got something back from our cut (or are we at the end) | ||||
|     if [ -z "$_domain" ]; then | ||||
|       ## we got to the end of the domain - invalid domain | ||||
|       _err "domain not found in DigitalOcean account" | ||||
|       return 1 | ||||
|     fi | ||||
|     ## 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 | ||||
|       ## exists - exit loop returning the parts | ||||
|       sub_point=$(_math $i - 1) | ||||
|       _sub_domain=$(printf "%s" "$fulldomain" | cut -d . -f 1-"$sub_point") | ||||
|       _debug _domain "$_domain" | ||||
|       _debug _sub_domain "$_sub_domain" | ||||
|       return 0 | ||||
|     fi | ||||
|     ## increment cut point $i | ||||
|     i=$(_math $i + 1) | ||||
|   done | ||||
| 
 | ||||
|   ## we went through the entire domain zone list and dint find one that matched | ||||
|   ## doesnt look like we can add in the record | ||||
|   _err "domain not found in DigitalOcean account, but we should never get here" | ||||
|   return 1 | ||||
| } | ||||
							
								
								
									
										215
									
								
								dnsapi/dns_dnsimple.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										215
									
								
								dnsapi/dns_dnsimple.sh
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,215 @@ | ||||
| #!/usr/bin/env sh | ||||
| 
 | ||||
| # DNSimple domain api | ||||
| # https://github.com/pho3nixf1re/acme.sh/issues | ||||
| # | ||||
| # This is your oauth token which can be acquired on the account page. Please | ||||
| # note that this must be an _account_ token and not a _user_ token. | ||||
| # https://dnsimple.com/a/<your account id>/account/access_tokens | ||||
| # DNSimple_OAUTH_TOKEN="sdfsdfsdfljlbjkljlkjsdfoiwje" | ||||
| 
 | ||||
| DNSimple_API="https://api.dnsimple.com/v2" | ||||
| 
 | ||||
| ########  Public functions ##################### | ||||
| 
 | ||||
| # Usage: add  _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" | ||||
| dns_dnsimple_add() { | ||||
|   fulldomain=$1 | ||||
|   txtvalue=$2 | ||||
| 
 | ||||
|   if [ -z "$DNSimple_OAUTH_TOKEN" ]; then | ||||
|     DNSimple_OAUTH_TOKEN="" | ||||
|     _err "You have not set the dnsimple oauth token yet." | ||||
|     _err "Please visit https://dnsimple.com/user to generate it." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   # save the oauth token for later | ||||
|   _saveaccountconf DNSimple_OAUTH_TOKEN "$DNSimple_OAUTH_TOKEN" | ||||
| 
 | ||||
|   if ! _get_account_id; then | ||||
|     _err "failed to retrive account id" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   if ! _get_root "$fulldomain"; then | ||||
|     _err "invalid domain" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _get_records "$_account_id" "$_domain" "$_sub_domain" | ||||
| 
 | ||||
|   if [ "$_records_count" = "0" ]; then | ||||
|     _info "Adding record" | ||||
|     if _dnsimple_rest POST "$_account_id/zones/$_domain/records" "{\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"content\":\"$txtvalue\",\"ttl\":120}"; then | ||||
|       if printf -- "%s" "$response" | grep "\"name\":\"$_sub_domain\"" >/dev/null; then | ||||
|         _info "Added" | ||||
|         return 0 | ||||
|       else | ||||
|         _err "Unexpected response while adding text record." | ||||
|         return 1 | ||||
|       fi | ||||
|     fi | ||||
|     _err "Add txt record error." | ||||
|   else | ||||
|     _info "Updating record" | ||||
|     _extract_record_id "$_records" "$_sub_domain" | ||||
| 
 | ||||
|     if _dnsimple_rest \ | ||||
|       PATCH \ | ||||
|       "$_account_id/zones/$_domain/records/$_record_id" \ | ||||
|       "{\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"content\":\"$txtvalue\",\"ttl\":120}"; then | ||||
| 
 | ||||
|       _info "Updated!" | ||||
|       return 0 | ||||
|     fi | ||||
| 
 | ||||
|     _err "Update error" | ||||
|     return 1 | ||||
|   fi | ||||
| } | ||||
| 
 | ||||
| # fulldomain | ||||
| dns_dnsimple_rm() { | ||||
|   fulldomain=$1 | ||||
| 
 | ||||
|   if ! _get_account_id; then | ||||
|     _err "failed to retrive account id" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   if ! _get_root "$fulldomain"; then | ||||
|     _err "invalid domain" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _get_records "$_account_id" "$_domain" "$_sub_domain" | ||||
|   _extract_record_id "$_records" "$_sub_domain" | ||||
| 
 | ||||
|   if [ "$_record_id" ]; then | ||||
| 
 | ||||
|     if _dnsimple_rest DELETE "$_account_id/zones/$_domain/records/$_record_id"; then | ||||
|       _info "removed record" "$_record_id" | ||||
|       return 0 | ||||
|     fi | ||||
|   fi | ||||
| 
 | ||||
|   _err "failed to remove record" "$_record_id" | ||||
|   return 1 | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| ####################  Private functions bellow ################################## | ||||
| # _acme-challenge.www.domain.com | ||||
| # returns | ||||
| #   _sub_domain=_acme-challenge.www | ||||
| #   _domain=domain.com | ||||
| _get_root() { | ||||
|   domain=$1 | ||||
|   i=2 | ||||
|   previous=1 | ||||
|   while true; do | ||||
|     h=$(printf "%s" "$domain" | cut -d . -f $i-100) | ||||
|     if [ -z "$h" ]; then | ||||
|       # not valid | ||||
|       return 1 | ||||
|     fi | ||||
| 
 | ||||
|     if ! _dnsimple_rest GET "$_account_id/zones/$h"; then | ||||
|       return 1 | ||||
|     fi | ||||
| 
 | ||||
|     if _contains "$response" 'not found'; then | ||||
|       _debug "$h not found" | ||||
|     else | ||||
|       _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$previous) | ||||
|       _domain="$h" | ||||
| 
 | ||||
|       _debug _domain "$_domain" | ||||
|       _debug _sub_domain "$_sub_domain" | ||||
| 
 | ||||
|       return 0 | ||||
|     fi | ||||
| 
 | ||||
|     previous="$i" | ||||
|     i=$(_math "$i" + 1) | ||||
|   done | ||||
|   return 1 | ||||
| } | ||||
| 
 | ||||
| # returns _account_id | ||||
| _get_account_id() { | ||||
|   _debug "retrive account id" | ||||
|   if ! _dnsimple_rest GET "whoami"; then | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   if _contains "$response" "\"account\":null"; then | ||||
|     _err "no account associated with this token" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   if _contains "$response" "timeout"; then | ||||
|     _err "timeout retrieving account id" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _account_id=$(printf "%s" "$response" | _egrep_o "\"id\":[^,]*,\"email\":" | cut -d: -f2 | cut -d, -f1) | ||||
|   _debug _account_id "$_account_id" | ||||
| 
 | ||||
|   return 0 | ||||
| } | ||||
| 
 | ||||
| # returns | ||||
| #   _records | ||||
| #   _records_count | ||||
| _get_records() { | ||||
|   account_id=$1 | ||||
|   domain=$2 | ||||
|   sub_domain=$3 | ||||
| 
 | ||||
|   _debug "fetching txt records" | ||||
|   _dnsimple_rest GET "$account_id/zones/$domain/records?per_page=100" | ||||
| 
 | ||||
|   if ! _contains "$response" "\"id\":"; then | ||||
|     _err "failed to retrieve records" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _records_count=$(printf "%s" "$response" | _egrep_o "\"name\":\"$sub_domain\"" | wc -l | _egrep_o "[0-9]+") | ||||
|   _records=$response | ||||
|   _debug _records_count "$_records_count" | ||||
| } | ||||
| 
 | ||||
| # returns _record_id | ||||
| _extract_record_id() { | ||||
|   _record_id=$(printf "%s" "$_records" | _egrep_o "\"id\":[^,]*,\"zone_id\":\"[^,]*\",\"parent_id\":null,\"name\":\"$_sub_domain\"" | cut -d: -f2 | cut -d, -f1) | ||||
|   _debug "_record_id" "$_record_id" | ||||
| } | ||||
| 
 | ||||
| # returns response | ||||
| _dnsimple_rest() { | ||||
|   method=$1 | ||||
|   path="$2" | ||||
|   data="$3" | ||||
|   request_url="$DNSimple_API/$path" | ||||
|   _debug "$path" | ||||
| 
 | ||||
|   export _H1="Accept: application/json" | ||||
|   export _H2="Authorization: Bearer $DNSimple_OAUTH_TOKEN" | ||||
| 
 | ||||
|   if [ "$data" ] || [ "$method" = "DELETE" ]; then | ||||
|     _H1="Content-Type: application/json" | ||||
|     _debug data "$data" | ||||
|     response="$(_post "$data" "$request_url" "" "$method")" | ||||
|   else | ||||
|     response="$(_get "$request_url" "" "" "$method")" | ||||
|   fi | ||||
| 
 | ||||
|   if [ "$?" != "0" ]; then | ||||
|     _err "error $request_url" | ||||
|     return 1 | ||||
|   fi | ||||
|   _debug2 response "$response" | ||||
|   return 0 | ||||
| } | ||||
							
								
								
									
										148
									
								
								dnsapi/dns_do.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										148
									
								
								dnsapi/dns_do.sh
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,148 @@ | ||||
| #!/usr/bin/env sh | ||||
| 
 | ||||
| # DNS API for Domain-Offensive / Resellerinterface / Domainrobot | ||||
| 
 | ||||
| # Report bugs at https://github.com/seidler2547/acme.sh/issues | ||||
| 
 | ||||
| # set these environment variables to match your customer ID and password: | ||||
| # DO_PID="KD-1234567" | ||||
| # DO_PW="cdfkjl3n2" | ||||
| 
 | ||||
| DO_URL="https://soap.resellerinterface.de/" | ||||
| 
 | ||||
| ########  Public functions ##################### | ||||
| 
 | ||||
| #Usage: dns_myapi_add   _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" | ||||
| dns_do_add() { | ||||
|   fulldomain=$1 | ||||
|   txtvalue=$2 | ||||
|   if _dns_do_authenticate; then | ||||
|     _info "Adding TXT record to ${_domain} as ${fulldomain}" | ||||
|     _dns_do_soap createRR origin "${_domain}" name "${fulldomain}" type TXT data "${txtvalue}" ttl 300 | ||||
|     if _contains "${response}" '>success<'; then | ||||
|       return 0 | ||||
|     fi | ||||
|     _err "Could not create resource record, check logs" | ||||
|   fi | ||||
|   return 1 | ||||
| } | ||||
| 
 | ||||
| #fulldomain | ||||
| dns_do_rm() { | ||||
|   fulldomain=$1 | ||||
|   if _dns_do_authenticate; then | ||||
|     if _dns_do_list_rrs; then | ||||
|       _dns_do_had_error=0 | ||||
|       for _rrid in ${_rr_list}; do | ||||
|         _info "Deleting resource record $_rrid for $_domain" | ||||
|         _dns_do_soap deleteRR origin "${_domain}" rrid "${_rrid}" | ||||
|         if ! _contains "${response}" '>success<'; then | ||||
|           _dns_do_had_error=1 | ||||
|           _err "Could not delete resource record for ${_domain}, id ${_rrid}" | ||||
|         fi | ||||
|       done | ||||
|       return $_dns_do_had_error | ||||
|     fi | ||||
|   fi | ||||
|   return 1 | ||||
| } | ||||
| 
 | ||||
| ####################  Private functions below ################################## | ||||
| _dns_do_authenticate() { | ||||
|   _info "Authenticating as ${DO_PID}" | ||||
|   _dns_do_soap authPartner partner "${DO_PID}" password "${DO_PW}" | ||||
|   if _contains "${response}" '>success<'; then | ||||
|     _get_root "$fulldomain" | ||||
|     _debug "_domain $_domain" | ||||
|     return 0 | ||||
|   else | ||||
|     _err "Authentication failed, are DO_PID and DO_PW set correctly?" | ||||
|   fi | ||||
|   return 1 | ||||
| } | ||||
| 
 | ||||
| _dns_do_list_rrs() { | ||||
|   _dns_do_soap getRRList origin "${_domain}" | ||||
|   if ! _contains "${response}" 'SOAP-ENC:Array'; then | ||||
|     _err "getRRList origin ${_domain} failed" | ||||
|     return 1 | ||||
|   fi | ||||
|   _rr_list="$(echo "${response}" \ | ||||
|     | tr -d "\n\r\t" \ | ||||
|     | sed -e 's/<item xsi:type="ns2:Map">/\n/g' \ | ||||
|     | grep ">$(_regexcape "$fulldomain")</value>" \ | ||||
|     | sed -e 's/<\/item>/\n/g' \ | ||||
|     | grep '>id</key><value' \ | ||||
|     | _egrep_o '>[0-9]{1,16}<' \ | ||||
|     | tr -d '><')" | ||||
|   [ "${_rr_list}" ] | ||||
| } | ||||
| 
 | ||||
| _dns_do_soap() { | ||||
|   func="$1" | ||||
|   shift | ||||
|   # put the parameters to xml | ||||
|   body="<tns:${func} xmlns:tns=\"${DO_URL}\">" | ||||
|   while [ "$1" ]; do | ||||
|     _k="$1" | ||||
|     shift | ||||
|     _v="$1" | ||||
|     shift | ||||
|     body="$body<$_k>$_v</$_k>" | ||||
|   done | ||||
|   body="$body</tns:${func}>" | ||||
|   _debug2 "SOAP request ${body}" | ||||
| 
 | ||||
|   # build SOAP XML | ||||
|   _xml='<?xml version="1.0" encoding="UTF-8"?> | ||||
| <env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"> | ||||
|   <env:Body>'"$body"'</env:Body> | ||||
| </env:Envelope>' | ||||
| 
 | ||||
|   # set SOAP headers | ||||
|   export _H1="SOAPAction: ${DO_URL}#${func}" | ||||
| 
 | ||||
|   if ! response="$(_post "${_xml}" "${DO_URL}")"; then | ||||
|     _err "Error <$1>" | ||||
|     return 1 | ||||
|   fi | ||||
|   _debug2 "SOAP response $response" | ||||
| 
 | ||||
|   # retrieve cookie header | ||||
|   _H2="$(_egrep_o 'Cookie: [^;]+' <"$HTTP_HEADER" | _head_n 1)" | ||||
|   export _H2 | ||||
| 
 | ||||
|   return 0 | ||||
| } | ||||
| 
 | ||||
| _get_root() { | ||||
|   domain=$1 | ||||
|   i=1 | ||||
| 
 | ||||
|   _dns_do_soap getDomainList | ||||
|   _all_domains="$(echo "${response}" \ | ||||
|     | tr -d "\n\r\t " \ | ||||
|     | _egrep_o 'domain</key><value[^>]+>[^<]+' \ | ||||
|     | sed -e 's/^domain<\/key><value[^>]*>//g')" | ||||
| 
 | ||||
|   while true; do | ||||
|     h=$(printf "%s" "$domain" | cut -d . -f $i-100) | ||||
|     if [ -z "$h" ]; then | ||||
|       return 1 | ||||
|     fi | ||||
| 
 | ||||
|     if _contains "${_all_domains}" "^$(_regexcape "$h")\$"; then | ||||
|       _domain="$h" | ||||
|       return 0 | ||||
|     fi | ||||
| 
 | ||||
|     i=$(_math $i + 1) | ||||
|   done | ||||
|   _debug "$domain not found" | ||||
| 
 | ||||
|   return 1 | ||||
| } | ||||
| 
 | ||||
| _regexcape() { | ||||
|   echo "$1" | sed -e 's/\([]\.$*^[]\)/\\\1/g' | ||||
| } | ||||
| @ -6,9 +6,8 @@ | ||||
| # | ||||
| #DP_Key="sADDsdasdgdsf" | ||||
| 
 | ||||
| DP_Api="https://dnsapi.cn" | ||||
| REST_API="https://dnsapi.cn" | ||||
| 
 | ||||
| #REST_API | ||||
| ########  Public functions ##################### | ||||
| 
 | ||||
| #Usage: add  _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" | ||||
| @ -24,8 +23,6 @@ dns_dp_add() { | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   REST_API="$DP_Api" | ||||
| 
 | ||||
|   #save the api key and email to the account conf file. | ||||
|   _saveaccountconf DP_Id "$DP_Id" | ||||
|   _saveaccountconf DP_Key "$DP_Key" | ||||
| @ -50,9 +47,39 @@ dns_dp_add() { | ||||
|   fi | ||||
| } | ||||
| 
 | ||||
| #fulldomain | ||||
| #fulldomain txtvalue | ||||
| dns_dp_rm() { | ||||
|   fulldomain=$1 | ||||
|   txtvalue=$2 | ||||
|   _debug "First detect the root zone" | ||||
|   if ! _get_root "$fulldomain"; then | ||||
|     _err "invalid domain" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   if ! _rest POST "Record.List" "login_token=$DP_Id,$DP_Key&format=json&domain_id=$_domain_id&sub_domain=$_sub_domain"; then | ||||
|     _err "Record.Lis error." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   if _contains "$response" 'No records'; then | ||||
|     _info "Don't need to remove." | ||||
|     return 0 | ||||
|   fi | ||||
| 
 | ||||
|   record_id=$(echo "$response" | _egrep_o '{[^{]*"value":"'"$txtvalue"'"' | cut -d , -f 1 | cut -d : -f 2 | tr -d \") | ||||
|   _debug record_id "$record_id" | ||||
|   if [ -z "$record_id" ]; then | ||||
|     _err "Can not get record id." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   if ! _rest POST "Record.Remove" "login_token=$DP_Id,$DP_Key&format=json&domain_id=$_domain_id&record_id=$record_id"; then | ||||
|     _err "Record.Remove error." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _contains "$response" "Action completed successful" | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| @ -75,8 +102,9 @@ existing_records() { | ||||
|   fi | ||||
| 
 | ||||
|   if _contains "$response" "Action completed successful"; then | ||||
|     count=$(printf "%s" "$response" | grep '<type>TXT</type>' | wc -l) | ||||
|     count=$(printf "%s" "$response" | grep -c '<type>TXT</type>' | tr -d ' ') | ||||
|     record_id=$(printf "%s" "$response" | grep '^<id>' | tail -1 | cut -d '>' -f 2 | cut -d '<' -f 1) | ||||
|     _debug record_id "$record_id" | ||||
|     return 0 | ||||
|   else | ||||
|     _err "get existing records error." | ||||
| @ -130,7 +158,7 @@ update_record() { | ||||
|   return 1 #error | ||||
| } | ||||
| 
 | ||||
| ####################  Private functions bellow ################################## | ||||
| ####################  Private functions below ################################## | ||||
| #_acme-challenge.www.domain.com | ||||
| #returns | ||||
| # _sub_domain=_acme-challenge.www | ||||
| @ -171,7 +199,7 @@ _get_root() { | ||||
| 
 | ||||
| #Usage: method  URI  data | ||||
| _rest() { | ||||
|   m=$1 | ||||
|   m="$1" | ||||
|   ep="$2" | ||||
|   data="$3" | ||||
|   _debug "$ep" | ||||
| @ -179,11 +207,11 @@ _rest() { | ||||
| 
 | ||||
|   _debug url "$url" | ||||
| 
 | ||||
|   if [ "$data" ]; then | ||||
|     _debug2 data "$data" | ||||
|     response="$(_post "$data" "$url")" | ||||
|   if [ "$m" = "GET" ]; then | ||||
|     response="$(_get "$url" | tr -d '\r')" | ||||
|   else | ||||
|     response="$(_get "$url")" | ||||
|     _debug2 data "$data" | ||||
|     response="$(_post "$data" "$url" | tr -d '\r')" | ||||
|   fi | ||||
| 
 | ||||
|   if [ "$?" != "0" ]; then | ||||
|  | ||||
							
								
								
									
										128
									
								
								dnsapi/dns_duckdns.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										128
									
								
								dnsapi/dns_duckdns.sh
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,128 @@ | ||||
| #!/usr/bin/env sh | ||||
| 
 | ||||
| #Created by RaidenII, to use DuckDNS's API to add/remove text records | ||||
| #06/27/2017 | ||||
| 
 | ||||
| # Pass credentials before "acme.sh --issue --dns dns_duckdns ..." | ||||
| # -- | ||||
| # export DuckDNS_Token="aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" | ||||
| # -- | ||||
| # | ||||
| # Due to the fact that DuckDNS uses StartSSL as cert provider, --insecure may need to be used with acme.sh | ||||
| 
 | ||||
| DuckDNS_API="https://www.duckdns.org/update" | ||||
| 
 | ||||
| ########  Public functions ##################### | ||||
| 
 | ||||
| #Usage: dns_duckdns_add _acme-challenge.domain.duckdns.org "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" | ||||
| dns_duckdns_add() { | ||||
|   fulldomain=$1 | ||||
|   txtvalue=$2 | ||||
| 
 | ||||
|   DuckDNS_Token="${DuckDNS_Token:-$(_readaccountconf_mutable DuckDNS_Token)}" | ||||
|   if [ -z "$DuckDNS_Token" ]; then | ||||
|     _err "You must export variable: DuckDNS_Token" | ||||
|     _err "The token for your DuckDNS account is necessary." | ||||
|     _err "You can look it up in your DuckDNS account." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   # Now save the credentials. | ||||
|   _saveaccountconf_mutable DuckDNS_Token "$DuckDNS_Token" | ||||
| 
 | ||||
|   # Unfortunately, DuckDNS does not seems to support lookup domain through API | ||||
|   # So I assume your credentials (which are your domain and token) are correct | ||||
|   # If something goes wrong, we will get a KO response from DuckDNS | ||||
| 
 | ||||
|   if ! _duckdns_get_domain; then | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   # Now add the TXT record to DuckDNS | ||||
|   _info "Trying to add TXT record" | ||||
|   if _duckdns_rest GET "domains=$_duckdns_domain&token=$DuckDNS_Token&txt=$txtvalue"; then | ||||
|     if [ "$response" = "OK" ]; then | ||||
|       _info "TXT record has been successfully added to your DuckDNS domain." | ||||
|       _info "Note that all subdomains under this domain uses the same TXT record." | ||||
|       return 0 | ||||
|     else | ||||
|       _err "Errors happened during adding the TXT record, response=$response" | ||||
|       return 1 | ||||
|     fi | ||||
|   else | ||||
|     _err "Errors happened during adding the TXT record." | ||||
|     return 1 | ||||
|   fi | ||||
| } | ||||
| 
 | ||||
| #Usage: fulldomain txtvalue | ||||
| #Remove the txt record after validation. | ||||
| dns_duckdns_rm() { | ||||
|   fulldomain=$1 | ||||
|   txtvalue=$2 | ||||
| 
 | ||||
|   DuckDNS_Token="${DuckDNS_Token:-$(_readaccountconf_mutable DuckDNS_Token)}" | ||||
|   if [ -z "$DuckDNS_Token" ]; then | ||||
|     _err "You must export variable: DuckDNS_Token" | ||||
|     _err "The token for your DuckDNS account is necessary." | ||||
|     _err "You can look it up in your DuckDNS account." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   if ! _duckdns_get_domain; then | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   # Now remove the TXT record from DuckDNS | ||||
|   _info "Trying to remove TXT record" | ||||
|   if _duckdns_rest GET "domains=$_duckdns_domain&token=$DuckDNS_Token&txt=&clear=true"; then | ||||
|     if [ "$response" = "OK" ]; then | ||||
|       _info "TXT record has been successfully removed from your DuckDNS domain." | ||||
|       return 0 | ||||
|     else | ||||
|       _err "Errors happened during removing the TXT record, response=$response" | ||||
|       return 1 | ||||
|     fi | ||||
|   else | ||||
|     _err "Errors happened during removing the TXT record." | ||||
|     return 1 | ||||
|   fi | ||||
| } | ||||
| 
 | ||||
| ####################  Private functions below ################################## | ||||
| 
 | ||||
| #fulldomain=_acme-challenge.domain.duckdns.org | ||||
| #returns | ||||
| # _duckdns_domain=domain | ||||
| _duckdns_get_domain() { | ||||
| 
 | ||||
|   # We'll extract the domain/username from full domain | ||||
|   _duckdns_domain="$(printf "%s" "$fulldomain" | _lower_case | _egrep_o '[.][^.][^.]*[.]duckdns.org' | cut -d . -f 2)" | ||||
| 
 | ||||
|   if [ -z "$_duckdns_domain" ]; then | ||||
|     _err "Error extracting the domain." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   return 0 | ||||
| } | ||||
| 
 | ||||
| #Usage: method URI | ||||
| _duckdns_rest() { | ||||
|   method=$1 | ||||
|   param="$2" | ||||
|   _debug param "$param" | ||||
|   url="$DuckDNS_API?$param" | ||||
|   _debug url "$url" | ||||
| 
 | ||||
|   # DuckDNS uses GET to update domain info | ||||
|   if [ "$method" = "GET" ]; then | ||||
|     response="$(_get "$url")" | ||||
|   else | ||||
|     _err "Unsupported method" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _debug2 response "$response" | ||||
|   return 0 | ||||
| } | ||||
							
								
								
									
										339
									
								
								dnsapi/dns_dyn.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										339
									
								
								dnsapi/dns_dyn.sh
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,339 @@ | ||||
| #!/usr/bin/env sh | ||||
| # | ||||
| # Dyn.com Domain API | ||||
| # | ||||
| # Author: Gerd Naschenweng | ||||
| # https://github.com/magicdude4eva | ||||
| # | ||||
| # Dyn Managed DNS API | ||||
| # https://help.dyn.com/dns-api-knowledge-base/ | ||||
| # | ||||
| # It is recommended to add a "Dyn Managed DNS" user specific for API access. | ||||
| # The "Zones & Records Permissions" required by this script are: | ||||
| # -- | ||||
| # RecordAdd | ||||
| # RecordUpdate | ||||
| # RecordDelete | ||||
| # RecordGet | ||||
| # ZoneGet | ||||
| # ZoneAddNode | ||||
| # ZoneRemoveNode | ||||
| # ZonePublish | ||||
| # -- | ||||
| # | ||||
| # Pass credentials before "acme.sh --issue --dns dns_dyn ..." | ||||
| # -- | ||||
| # export DYN_Customer="customer" | ||||
| # export DYN_Username="apiuser" | ||||
| # export DYN_Password="secret" | ||||
| # -- | ||||
| 
 | ||||
| DYN_API="https://api.dynect.net/REST" | ||||
| 
 | ||||
| #REST_API | ||||
| ########  Public functions ##################### | ||||
| 
 | ||||
| #Usage: add  _acme-challenge.www.domain.com   "Challenge-code" | ||||
| dns_dyn_add() { | ||||
|   fulldomain="$1" | ||||
|   txtvalue="$2" | ||||
| 
 | ||||
|   DYN_Customer="${DYN_Customer:-$(_readaccountconf_mutable DYN_Customer)}" | ||||
|   DYN_Username="${DYN_Username:-$(_readaccountconf_mutable DYN_Username)}" | ||||
|   DYN_Password="${DYN_Password:-$(_readaccountconf_mutable DYN_Password)}" | ||||
|   if [ -z "$DYN_Customer" ] || [ -z "$DYN_Username" ] || [ -z "$DYN_Password" ]; then | ||||
|     DYN_Customer="" | ||||
|     DYN_Username="" | ||||
|     DYN_Password="" | ||||
|     _err "You must export variables: DYN_Customer, DYN_Username and DYN_Password" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   #save the config variables to the account conf file. | ||||
|   _saveaccountconf_mutable DYN_Customer "$DYN_Customer" | ||||
|   _saveaccountconf_mutable DYN_Username "$DYN_Username" | ||||
|   _saveaccountconf_mutable DYN_Password "$DYN_Password" | ||||
| 
 | ||||
|   if ! _dyn_get_authtoken; then | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   if [ -z "$_dyn_authtoken" ]; then | ||||
|     _dyn_end_session | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   if ! _dyn_get_zone; then | ||||
|     _dyn_end_session | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   if ! _dyn_add_record; then | ||||
|     _dyn_end_session | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   if ! _dyn_publish_zone; then | ||||
|     _dyn_end_session | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _dyn_end_session | ||||
| 
 | ||||
|   return 0 | ||||
| } | ||||
| 
 | ||||
| #Usage: fulldomain txtvalue | ||||
| #Remove the txt record after validation. | ||||
| dns_dyn_rm() { | ||||
|   fulldomain="$1" | ||||
|   txtvalue="$2" | ||||
| 
 | ||||
|   DYN_Customer="${DYN_Customer:-$(_readaccountconf_mutable DYN_Customer)}" | ||||
|   DYN_Username="${DYN_Username:-$(_readaccountconf_mutable DYN_Username)}" | ||||
|   DYN_Password="${DYN_Password:-$(_readaccountconf_mutable DYN_Password)}" | ||||
|   if [ -z "$DYN_Customer" ] || [ -z "$DYN_Username" ] || [ -z "$DYN_Password" ]; then | ||||
|     DYN_Customer="" | ||||
|     DYN_Username="" | ||||
|     DYN_Password="" | ||||
|     _err "You must export variables: DYN_Customer, DYN_Username and DYN_Password" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   if ! _dyn_get_authtoken; then | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   if [ -z "$_dyn_authtoken" ]; then | ||||
|     _dyn_end_session | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   if ! _dyn_get_zone; then | ||||
|     _dyn_end_session | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   if ! _dyn_get_record_id; then | ||||
|     _dyn_end_session | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   if [ -z "$_dyn_record_id" ]; then | ||||
|     _dyn_end_session | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   if ! _dyn_rm_record; then | ||||
|     _dyn_end_session | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   if ! _dyn_publish_zone; then | ||||
|     _dyn_end_session | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _dyn_end_session | ||||
| 
 | ||||
|   return 0 | ||||
| } | ||||
| 
 | ||||
| ####################  Private functions below ################################## | ||||
| 
 | ||||
| #get Auth-Token | ||||
| _dyn_get_authtoken() { | ||||
| 
 | ||||
|   _info "Start Dyn API Session" | ||||
| 
 | ||||
|   data="{\"customer_name\":\"$DYN_Customer\", \"user_name\":\"$DYN_Username\", \"password\":\"$DYN_Password\"}" | ||||
|   dyn_url="$DYN_API/Session/" | ||||
|   method="POST" | ||||
| 
 | ||||
|   _debug data "$data" | ||||
|   _debug dyn_url "$dyn_url" | ||||
| 
 | ||||
|   export _H1="Content-Type: application/json" | ||||
| 
 | ||||
|   response="$(_post "$data" "$dyn_url" "" "$method")" | ||||
|   sessionstatus="$(printf "%s\n" "$response" | _egrep_o '"status" *: *"[^"]*' | _head_n 1 | sed 's#^"status" *: *"##')" | ||||
| 
 | ||||
|   _debug response "$response" | ||||
|   _debug sessionstatus "$sessionstatus" | ||||
| 
 | ||||
|   if [ "$sessionstatus" = "success" ]; then | ||||
|     _dyn_authtoken="$(printf "%s\n" "$response" | _egrep_o '"token" *: *"[^"]*' | _head_n 1 | sed 's#^"token" *: *"##')" | ||||
|     _info "Token received" | ||||
|     _debug _dyn_authtoken "$_dyn_authtoken" | ||||
|     return 0 | ||||
|   fi | ||||
| 
 | ||||
|   _dyn_authtoken="" | ||||
|   _err "get token failed" | ||||
|   return 1 | ||||
| } | ||||
| 
 | ||||
| #fulldomain=_acme-challenge.www.domain.com | ||||
| #returns | ||||
| # _dyn_zone=domain.com | ||||
| _dyn_get_zone() { | ||||
|   i=2 | ||||
|   while true; do | ||||
|     domain="$(printf "%s" "$fulldomain" | cut -d . -f "$i-100")" | ||||
|     if [ -z "$domain" ]; then | ||||
|       break | ||||
|     fi | ||||
| 
 | ||||
|     dyn_url="$DYN_API/Zone/$domain/" | ||||
| 
 | ||||
|     export _H1="Auth-Token: $_dyn_authtoken" | ||||
|     export _H2="Content-Type: application/json" | ||||
| 
 | ||||
|     response="$(_get "$dyn_url" "" "")" | ||||
|     sessionstatus="$(printf "%s\n" "$response" | _egrep_o '"status" *: *"[^"]*' | _head_n 1 | sed 's#^"status" *: *"##')" | ||||
| 
 | ||||
|     _debug dyn_url "$dyn_url" | ||||
|     _debug response "$response" | ||||
|     _debug sessionstatus "$sessionstatus" | ||||
| 
 | ||||
|     if [ "$sessionstatus" = "success" ]; then | ||||
|       _dyn_zone="$domain" | ||||
|       return 0 | ||||
|     fi | ||||
|     i=$(_math "$i" + 1) | ||||
|   done | ||||
| 
 | ||||
|   _dyn_zone="" | ||||
|   _err "get zone failed" | ||||
|   return 1 | ||||
| } | ||||
| 
 | ||||
| #add TXT record | ||||
| _dyn_add_record() { | ||||
| 
 | ||||
|   _info "Adding TXT record" | ||||
| 
 | ||||
|   data="{\"rdata\":{\"txtdata\":\"$txtvalue\"},\"ttl\":\"300\"}" | ||||
|   dyn_url="$DYN_API/TXTRecord/$_dyn_zone/$fulldomain/" | ||||
|   method="POST" | ||||
| 
 | ||||
|   export _H1="Auth-Token: $_dyn_authtoken" | ||||
|   export _H2="Content-Type: application/json" | ||||
| 
 | ||||
|   response="$(_post "$data" "$dyn_url" "" "$method")" | ||||
|   sessionstatus="$(printf "%s\n" "$response" | _egrep_o '"status" *: *"[^"]*' | _head_n 1 | sed 's#^"status" *: *"##')" | ||||
| 
 | ||||
|   _debug response "$response" | ||||
|   _debug sessionstatus "$sessionstatus" | ||||
| 
 | ||||
|   if [ "$sessionstatus" = "success" ]; then | ||||
|     _info "TXT Record successfully added" | ||||
|     return 0 | ||||
|   fi | ||||
| 
 | ||||
|   _err "add TXT record failed" | ||||
|   return 1 | ||||
| } | ||||
| 
 | ||||
| #publish the zone | ||||
| _dyn_publish_zone() { | ||||
| 
 | ||||
|   _info "Publishing zone" | ||||
| 
 | ||||
|   data="{\"publish\":\"true\"}" | ||||
|   dyn_url="$DYN_API/Zone/$_dyn_zone/" | ||||
|   method="PUT" | ||||
| 
 | ||||
|   export _H1="Auth-Token: $_dyn_authtoken" | ||||
|   export _H2="Content-Type: application/json" | ||||
| 
 | ||||
|   response="$(_post "$data" "$dyn_url" "" "$method")" | ||||
|   sessionstatus="$(printf "%s\n" "$response" | _egrep_o '"status" *: *"[^"]*' | _head_n 1 | sed 's#^"status" *: *"##')" | ||||
| 
 | ||||
|   _debug response "$response" | ||||
|   _debug sessionstatus "$sessionstatus" | ||||
| 
 | ||||
|   if [ "$sessionstatus" = "success" ]; then | ||||
|     _info "Zone published" | ||||
|     return 0 | ||||
|   fi | ||||
| 
 | ||||
|   _err "publish zone failed" | ||||
|   return 1 | ||||
| } | ||||
| 
 | ||||
| #get record_id of TXT record so we can delete the record | ||||
| _dyn_get_record_id() { | ||||
| 
 | ||||
|   _info "Getting record_id of TXT record" | ||||
| 
 | ||||
|   dyn_url="$DYN_API/TXTRecord/$_dyn_zone/$fulldomain/" | ||||
| 
 | ||||
|   export _H1="Auth-Token: $_dyn_authtoken" | ||||
|   export _H2="Content-Type: application/json" | ||||
| 
 | ||||
|   response="$(_get "$dyn_url" "" "")" | ||||
|   sessionstatus="$(printf "%s\n" "$response" | _egrep_o '"status" *: *"[^"]*' | _head_n 1 | sed 's#^"status" *: *"##')" | ||||
| 
 | ||||
|   _debug response "$response" | ||||
|   _debug sessionstatus "$sessionstatus" | ||||
| 
 | ||||
|   if [ "$sessionstatus" = "success" ]; then | ||||
|     _dyn_record_id="$(printf "%s\n" "$response" | _egrep_o "\"data\" *: *\[\"/REST/TXTRecord/$_dyn_zone/$fulldomain/[^\"]*" | _head_n 1 | sed "s#^\"data\" *: *\[\"/REST/TXTRecord/$_dyn_zone/$fulldomain/##")" | ||||
|     _debug _dyn_record_id "$_dyn_record_id" | ||||
|     return 0 | ||||
|   fi | ||||
| 
 | ||||
|   _dyn_record_id="" | ||||
|   _err "getting record_id failed" | ||||
|   return 1 | ||||
| } | ||||
| 
 | ||||
| #delete TXT record | ||||
| _dyn_rm_record() { | ||||
| 
 | ||||
|   _info "Deleting TXT record" | ||||
| 
 | ||||
|   dyn_url="$DYN_API/TXTRecord/$_dyn_zone/$fulldomain/$_dyn_record_id/" | ||||
|   method="DELETE" | ||||
| 
 | ||||
|   _debug dyn_url "$dyn_url" | ||||
| 
 | ||||
|   export _H1="Auth-Token: $_dyn_authtoken" | ||||
|   export _H2="Content-Type: application/json" | ||||
| 
 | ||||
|   response="$(_post "" "$dyn_url" "" "$method")" | ||||
|   sessionstatus="$(printf "%s\n" "$response" | _egrep_o '"status" *: *"[^"]*' | _head_n 1 | sed 's#^"status" *: *"##')" | ||||
| 
 | ||||
|   _debug response "$response" | ||||
|   _debug sessionstatus "$sessionstatus" | ||||
| 
 | ||||
|   if [ "$sessionstatus" = "success" ]; then | ||||
|     _info "TXT record successfully deleted" | ||||
|     return 0 | ||||
|   fi | ||||
| 
 | ||||
|   _err "delete TXT record failed" | ||||
|   return 1 | ||||
| } | ||||
| 
 | ||||
| #logout | ||||
| _dyn_end_session() { | ||||
| 
 | ||||
|   _info "End Dyn API Session" | ||||
| 
 | ||||
|   dyn_url="$DYN_API/Session/" | ||||
|   method="DELETE" | ||||
| 
 | ||||
|   _debug dyn_url "$dyn_url" | ||||
| 
 | ||||
|   export _H1="Auth-Token: $_dyn_authtoken" | ||||
|   export _H2="Content-Type: application/json" | ||||
| 
 | ||||
|   response="$(_post "" "$dyn_url" "" "$method")" | ||||
| 
 | ||||
|   _debug response "$response" | ||||
| 
 | ||||
|   _dyn_authtoken="" | ||||
|   return 0 | ||||
| } | ||||
							
								
								
									
										228
									
								
								dnsapi/dns_dynu.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										228
									
								
								dnsapi/dns_dynu.sh
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,228 @@ | ||||
| #!/usr/bin/env sh | ||||
| 
 | ||||
| #Client ID | ||||
| #Dynu_ClientId="0b71cae7-a099-4f6b-8ddf-94571cdb760d" | ||||
| # | ||||
| #Secret | ||||
| #Dynu_Secret="aCUEY4BDCV45KI8CSIC3sp2LKQ9" | ||||
| # | ||||
| #Token | ||||
| Dynu_Token="" | ||||
| # | ||||
| #Endpoint | ||||
| Dynu_EndPoint="https://api.dynu.com/v1" | ||||
| # | ||||
| #Author: Dynu Systems, Inc. | ||||
| #Report Bugs here: https://github.com/shar0119/acme.sh | ||||
| # | ||||
| ########  Public functions ##################### | ||||
| 
 | ||||
| #Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" | ||||
| dns_dynu_add() { | ||||
|   fulldomain=$1 | ||||
|   txtvalue=$2 | ||||
| 
 | ||||
|   if [ -z "$Dynu_ClientId" ] || [ -z "$Dynu_Secret" ]; then | ||||
|     Dynu_ClientId="" | ||||
|     Dynu_Secret="" | ||||
|     _err "Dynu client id and secret is not specified." | ||||
|     _err "Please create you API client id and secret and try again." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   #save the client id and secret to the account conf file. | ||||
|   _saveaccountconf Dynu_ClientId "$Dynu_ClientId" | ||||
|   _saveaccountconf Dynu_Secret "$Dynu_Secret" | ||||
| 
 | ||||
|   if [ -z "$Dynu_Token" ]; then | ||||
|     _info "Getting Dynu token." | ||||
|     if ! _dynu_authentication; then | ||||
|       _err "Can not get token." | ||||
|     fi | ||||
|   fi | ||||
| 
 | ||||
|   _debug "Detect root zone" | ||||
|   if ! _get_root "$fulldomain"; then | ||||
|     _err "Invalid domain." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _debug _node "$_node" | ||||
|   _debug _domain_name "$_domain_name" | ||||
| 
 | ||||
|   _info "Creating TXT record." | ||||
|   if ! _dynu_rest POST "dns/record/add" "{\"domain_name\":\"$_domain_name\",\"node_name\":\"$_node\",\"record_type\":\"TXT\",\"text_data\":\"$txtvalue\",\"state\":true,\"ttl\":90}"; then | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   if ! _contains "$response" "text_data"; then | ||||
|     _err "Could not add TXT record." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   return 0 | ||||
| } | ||||
| 
 | ||||
| #Usage: rm _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" | ||||
| dns_dynu_rm() { | ||||
|   fulldomain=$1 | ||||
|   txtvalue=$2 | ||||
| 
 | ||||
|   if [ -z "$Dynu_ClientId" ] || [ -z "$Dynu_Secret" ]; then | ||||
|     Dynu_ClientId="" | ||||
|     Dynu_Secret="" | ||||
|     _err "Dynu client id and secret is not specified." | ||||
|     _err "Please create you API client id and secret and try again." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   #save the client id and secret to the account conf file. | ||||
|   _saveaccountconf Dynu_ClientId "$Dynu_ClientId" | ||||
|   _saveaccountconf Dynu_Secret "$Dynu_Secret" | ||||
| 
 | ||||
|   if [ -z "$Dynu_Token" ]; then | ||||
|     _info "Getting Dynu token." | ||||
|     if ! _dynu_authentication; then | ||||
|       _err "Can not get token." | ||||
|     fi | ||||
|   fi | ||||
| 
 | ||||
|   _debug "Detect root zone." | ||||
|   if ! _get_root "$fulldomain"; then | ||||
|     _err "Invalid domain." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _debug _node "$_node" | ||||
|   _debug _domain_name "$_domain_name" | ||||
| 
 | ||||
|   _info "Checking for TXT record." | ||||
|   if ! _get_recordid "$fulldomain" "$txtvalue"; then | ||||
|     _err "Could not get TXT record id." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   if [ "$_dns_record_id" = "" ]; then | ||||
|     _err "TXT record not found." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _info "Removing TXT record." | ||||
|   if ! _delete_txt_record "$_dns_record_id"; then | ||||
|     _err "Could not remove TXT record $_dns_record_id." | ||||
|   fi | ||||
| 
 | ||||
|   return 0 | ||||
| } | ||||
| 
 | ||||
| ########  Private functions below ################################## | ||||
| #_acme-challenge.www.domain.com | ||||
| #returns | ||||
| # _node=_acme-challenge.www | ||||
| # _domain_name=domain.com | ||||
| _get_root() { | ||||
|   domain=$1 | ||||
|   i=2 | ||||
|   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 ! _dynu_rest GET "dns/get/$h"; then | ||||
|       return 1 | ||||
|     fi | ||||
| 
 | ||||
|     if _contains "$response" "\"name\":\"$h\"" >/dev/null; then | ||||
|       _domain_name=$h | ||||
|       _node=$(printf "%s" "$domain" | cut -d . -f 1-$p) | ||||
|       return 0 | ||||
|     fi | ||||
|     p=$i | ||||
|     i=$(_math "$i" + 1) | ||||
|   done | ||||
|   return 1 | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| _get_recordid() { | ||||
|   fulldomain=$1 | ||||
|   txtvalue=$2 | ||||
| 
 | ||||
|   if ! _dynu_rest GET "dns/record/get?hostname=$fulldomain&rrtype=TXT"; then | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   if ! _contains "$response" "$txtvalue"; then | ||||
|     _dns_record_id=0 | ||||
|     return 0 | ||||
|   fi | ||||
| 
 | ||||
|   _dns_record_id=$(printf "%s" "$response" | _egrep_o "{[^}]*}" | grep "\"text_data\":\"$txtvalue\"" | _egrep_o ",[^,]*," | grep ',"id":' | tr -d ",," | cut -d : -f 2) | ||||
| 
 | ||||
|   return 0 | ||||
| } | ||||
| 
 | ||||
| _delete_txt_record() { | ||||
|   _dns_record_id=$1 | ||||
| 
 | ||||
|   if ! _dynu_rest GET "dns/record/delete/$_dns_record_id"; then | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   if ! _contains "$response" "true"; then | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   return 0 | ||||
| } | ||||
| 
 | ||||
| _dynu_rest() { | ||||
|   m=$1 | ||||
|   ep="$2" | ||||
|   data="$3" | ||||
|   _debug "$ep" | ||||
| 
 | ||||
|   export _H1="Authorization: Bearer $Dynu_Token" | ||||
|   export _H2="Content-Type: application/json" | ||||
| 
 | ||||
|   if [ "$data" ]; then | ||||
|     _debug data "$data" | ||||
|     response="$(_post "$data" "$Dynu_EndPoint/$ep" "" "$m")" | ||||
|   else | ||||
|     _info "Getting $Dynu_EndPoint/$ep" | ||||
|     response="$(_get "$Dynu_EndPoint/$ep")" | ||||
|   fi | ||||
| 
 | ||||
|   if [ "$?" != "0" ]; then | ||||
|     _err "error $ep" | ||||
|     return 1 | ||||
|   fi | ||||
|   _debug2 response "$response" | ||||
|   return 0 | ||||
| } | ||||
| 
 | ||||
| _dynu_authentication() { | ||||
|   realm="$(printf "%s" "$Dynu_ClientId:$Dynu_Secret" | _base64)" | ||||
| 
 | ||||
|   export _H1="Authorization: Basic $realm" | ||||
|   export _H2="Content-Type: application/json" | ||||
| 
 | ||||
|   response="$(_get "$Dynu_EndPoint/oauth2/token")" | ||||
|   if [ "$?" != "0" ]; then | ||||
|     _err "Authentication failed." | ||||
|     return 1 | ||||
|   fi | ||||
|   if _contains "$response" "accessToken"; then | ||||
|     Dynu_Token=$(printf "%s" "$response" | tr -d "[]" | cut -d , -f 2 | cut -d : -f 2 | cut -d '"' -f 2) | ||||
|   fi | ||||
|   if _contains "$Dynu_Token" "null"; then | ||||
|     Dynu_Token="" | ||||
|   fi | ||||
| 
 | ||||
|   _debug2 response "$response" | ||||
|   return 0 | ||||
| } | ||||
							
								
								
									
										362
									
								
								dnsapi/dns_freedns.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										362
									
								
								dnsapi/dns_freedns.sh
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,362 @@ | ||||
| #!/usr/bin/env sh | ||||
| 
 | ||||
| #This file name is "dns_freedns.sh" | ||||
| #So, here must be a method dns_freedns_add() | ||||
| #Which will be called by acme.sh to add the txt record to your api system. | ||||
| #returns 0 means success, otherwise error. | ||||
| # | ||||
| #Author: David Kerr | ||||
| #Report Bugs here: https://github.com/dkerr64/acme.sh | ||||
| # | ||||
| ########  Public functions ##################### | ||||
| 
 | ||||
| # Export FreeDNS userid and password in following variables... | ||||
| #  FREEDNS_User=username | ||||
| #  FREEDNS_Password=password | ||||
| # login cookie is saved in acme account config file so userid / pw | ||||
| # need to be set only when changed. | ||||
| 
 | ||||
| #Usage: dns_freedns_add   _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" | ||||
| dns_freedns_add() { | ||||
|   fulldomain="$1" | ||||
|   txtvalue="$2" | ||||
| 
 | ||||
|   _info "Add TXT record using FreeDNS" | ||||
|   _debug "fulldomain: $fulldomain" | ||||
|   _debug "txtvalue: $txtvalue" | ||||
| 
 | ||||
|   if [ -z "$FREEDNS_User" ] || [ -z "$FREEDNS_Password" ]; then | ||||
|     FREEDNS_User="" | ||||
|     FREEDNS_Password="" | ||||
|     if [ -z "$FREEDNS_COOKIE" ]; then | ||||
|       _err "You did not specify the FreeDNS username and password yet." | ||||
|       _err "Please export as FREEDNS_User / FREEDNS_Password and try again." | ||||
|       return 1 | ||||
|     fi | ||||
|     using_cached_cookies="true" | ||||
|   else | ||||
|     FREEDNS_COOKIE="$(_freedns_login "$FREEDNS_User" "$FREEDNS_Password")" | ||||
|     if [ -z "$FREEDNS_COOKIE" ]; then | ||||
|       return 1 | ||||
|     fi | ||||
|     using_cached_cookies="false" | ||||
|   fi | ||||
| 
 | ||||
|   _debug "FreeDNS login cookies: $FREEDNS_COOKIE (cached = $using_cached_cookies)" | ||||
| 
 | ||||
|   _saveaccountconf FREEDNS_COOKIE "$FREEDNS_COOKIE" | ||||
| 
 | ||||
|   # split our full domain name into two parts... | ||||
|   i="$(echo "$fulldomain" | tr '.' ' ' | wc -w)" | ||||
|   i="$(_math "$i" - 1)" | ||||
|   top_domain="$(echo "$fulldomain" | cut -d. -f "$i"-100)" | ||||
|   i="$(_math "$i" - 1)" | ||||
|   sub_domain="$(echo "$fulldomain" | cut -d. -f -"$i")" | ||||
| 
 | ||||
|   _debug top_domain "$top_domain" | ||||
|   _debug sub_domain "$sub_domain" | ||||
|   # Sometimes FreeDNS does not return the subdomain page but rather | ||||
|   # returns a page regarding becoming a premium member.  This usually | ||||
|   # happens after a period of inactivity.  Immediately trying again | ||||
|   # returns the correct subdomain page.  So, we will try twice to | ||||
|   # load the page and obtain our domain ID | ||||
|   attempts=2 | ||||
|   while [ "$attempts" -gt "0" ]; do | ||||
|     attempts="$(_math "$attempts" - 1)" | ||||
|     htmlpage="$(_freedns_retrieve_subdomain_page "$FREEDNS_COOKIE")" | ||||
|     if [ "$?" != "0" ]; then | ||||
|       if [ "$using_cached_cookies" = "true" ]; then | ||||
|         _err "Has your FreeDNS username and password changed?  If so..." | ||||
|         _err "Please export as FREEDNS_User / FREEDNS_Password and try again." | ||||
|       fi | ||||
|       return 1 | ||||
|     fi | ||||
|     _debug2 htmlpage "$htmlpage" | ||||
| 
 | ||||
|     subdomain_csv="$(echo "$htmlpage" | tr -d "\n\r" | _egrep_o '<form .*</form>' | sed 's/<tr>/@<tr>/g' | tr '@' '\n' | grep edit.php | grep "$top_domain")" | ||||
|     _debug2 subdomain_csv "$subdomain_csv" | ||||
| 
 | ||||
|     # The above beauty ends with striping out rows that do not have an | ||||
|     # href to edit.php and do not have the top domain we are looking for. | ||||
|     # So all we should be left with is CSV of table of subdomains we are | ||||
|     # interested in. | ||||
| 
 | ||||
|     # Now we have to read through this table and extract the data we need | ||||
|     lines="$(echo "$subdomain_csv" | wc -l)" | ||||
|     i=0 | ||||
|     found=0 | ||||
|     while [ "$i" -lt "$lines" ]; do | ||||
|       i="$(_math "$i" + 1)" | ||||
|       line="$(echo "$subdomain_csv" | sed -n "${i}p")" | ||||
|       _debug2 line "$line" | ||||
|       if [ $found = 0 ] && _contains "$line" "<td>$top_domain</td>"; then | ||||
|         # this line will contain DNSdomainid for the top_domain | ||||
|         DNSdomainid="$(echo "$line" | _egrep_o "edit_domain_id *= *.*>" | cut -d = -f 2 | cut -d '>' -f 1)" | ||||
|         _debug2 DNSdomainid "$DNSdomainid" | ||||
|         found=1 | ||||
|       else | ||||
|         # lines contain DNS records for all subdomains | ||||
|         DNSname="$(echo "$line" | _egrep_o 'edit.php.*</a>' | cut -d '>' -f 2 | cut -d '<' -f 1)" | ||||
|         _debug2 DNSname "$DNSname" | ||||
|         DNStype="$(echo "$line" | sed 's/<td/@<td/g' | tr '@' '\n' | sed -n '4p' | cut -d '>' -f 2 | cut -d '<' -f 1)" | ||||
|         _debug2 DNStype "$DNStype" | ||||
|         if [ "$DNSname" = "$fulldomain" ] && [ "$DNStype" = "TXT" ]; then | ||||
|           DNSdataid="$(echo "$line" | _egrep_o 'data_id=.*' | cut -d = -f 2 | cut -d '>' -f 1)" | ||||
|           # Now get current value for the TXT record.  This method may | ||||
|           # not produce accurate results as the value field is truncated | ||||
|           # on this webpage. To get full value we would need to load | ||||
|           # another page. However we don't really need this so long as | ||||
|           # there is only one TXT record for the acme challenge subdomain. | ||||
|           DNSvalue="$(echo "$line" | sed 's/<td/@<td/g' | tr '@' '\n' | sed -n '5p' | cut -d '>' -f 2 | cut -d '<' -f 1)" | ||||
|           _debug2 DNSvalue "$DNSvalue" | ||||
|           if [ $found != 0 ]; then | ||||
|             break | ||||
|             # we are breaking out of the loop at the first match of DNS name | ||||
|             # and DNS type (if we are past finding the domainid). This assumes | ||||
|             # that there is only ever one TXT record for the LetsEncrypt/acme | ||||
|             # challenge subdomain.  This seems to be a reasonable assumption | ||||
|             # as the acme client deletes the TXT record on successful validation. | ||||
|           fi | ||||
|         else | ||||
|           DNSname="" | ||||
|           DNStype="" | ||||
|         fi | ||||
|       fi | ||||
|     done | ||||
| 
 | ||||
|     _debug "DNSname: $DNSname DNStype: $DNStype DNSdomainid: $DNSdomainid DNSdataid: $DNSdataid" | ||||
|     _debug "DNSvalue: $DNSvalue" | ||||
| 
 | ||||
|     if [ -z "$DNSdomainid" ]; then | ||||
|       # If domain ID is empty then something went wrong (top level | ||||
|       # domain not found at FreeDNS). | ||||
|       if [ "$attempts" = "0" ]; then | ||||
|         # exhausted maximum retry attempts | ||||
|         _debug "$htmlpage" | ||||
|         _debug "$subdomain_csv" | ||||
|         _err "Domain $top_domain not found at FreeDNS" | ||||
|         return 1 | ||||
|       fi | ||||
|     else | ||||
|       # break out of the 'retry' loop... we have found our domain ID | ||||
|       break | ||||
|     fi | ||||
|     _info "Domain $top_domain not found at FreeDNS" | ||||
|     _info "Retry loading subdomain page ($attempts attempts remaining)" | ||||
|   done | ||||
| 
 | ||||
|   if [ -z "$DNSdataid" ]; then | ||||
|     # If data ID is empty then specific subdomain does not exist yet, need | ||||
|     # to create it this should always be the case as the acme client | ||||
|     # deletes the entry after domain is validated. | ||||
|     _freedns_add_txt_record "$FREEDNS_COOKIE" "$DNSdomainid" "$sub_domain" "$txtvalue" | ||||
|     return $? | ||||
|   else | ||||
|     if [ "$txtvalue" = "$DNSvalue" ]; then | ||||
|       # if value in TXT record matches value requested then DNS record | ||||
|       # does not need to be updated. But... | ||||
|       # Testing value match fails.  Website is truncating the value field. | ||||
|       # So for now we will always go down the else path.  Though in theory | ||||
|       # should never come here anyway as the acme client deletes | ||||
|       # the TXT record on successful validation, so we should not even | ||||
|       # have found a TXT record !! | ||||
|       _info "No update necessary for $fulldomain at FreeDNS" | ||||
|       return 0 | ||||
|     else | ||||
|       # Delete the old TXT record (with the wrong value) | ||||
|       if _freedns_delete_txt_record "$FREEDNS_COOKIE" "$DNSdataid"; then | ||||
|         # And add in new TXT record with the value provided | ||||
|         _freedns_add_txt_record "$FREEDNS_COOKIE" "$DNSdomainid" "$sub_domain" "$txtvalue" | ||||
|       fi | ||||
|       return $? | ||||
|     fi | ||||
|   fi | ||||
|   return 0 | ||||
| } | ||||
| 
 | ||||
| #Usage: fulldomain txtvalue | ||||
| #Remove the txt record after validation. | ||||
| dns_freedns_rm() { | ||||
|   fulldomain="$1" | ||||
|   txtvalue="$2" | ||||
| 
 | ||||
|   _info "Delete TXT record using FreeDNS" | ||||
|   _debug "fulldomain: $fulldomain" | ||||
|   _debug "txtvalue: $txtvalue" | ||||
| 
 | ||||
|   # Need to read cookie from conf file again in case new value set | ||||
|   # during login to FreeDNS when TXT record was created. | ||||
|   # acme.sh does not have a _readaccountconf() function | ||||
|   FREEDNS_COOKIE="$(_read_conf "$ACCOUNT_CONF_PATH" "FREEDNS_COOKIE")" | ||||
|   _debug "FreeDNS login cookies: $FREEDNS_COOKIE" | ||||
| 
 | ||||
|   # Sometimes FreeDNS does not return the subdomain page but rather | ||||
|   # returns a page regarding becoming a premium member.  This usually | ||||
|   # happens after a period of inactivity.  Immediately trying again | ||||
|   # returns the correct subdomain page.  So, we will try twice to | ||||
|   # load the page and obtain our TXT record. | ||||
|   attempts=2 | ||||
|   while [ "$attempts" -gt "0" ]; do | ||||
|     attempts="$(_math "$attempts" - 1)" | ||||
| 
 | ||||
|     htmlpage="$(_freedns_retrieve_subdomain_page "$FREEDNS_COOKIE")" | ||||
|     if [ "$?" != "0" ]; then | ||||
|       return 1 | ||||
|     fi | ||||
| 
 | ||||
|     subdomain_csv="$(echo "$htmlpage" | tr -d "\n\r" | _egrep_o '<form .*</form>' | sed 's/<tr>/@<tr>/g' | tr '@' '\n' | grep edit.php | grep "$fulldomain")" | ||||
|     _debug2 subdomain_csv "$subdomain_csv" | ||||
| 
 | ||||
|     # The above beauty ends with striping out rows that do not have an | ||||
|     # href to edit.php and do not have the domain name we are looking for. | ||||
|     # So all we should be left with is CSV of table of subdomains we are | ||||
|     # interested in. | ||||
| 
 | ||||
|     # Now we have to read through this table and extract the data we need | ||||
|     lines="$(echo "$subdomain_csv" | wc -l)" | ||||
|     i=0 | ||||
|     found=0 | ||||
|     while [ "$i" -lt "$lines" ]; do | ||||
|       i="$(_math "$i" + 1)" | ||||
|       line="$(echo "$subdomain_csv" | sed -n "${i}p")" | ||||
|       _debug2 line "$line" | ||||
|       DNSname="$(echo "$line" | _egrep_o 'edit.php.*</a>' | cut -d '>' -f 2 | cut -d '<' -f 1)" | ||||
|       _debug2 DNSname "$DNSname" | ||||
|       DNStype="$(echo "$line" | sed 's/<td/@<td/g' | tr '@' '\n' | sed -n '4p' | cut -d '>' -f 2 | cut -d '<' -f 1)" | ||||
|       _debug2 DNStype "$DNStype" | ||||
|       if [ "$DNSname" = "$fulldomain" ] && [ "$DNStype" = "TXT" ]; then | ||||
|         DNSdataid="$(echo "$line" | _egrep_o 'data_id=.*' | cut -d = -f 2 | cut -d '>' -f 1)" | ||||
|         _debug2 DNSdataid "$DNSdataid" | ||||
|         DNSvalue="$(echo "$line" | sed 's/<td/@<td/g' | tr '@' '\n' | sed -n '5p' | cut -d '>' -f 2 | cut -d '<' -f 1)" | ||||
|         _debug2 DNSvalue "$DNSvalue" | ||||
|         #     if [ "$DNSvalue" = "$txtvalue" ]; then | ||||
|         # Testing value match fails.  Website is truncating the value | ||||
|         # field. So for now we will assume that there is only one TXT | ||||
|         # field for the sub domain and just delete it. Currently this | ||||
|         # is a safe assumption. | ||||
|         _freedns_delete_txt_record "$FREEDNS_COOKIE" "$DNSdataid" | ||||
|         return $? | ||||
|         #     fi | ||||
|       fi | ||||
|     done | ||||
|   done | ||||
| 
 | ||||
|   # If we get this far we did not find a match (after two attempts) | ||||
|   # Not necessarily an error, but log anyway. | ||||
|   _debug2 "$subdomain_csv" | ||||
|   _info "Cannot delete TXT record for $fulldomain/$txtvalue. Does not exist at FreeDNS" | ||||
|   return 0 | ||||
| } | ||||
| 
 | ||||
| ####################  Private functions below ################################## | ||||
| 
 | ||||
| # usage: _freedns_login username password | ||||
| # print string "cookie=value" etc. | ||||
| # returns 0 success | ||||
| _freedns_login() { | ||||
|   export _H1="Accept-Language:en-US" | ||||
|   username="$1" | ||||
|   password="$2" | ||||
|   url="https://freedns.afraid.org/zc.php?step=2" | ||||
| 
 | ||||
|   _debug "Login to FreeDNS as user $username" | ||||
| 
 | ||||
|   htmlpage="$(_post "username=$(printf '%s' "$username" | _url_encode)&password=$(printf '%s' "$password" | _url_encode)&submit=Login&action=auth" "$url")" | ||||
| 
 | ||||
|   if [ "$?" != "0" ]; then | ||||
|     _err "FreeDNS login failed for user $username bad RC from _post" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   cookies="$(grep -i '^Set-Cookie.*dns_cookie.*$' "$HTTP_HEADER" | _head_n 1 | tr -d "\r\n" | cut -d " " -f 2)" | ||||
| 
 | ||||
|   # if cookies is not empty then logon successful | ||||
|   if [ -z "$cookies" ]; then | ||||
|     _debug "$htmlpage" | ||||
|     _err "FreeDNS login failed for user $username. Check $HTTP_HEADER file" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   printf "%s" "$cookies" | ||||
|   return 0 | ||||
| } | ||||
| 
 | ||||
| # usage _freedns_retrieve_subdomain_page login_cookies | ||||
| # echo page retrieved (html) | ||||
| # returns 0 success | ||||
| _freedns_retrieve_subdomain_page() { | ||||
|   export _H1="Cookie:$1" | ||||
|   export _H2="Accept-Language:en-US" | ||||
|   url="https://freedns.afraid.org/subdomain/" | ||||
| 
 | ||||
|   _debug "Retrieve subdomain page from FreeDNS" | ||||
| 
 | ||||
|   htmlpage="$(_get "$url")" | ||||
| 
 | ||||
|   if [ "$?" != "0" ]; then | ||||
|     _err "FreeDNS retrieve subdomains failed bad RC from _get" | ||||
|     return 1 | ||||
|   elif [ -z "$htmlpage" ]; then | ||||
|     _err "FreeDNS returned empty subdomain page" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _debug2 "$htmlpage" | ||||
| 
 | ||||
|   printf "%s" "$htmlpage" | ||||
|   return 0 | ||||
| } | ||||
| 
 | ||||
| # usage _freedns_add_txt_record login_cookies domain_id subdomain value | ||||
| # returns 0 success | ||||
| _freedns_add_txt_record() { | ||||
|   export _H1="Cookie:$1" | ||||
|   export _H2="Accept-Language:en-US" | ||||
|   domain_id="$2" | ||||
|   subdomain="$3" | ||||
|   value="$(printf '%s' "$4" | _url_encode)" | ||||
|   url="http://freedns.afraid.org/subdomain/save.php?step=2" | ||||
| 
 | ||||
|   htmlpage="$(_post "type=TXT&domain_id=$domain_id&subdomain=$subdomain&address=%22$value%22&send=Save%21" "$url")" | ||||
| 
 | ||||
|   if [ "$?" != "0" ]; then | ||||
|     _err "FreeDNS failed to add TXT record for $subdomain bad RC from _post" | ||||
|     return 1 | ||||
|   elif ! grep "200 OK" "$HTTP_HEADER" >/dev/null; then | ||||
|     _debug "$htmlpage" | ||||
|     _err "FreeDNS failed to add TXT record for $subdomain. Check $HTTP_HEADER file" | ||||
|     return 1 | ||||
|   elif _contains "$htmlpage" "security code was incorrect"; then | ||||
|     _debug "$htmlpage" | ||||
|     _err "FreeDNS failed to add TXT record for $subdomain as FreeDNS requested security code" | ||||
|     _err "Note that you cannot use automatic DNS validation for FreeDNS public domains" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _debug2 "$htmlpage" | ||||
|   _info "Added acme challenge TXT record for $fulldomain at FreeDNS" | ||||
|   return 0 | ||||
| } | ||||
| 
 | ||||
| # usage _freedns_delete_txt_record login_cookies data_id | ||||
| # returns 0 success | ||||
| _freedns_delete_txt_record() { | ||||
|   export _H1="Cookie:$1" | ||||
|   export _H2="Accept-Language:en-US" | ||||
|   data_id="$2" | ||||
|   url="https://freedns.afraid.org/subdomain/delete2.php" | ||||
| 
 | ||||
|   htmlheader="$(_get "$url?data_id%5B%5D=$data_id&submit=delete+selected" "onlyheader")" | ||||
| 
 | ||||
|   if [ "$?" != "0" ]; then | ||||
|     _err "FreeDNS failed to delete TXT record for $data_id bad RC from _get" | ||||
|     return 1 | ||||
|   elif ! _contains "$htmlheader" "200 OK"; then | ||||
|     _debug "$htmlheader" | ||||
|     _err "FreeDNS failed to delete TXT record $data_id" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _info "Deleted acme challenge TXT record for $fulldomain at FreeDNS" | ||||
|   return 0 | ||||
| } | ||||
							
								
								
									
										123
									
								
								dnsapi/dns_gandi_livedns.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										123
									
								
								dnsapi/dns_gandi_livedns.sh
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,123 @@ | ||||
| #!/usr/bin/env sh | ||||
| 
 | ||||
| # Gandi LiveDNS v5 API | ||||
| # http://doc.livedns.gandi.net/ | ||||
| # currently under beta | ||||
| # | ||||
| # Requires GANDI API KEY set in GANDI_LIVEDNS_KEY set as environment variable | ||||
| # | ||||
| #Author: Frédéric Crozat <fcrozat@suse.com> | ||||
| #Report Bugs here: https://github.com/fcrozat/acme.sh | ||||
| # | ||||
| ########  Public functions ##################### | ||||
| 
 | ||||
| GANDI_LIVEDNS_API="https://dns.api.gandi.net/api/v5" | ||||
| 
 | ||||
| #Usage: dns_gandi_livedns_add   _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" | ||||
| dns_gandi_livedns_add() { | ||||
|   fulldomain=$1 | ||||
|   txtvalue=$2 | ||||
| 
 | ||||
|   if [ -z "$GANDI_LIVEDNS_KEY" ]; then | ||||
|     _err "No API key specified for Gandi LiveDNS." | ||||
|     _err "Create your key and export it as GANDI_LIVEDNS_KEY" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _saveaccountconf GANDI_LIVEDNS_KEY "$GANDI_LIVEDNS_KEY" | ||||
| 
 | ||||
|   _debug "First detect the root zone" | ||||
|   if ! _get_root "$fulldomain"; then | ||||
|     _err "invalid domain" | ||||
|     return 1 | ||||
|   fi | ||||
|   _debug fulldomain "$fulldomain" | ||||
|   _debug txtvalue "$txtvalue" | ||||
|   _debug domain "$_domain" | ||||
|   _debug sub_domain "$_sub_domain" | ||||
| 
 | ||||
|   _gandi_livedns_rest PUT "domains/$_domain/records/$_sub_domain/TXT" "{\"rrset_ttl\": 300, \"rrset_values\":[\"$txtvalue\"]}" \ | ||||
|     && _contains "$response" '{"message": "DNS Record Created"}' \ | ||||
|     && _info "Add $(__green "success")" | ||||
| } | ||||
| 
 | ||||
| #Usage: fulldomain txtvalue | ||||
| #Remove the txt record after validation. | ||||
| dns_gandi_livedns_rm() { | ||||
|   fulldomain=$1 | ||||
|   txtvalue=$2 | ||||
| 
 | ||||
|   _debug "First detect the root zone" | ||||
|   if ! _get_root "$fulldomain"; then | ||||
|     _err "invalid domain" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _debug fulldomain "$fulldomain" | ||||
|   _debug domain "$_domain" | ||||
|   _debug sub_domain "$_sub_domain" | ||||
| 
 | ||||
|   _gandi_livedns_rest DELETE "domains/$_domain/records/$_sub_domain/TXT" "" | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| ####################  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) | ||||
|     _debug h "$h" | ||||
|     if [ -z "$h" ]; then | ||||
|       #not valid | ||||
|       return 1 | ||||
|     fi | ||||
| 
 | ||||
|     if ! _gandi_livedns_rest GET "domains/$h"; then | ||||
|       return 1 | ||||
|     fi | ||||
| 
 | ||||
|     if _contains "$response" '"code": 401'; then | ||||
|       _err "$response" | ||||
|       return 1 | ||||
|     elif _contains "$response" '"code": 404'; 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 | ||||
| } | ||||
| 
 | ||||
| _gandi_livedns_rest() { | ||||
|   m=$1 | ||||
|   ep="$2" | ||||
|   data="$3" | ||||
|   _debug "$ep" | ||||
| 
 | ||||
|   export _H1="Content-Type: application/json" | ||||
|   export _H2="X-Api-Key: $GANDI_LIVEDNS_KEY" | ||||
| 
 | ||||
|   if [ "$m" = "GET" ]; then | ||||
|     response="$(_get "$GANDI_LIVEDNS_API/$ep")" | ||||
|   else | ||||
|     _debug data "$data" | ||||
|     response="$(_post "$data" "$GANDI_LIVEDNS_API/$ep" "" "$m")" | ||||
|   fi | ||||
| 
 | ||||
|   if [ "$?" != "0" ]; then | ||||
|     _err "error $ep" | ||||
|     return 1 | ||||
|   fi | ||||
|   _debug2 response "$response" | ||||
|   return 0 | ||||
| } | ||||
| @ -40,7 +40,7 @@ dns_gd_add() { | ||||
|   if _gd_rest PUT "domains/$_domain/records/TXT/$_sub_domain" "[{\"data\":\"$txtvalue\"}]"; then | ||||
|     if [ "$response" = "{}" ]; then | ||||
|       _info "Added, sleeping 10 seconds" | ||||
|       sleep 10 | ||||
|       _sleep 10 | ||||
|       #todo: check if the record takes effect | ||||
|       return 0 | ||||
|     else | ||||
| @ -59,7 +59,7 @@ dns_gd_rm() { | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| ####################  Private functions bellow ################################## | ||||
| ####################  Private functions below ################################## | ||||
| #_acme-challenge.www.domain.com | ||||
| #returns | ||||
| # _sub_domain=_acme-challenge.www | ||||
| @ -98,8 +98,8 @@ _gd_rest() { | ||||
|   data="$3" | ||||
|   _debug "$ep" | ||||
| 
 | ||||
|   _H1="Authorization: sso-key $GD_Key:$GD_Secret" | ||||
|   _H2="Content-Type: application/json" | ||||
|   export _H1="Authorization: sso-key $GD_Key:$GD_Secret" | ||||
|   export _H2="Content-Type: application/json" | ||||
| 
 | ||||
|   if [ "$data" ]; then | ||||
|     _debug data "$data" | ||||
|  | ||||
							
								
								
									
										175
									
								
								dnsapi/dns_he.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										175
									
								
								dnsapi/dns_he.sh
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,175 @@ | ||||
| #!/usr/bin/env sh | ||||
| 
 | ||||
| ######################################################################## | ||||
| # Hurricane Electric hook script for acme.sh | ||||
| # | ||||
| # Environment variables: | ||||
| # | ||||
| #  - $HE_Username  (your dns.he.net username) | ||||
| #  - $HE_Password  (your dns.he.net password) | ||||
| # | ||||
| # Author: Ondrej Simek <me@ondrejsimek.com> | ||||
| # Git repo: https://github.com/angel333/acme.sh | ||||
| 
 | ||||
| #-- dns_he_add() - Add TXT record -------------------------------------- | ||||
| # Usage: dns_he_add _acme-challenge.subdomain.domain.com "XyZ123..." | ||||
| 
 | ||||
| dns_he_add() { | ||||
|   _full_domain=$1 | ||||
|   _txt_value=$2 | ||||
|   _info "Using DNS-01 Hurricane Electric hook" | ||||
| 
 | ||||
|   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." | ||||
|     return 1 | ||||
|   fi | ||||
|   _saveaccountconf HE_Username "$HE_Username" | ||||
|   _saveaccountconf HE_Password "$HE_Password" | ||||
| 
 | ||||
|   # Fills in the $_zone_id | ||||
|   _find_zone "$_full_domain" || return 1 | ||||
|   _debug "Zone id \"$_zone_id\" will be used." | ||||
| 
 | ||||
|   body="email=${HE_Username}&pass=${HE_Password}" | ||||
|   body="$body&account=" | ||||
|   body="$body&menu=edit_zone" | ||||
|   body="$body&Type=TXT" | ||||
|   body="$body&hosted_dns_zoneid=$_zone_id" | ||||
|   body="$body&hosted_dns_recordid=" | ||||
|   body="$body&hosted_dns_editzone=1" | ||||
|   body="$body&Priority=" | ||||
|   body="$body&Name=$_full_domain" | ||||
|   body="$body&Content=$_txt_value" | ||||
|   body="$body&TTL=300" | ||||
|   body="$body&hosted_dns_editrecord=Submit" | ||||
|   response="$(_post "$body" "https://dns.he.net/")" | ||||
|   exit_code="$?" | ||||
|   if [ "$exit_code" -eq 0 ]; then | ||||
|     _info "TXT record added successfully." | ||||
|   else | ||||
|     _err "Couldn't add the TXT record." | ||||
|   fi | ||||
|   _debug2 response "$response" | ||||
|   return "$exit_code" | ||||
| } | ||||
| 
 | ||||
| #-- dns_he_rm() - Remove TXT record ------------------------------------ | ||||
| # Usage: dns_he_rm _acme-challenge.subdomain.domain.com "XyZ123..." | ||||
| 
 | ||||
| dns_he_rm() { | ||||
|   _full_domain=$1 | ||||
|   _txt_value=$2 | ||||
|   _info "Cleaning up after DNS-01 Hurricane Electric hook" | ||||
| 
 | ||||
|   # fills in the $_zone_id | ||||
|   _find_zone "$_full_domain" || return 1 | ||||
|   _debug "Zone id \"$_zone_id\" will be used." | ||||
| 
 | ||||
|   # Find the record id to clean | ||||
|   body="email=${HE_Username}&pass=${HE_Password}" | ||||
|   body="$body&hosted_dns_zoneid=$_zone_id" | ||||
|   body="$body&menu=edit_zone" | ||||
|   body="$body&hosted_dns_editzone=" | ||||
|   domain_regex="$(echo "$_full_domain" | sed 's/\./\\./g')" # escape dots | ||||
|   _record_id=$(_post "$body" "https://dns.he.net/" \ | ||||
|     | tr -d '\n' \ | ||||
|     | _egrep_o "data=\""${_txt_value}"([^>]+>){6}[^<]+<[^;]+;deleteRecord\('[0-9]+','${domain_regex}','TXT'\)" \ | ||||
|     | _egrep_o "[0-9]+','${domain_regex}','TXT'\)$" \ | ||||
|     | _egrep_o "^[0-9]+" | ||||
|   ) | ||||
|   # The series of egreps above could have been done a bit shorter but | ||||
|   #  I wanted to double-check whether it's the correct record (in case | ||||
|   #  HE changes their website somehow). | ||||
| 
 | ||||
|   # Remove the record | ||||
|   body="email=${HE_Username}&pass=${HE_Password}" | ||||
|   body="$body&menu=edit_zone" | ||||
|   body="$body&hosted_dns_zoneid=$_zone_id" | ||||
|   body="$body&hosted_dns_recordid=$_record_id" | ||||
|   body="$body&hosted_dns_editzone=1" | ||||
|   body="$body&hosted_dns_delrecord=1" | ||||
|   body="$body&hosted_dns_delconfirm=delete" | ||||
|   _post "$body" "https://dns.he.net/" \ | ||||
|     | grep '<div id="dns_status" onClick="hideThis(this);">Successfully removed record.</div>' \ | ||||
|       >/dev/null | ||||
|   exit_code="$?" | ||||
|   if [ "$exit_code" -eq 0 ]; then | ||||
|     _info "Record removed successfully." | ||||
|   else | ||||
|     _err "Could not clean (remove) up the record. Please go to HE administration interface and clean it by hand." | ||||
|     return "$exit_code" | ||||
|   fi | ||||
| } | ||||
| 
 | ||||
| ########################## PRIVATE FUNCTIONS ########################### | ||||
| 
 | ||||
| #-- _find_zone() ------------------------------------------------------- | ||||
| # Returns the most specific zone found in administration interface. | ||||
| # | ||||
| # Example: | ||||
| # | ||||
| # _find_zone first.second.third.co.uk | ||||
| # | ||||
| # ... will return the first zone that exists in admin out of these: | ||||
| # - "first.second.third.co.uk" | ||||
| # - "second.third.co.uk" | ||||
| # - "third.co.uk" | ||||
| # - "co.uk" <-- unlikely | ||||
| # - "uk"    <-' | ||||
| # | ||||
| # (another approach would be something like this: | ||||
| #   https://github.com/hlandau/acme/blob/master/_doc/dns.hook | ||||
| #   - that's better if there are multiple pages. It's so much simpler. | ||||
| # ) | ||||
| 
 | ||||
| _find_zone() { | ||||
| 
 | ||||
|   _domain="$1" | ||||
| 
 | ||||
|   body="email=${HE_Username}&pass=${HE_Password}" | ||||
|   _matches=$(_post "$body" "https://dns.he.net/" \ | ||||
|     | _egrep_o "delete_dom.*name=\"[^\"]+\" value=\"[0-9]+" | ||||
|   ) | ||||
|   # Zone names and zone IDs are in same order | ||||
|   _zone_ids=$(echo "$_matches" | cut -d '"' -f 5) | ||||
|   _zone_names=$(echo "$_matches" | cut -d '"' -f 3) | ||||
|   _debug2 "These are the zones on this HE account:" | ||||
|   _debug2 "$_zone_names" | ||||
|   _debug2 "And these are their respective IDs:" | ||||
|   _debug2 "$_zone_ids" | ||||
| 
 | ||||
|   # Walk through all possible zone names | ||||
|   _strip_counter=1 | ||||
|   while true; do | ||||
|     _attempted_zone=$(echo "$_domain" | cut -d . -f ${_strip_counter}-) | ||||
| 
 | ||||
|     # All possible zone names have been tried | ||||
|     if [ -z "$_attempted_zone" ]; then | ||||
|       _err "No zone for domain \"$_domain\" found." | ||||
|       return 1 | ||||
|     fi | ||||
| 
 | ||||
|     _debug "Looking for zone \"${_attempted_zone}\"" | ||||
| 
 | ||||
|     # Take care of "." and only match whole lines. Note that grep -F | ||||
|     # cannot be used because there's no way to make it match whole | ||||
|     # lines. | ||||
|     regex="^$(echo "$_attempted_zone" | sed 's/\./\\./g')$" | ||||
|     line_num=$(echo "$_zone_names" \ | ||||
|       | grep -n "$regex" \ | ||||
|       | cut -d : -f 1 | ||||
|     ) | ||||
| 
 | ||||
|     if [ -n "$line_num" ]; then | ||||
|       _zone_id=$(echo "$_zone_ids" | sed "${line_num}q;d") | ||||
|       _debug "Found relevant zone \"$_attempted_zone\" with id \"$_zone_id\" - will be used for domain \"$_domain\"." | ||||
|       return 0 | ||||
|     fi | ||||
| 
 | ||||
|     _debug "Zone \"$_attempted_zone\" doesn't exist, let's try a less specific zone." | ||||
|     _strip_counter=$(_math "$_strip_counter" + 1) | ||||
|   done | ||||
| } | ||||
| # vim: et:ts=2:sw=2: | ||||
							
								
								
									
										102
									
								
								dnsapi/dns_infoblox.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								dnsapi/dns_infoblox.sh
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,102 @@ | ||||
| #!/usr/bin/env sh | ||||
| 
 | ||||
| ## Infoblox API integration by Jason Keller and Elijah Tenai | ||||
| ## | ||||
| ## Report any bugs via https://github.com/jasonkeller/acme.sh | ||||
| 
 | ||||
| dns_infoblox_add() { | ||||
| 
 | ||||
|   ## Nothing to see here, just some housekeeping | ||||
|   fulldomain=$1 | ||||
|   txtvalue=$2 | ||||
|   baseurlnObject="https://$Infoblox_Server/wapi/v2.2.2/record:txt?name=$fulldomain&text=$txtvalue&view=$Infoblox_View" | ||||
| 
 | ||||
|   _info "Using Infoblox API" | ||||
|   _debug fulldomain "$fulldomain" | ||||
|   _debug txtvalue "$txtvalue" | ||||
| 
 | ||||
|   ## Check for the credentials | ||||
|   if [ -z "$Infoblox_Creds" ] || [ -z "$Infoblox_Server" ]; then | ||||
|     Infoblox_Creds="" | ||||
|     Infoblox_Server="" | ||||
|     _err "You didn't specify the credentials, server or infoblox view yet (Infoblox_Creds, Infoblox_Server and Infoblox_View)." | ||||
|     _err "Please set them via EXPORT ([username:password], [ip or hostname]) and try again." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   if [ -z "$Infoblox_View" ]; then | ||||
|     Infoblox_View="default" | ||||
|   fi | ||||
| 
 | ||||
|   ## Save the credentials to the account file | ||||
|   _saveaccountconf Infoblox_Creds "$Infoblox_Creds" | ||||
|   _saveaccountconf Infoblox_Server "$Infoblox_Server" | ||||
|   _saveaccountconf Infoblox_View "$Infoblox_View" | ||||
| 
 | ||||
|   ## Base64 encode the credentials | ||||
|   Infoblox_CredsEncoded=$(printf "%b" "$Infoblox_Creds" | _base64) | ||||
| 
 | ||||
|   ## Construct the HTTP Authorization header | ||||
|   export _H1="Accept-Language:en-US" | ||||
|   export _H2="Authorization: Basic $Infoblox_CredsEncoded" | ||||
| 
 | ||||
|   ## Add the challenge record to the Infoblox grid member | ||||
|   result="$(_post "" "$baseurlnObject" "" "POST")" | ||||
| 
 | ||||
|   ## Let's see if we get something intelligible back from the unit | ||||
|   if [ "$(echo "$result" | _egrep_o "record:txt/.*:.*/$Infoblox_View")" ]; then | ||||
|     _info "Successfully created the txt record" | ||||
|     return 0 | ||||
|   else | ||||
|     _err "Error encountered during record addition" | ||||
|     _err "$result" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| dns_infoblox_rm() { | ||||
| 
 | ||||
|   ## Nothing to see here, just some housekeeping | ||||
|   fulldomain=$1 | ||||
|   txtvalue=$2 | ||||
| 
 | ||||
|   _info "Using Infoblox API" | ||||
|   _debug fulldomain "$fulldomain" | ||||
|   _debug txtvalue "$txtvalue" | ||||
| 
 | ||||
|   ## Base64 encode the credentials | ||||
|   Infoblox_CredsEncoded="$(printf "%b" "$Infoblox_Creds" | _base64)" | ||||
| 
 | ||||
|   ## Construct the HTTP Authorization header | ||||
|   export _H1="Accept-Language:en-US" | ||||
|   export _H2="Authorization: Basic $Infoblox_CredsEncoded" | ||||
| 
 | ||||
|   ## Does the record exist?  Let's check. | ||||
|   baseurlnObject="https://$Infoblox_Server/wapi/v2.2.2/record:txt?name=$fulldomain&text=$txtvalue&view=$Infoblox_View&_return_type=xml-pretty" | ||||
|   result="$(_get "$baseurlnObject")" | ||||
| 
 | ||||
|   ## Let's see if we get something intelligible back from the grid | ||||
|   if [ "$(echo "$result" | _egrep_o "record:txt/.*:.*/$Infoblox_View")" ]; then | ||||
|     ## Extract the object reference | ||||
|     objRef="$(printf "%b" "$result" | _egrep_o "record:txt/.*:.*/$Infoblox_View")" | ||||
|     objRmUrl="https://$Infoblox_Server/wapi/v2.2.2/$objRef" | ||||
|     ## Delete them! All the stale records! | ||||
|     rmResult="$(_post "" "$objRmUrl" "" "DELETE")" | ||||
|     ## Let's see if that worked | ||||
|     if [ "$(echo "$rmResult" | _egrep_o "record:txt/.*:.*/$Infoblox_View")" ]; then | ||||
|       _info "Successfully deleted $objRef" | ||||
|       return 0 | ||||
|     else | ||||
|       _err "Error occurred during txt record delete" | ||||
|       _err "$rmResult" | ||||
|       return 1 | ||||
|     fi | ||||
|   else | ||||
|     _err "Record to delete didn't match an existing record" | ||||
|     _err "$result" | ||||
|     return 1 | ||||
|   fi | ||||
| } | ||||
| 
 | ||||
| ####################  Private functions below ################################## | ||||
							
								
								
									
										355
									
								
								dnsapi/dns_inwx.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										355
									
								
								dnsapi/dns_inwx.sh
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,355 @@ | ||||
| #!/usr/bin/env sh | ||||
| 
 | ||||
| # | ||||
| #INWX_User="username" | ||||
| # | ||||
| #INWX_Password="password" | ||||
| 
 | ||||
| INWX_Api="https://api.domrobot.com/xmlrpc/" | ||||
| 
 | ||||
| ########  Public functions ##################### | ||||
| 
 | ||||
| #Usage: add  _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" | ||||
| dns_inwx_add() { | ||||
|   fulldomain=$1 | ||||
|   txtvalue=$2 | ||||
| 
 | ||||
|   INWX_User="${INWX_User:-$(_readaccountconf_mutable INWX_User)}" | ||||
|   INWX_Password="${INWX_Password:-$(_readaccountconf_mutable INWX_Password)}" | ||||
|   if [ -z "$INWX_User" ] || [ -z "$INWX_Password" ]; then | ||||
|     INWX_User="" | ||||
|     INWX_Password="" | ||||
|     _err "You don't specify inwx user and password yet." | ||||
|     _err "Please create you key and try again." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   #save the api key and email to the account conf file. | ||||
|   _saveaccountconf_mutable INWX_User "$INWX_User" | ||||
|   _saveaccountconf_mutable INWX_Password "$INWX_Password" | ||||
| 
 | ||||
|   _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 txt records" | ||||
| 
 | ||||
|   xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?> | ||||
|   <methodCall> | ||||
|   <methodName>nameserver.info</methodName> | ||||
|   <params> | ||||
|    <param> | ||||
|     <value> | ||||
|      <struct> | ||||
|       <member> | ||||
|        <name>domain</name> | ||||
|        <value> | ||||
|         <string>%s</string> | ||||
|        </value> | ||||
|       </member> | ||||
|       <member> | ||||
|        <name>type</name> | ||||
|        <value> | ||||
|         <string>TXT</string> | ||||
|        </value> | ||||
|       </member> | ||||
|       <member> | ||||
|        <name>name</name> | ||||
|        <value> | ||||
|         <string>%s</string> | ||||
|        </value> | ||||
|       </member> | ||||
|      </struct> | ||||
|     </value> | ||||
|    </param> | ||||
|   </params> | ||||
|   </methodCall>' "$_domain" "$_sub_domain") | ||||
|   response="$(_post "$xml_content" "$INWX_Api" "" "POST")" | ||||
| 
 | ||||
|   if ! printf "%s" "$response" | grep "Command completed successfully" >/dev/null; then | ||||
|     _err "Error could net get txt records" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   if ! printf "%s" "$response" | grep "count" >/dev/null; then | ||||
|     _info "Adding record" | ||||
|     _inwx_add_record "$_domain" "$_sub_domain" "$txtvalue" | ||||
|   else | ||||
|     _record_id=$(printf '%s' "$response" | _egrep_o '.*(<member><name>record){1}(.*)([0-9]+){1}' | _egrep_o '<name>id<\/name><value><int>[0-9]+' | _egrep_o '[0-9]+') | ||||
|     _info "Updating record" | ||||
|     _inwx_update_record "$_record_id" "$txtvalue" | ||||
|   fi | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #fulldomain txtvalue | ||||
| dns_inwx_rm() { | ||||
| 
 | ||||
|   fulldomain=$1 | ||||
|   txtvalue=$2 | ||||
| 
 | ||||
|   INWX_User="${INWX_User:-$(_readaccountconf_mutable INWX_User)}" | ||||
|   INWX_Password="${INWX_Password:-$(_readaccountconf_mutable INWX_Password)}" | ||||
|   if [ -z "$INWX_User" ] || [ -z "$INWX_Password" ]; then | ||||
|     INWX_User="" | ||||
|     INWX_Password="" | ||||
|     _err "You don't specify inwx user and password yet." | ||||
|     _err "Please create you key and try again." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   #save the api key and email to the account conf file. | ||||
|   _saveaccountconf_mutable INWX_User "$INWX_User" | ||||
|   _saveaccountconf_mutable INWX_Password "$INWX_Password" | ||||
| 
 | ||||
|   _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 txt records" | ||||
| 
 | ||||
|   xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?> | ||||
|   <methodCall> | ||||
|   <methodName>nameserver.info</methodName> | ||||
|   <params> | ||||
|    <param> | ||||
|     <value> | ||||
|      <struct> | ||||
|       <member> | ||||
|        <name>domain</name> | ||||
|        <value> | ||||
|         <string>%s</string> | ||||
|        </value> | ||||
|       </member> | ||||
|       <member> | ||||
|        <name>type</name> | ||||
|        <value> | ||||
|         <string>TXT</string> | ||||
|        </value> | ||||
|       </member> | ||||
|       <member> | ||||
|        <name>name</name> | ||||
|        <value> | ||||
|         <string>%s</string> | ||||
|        </value> | ||||
|       </member> | ||||
|      </struct> | ||||
|     </value> | ||||
|    </param> | ||||
|   </params> | ||||
|   </methodCall>' "$_domain" "$_sub_domain") | ||||
|   response="$(_post "$xml_content" "$INWX_Api" "" "POST")" | ||||
| 
 | ||||
|   if ! printf "%s" "$response" | grep "Command completed successfully" >/dev/null; then | ||||
|     _err "Error could not get txt records" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   if ! printf "%s" "$response" | grep "count" >/dev/null; then | ||||
|     _info "Do not need to delete record" | ||||
|   else | ||||
|     _record_id=$(printf '%s' "$response" | _egrep_o '.*(<member><name>record){1}(.*)([0-9]+){1}' | _egrep_o '<name>id<\/name><value><int>[0-9]+' | _egrep_o '[0-9]+') | ||||
|     _info "Deleting record" | ||||
|     _inwx_delete_record "$_record_id" | ||||
|   fi | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| ####################  Private functions below ################################## | ||||
| 
 | ||||
| _inwx_login() { | ||||
| 
 | ||||
|   xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?> | ||||
|   <methodCall> | ||||
|   <methodName>account.login</methodName> | ||||
|   <params> | ||||
|    <param> | ||||
|     <value> | ||||
|      <struct> | ||||
|       <member> | ||||
|        <name>user</name> | ||||
|        <value> | ||||
|         <string>%s</string> | ||||
|        </value> | ||||
|       </member> | ||||
|       <member> | ||||
|        <name>pass</name> | ||||
|        <value> | ||||
|         <string>%s</string> | ||||
|        </value> | ||||
|       </member> | ||||
|      </struct> | ||||
|     </value> | ||||
|    </param> | ||||
|   </params> | ||||
|   </methodCall>' $INWX_User $INWX_Password) | ||||
| 
 | ||||
|   response="$(_post "$xml_content" "$INWX_Api" "" "POST")" | ||||
| 
 | ||||
|   printf "Cookie: %s" "$(grep "domrobot=" "$HTTP_HEADER" | grep "^Set-Cookie:" | _tail_n 1 | _egrep_o 'domrobot=[^;]*;' | tr -d ';')" | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| _get_root() { | ||||
|   domain=$1 | ||||
|   _debug "get root" | ||||
| 
 | ||||
|   domain=$1 | ||||
|   i=2 | ||||
|   p=1 | ||||
| 
 | ||||
|   _H1=$(_inwx_login) | ||||
|   export _H1 | ||||
|   xml_content='<?xml version="1.0" encoding="UTF-8"?> | ||||
|   <methodCall> | ||||
|   <methodName>nameserver.list</methodName> | ||||
|   </methodCall>' | ||||
| 
 | ||||
|   response="$(_post "$xml_content" "$INWX_Api" "" "POST")" | ||||
|   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 _contains "$response" "$h"; then | ||||
|       _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) | ||||
|       _domain="$h" | ||||
|       return 0 | ||||
|     fi | ||||
|     p=$i | ||||
|     i=$(_math "$i" + 1) | ||||
|   done | ||||
|   return 1 | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| _inwx_delete_record() { | ||||
|   record_id=$1 | ||||
|   xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?> | ||||
|   <methodCall> | ||||
|   <methodName>nameserver.deleteRecord</methodName> | ||||
|   <params> | ||||
|    <param> | ||||
|     <value> | ||||
|      <struct> | ||||
|       <member> | ||||
|        <name>id</name> | ||||
|        <value> | ||||
|         <int>%s</int> | ||||
|        </value> | ||||
|       </member> | ||||
|      </struct> | ||||
|     </value> | ||||
|    </param> | ||||
|   </params> | ||||
|   </methodCall>' "$record_id") | ||||
| 
 | ||||
|   response="$(_post "$xml_content" "$INWX_Api" "" "POST")" | ||||
| 
 | ||||
|   if ! printf "%s" "$response" | grep "Command completed successfully" >/dev/null; then | ||||
|     _err "Error" | ||||
|     return 1 | ||||
|   fi | ||||
|   return 0 | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| _inwx_update_record() { | ||||
|   record_id=$1 | ||||
|   txtval=$2 | ||||
|   xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?> | ||||
|   <methodCall> | ||||
|   <methodName>nameserver.updateRecord</methodName> | ||||
|   <params> | ||||
|    <param> | ||||
|     <value> | ||||
|      <struct> | ||||
|       <member> | ||||
|        <name>content</name> | ||||
|        <value> | ||||
|         <string>%s</string> | ||||
|        </value> | ||||
|       </member> | ||||
|       <member> | ||||
|        <name>id</name> | ||||
|        <value> | ||||
|         <int>%s</int> | ||||
|        </value> | ||||
|       </member> | ||||
|      </struct> | ||||
|     </value> | ||||
|    </param> | ||||
|   </params> | ||||
|   </methodCall>' "$txtval" "$record_id") | ||||
| 
 | ||||
|   response="$(_post "$xml_content" "$INWX_Api" "" "POST")" | ||||
| 
 | ||||
|   if ! printf "%s" "$response" | grep "Command completed successfully" >/dev/null; then | ||||
|     _err "Error" | ||||
|     return 1 | ||||
|   fi | ||||
|   return 0 | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| _inwx_add_record() { | ||||
| 
 | ||||
|   domain=$1 | ||||
|   sub_domain=$2 | ||||
|   txtval=$3 | ||||
| 
 | ||||
|   xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?> | ||||
|   <methodCall> | ||||
|   <methodName>nameserver.createRecord</methodName> | ||||
|   <params> | ||||
|    <param> | ||||
|     <value> | ||||
|      <struct> | ||||
|       <member> | ||||
|        <name>domain</name> | ||||
|        <value> | ||||
|         <string>%s</string> | ||||
|        </value> | ||||
|       </member> | ||||
|       <member> | ||||
|        <name>type</name> | ||||
|        <value> | ||||
|         <string>TXT</string> | ||||
|        </value> | ||||
|       </member> | ||||
|       <member> | ||||
|        <name>content</name> | ||||
|        <value> | ||||
|         <string>%s</string> | ||||
|        </value> | ||||
|       </member> | ||||
|       <member> | ||||
|        <name>name</name> | ||||
|        <value> | ||||
|         <string>%s</string> | ||||
|        </value> | ||||
|       </member> | ||||
|      </struct> | ||||
|     </value> | ||||
|    </param> | ||||
|   </params> | ||||
|   </methodCall>' "$domain" "$txtval" "$sub_domain") | ||||
| 
 | ||||
|   response="$(_post "$xml_content" "$INWX_Api" "" "POST")" | ||||
| 
 | ||||
|   if ! printf "%s" "$response" | grep "Command completed successfully" >/dev/null; then | ||||
|     _err "Error" | ||||
|     return 1 | ||||
|   fi | ||||
|   return 0 | ||||
| } | ||||
| @ -30,7 +30,7 @@ dns_ispconfig_rm() { | ||||
|   _ISPC_credentials && _ISPC_login && _ISPC_rmTxt | ||||
| } | ||||
| 
 | ||||
| ####################  Private functions bellow ################################## | ||||
| ####################  Private functions below ################################## | ||||
| 
 | ||||
| _ISPC_credentials() { | ||||
|   if [ -z "${ISPC_User}" ] || [ -z "$ISPC_Password" ] || [ -z "${ISPC_Api}" ] || [ -z "${ISPC_Api_Insecure}" ]; then | ||||
| @ -46,7 +46,7 @@ _ISPC_credentials() { | ||||
|     _saveaccountconf ISPC_Api "${ISPC_Api}" | ||||
|     _saveaccountconf ISPC_Api_Insecure "${ISPC_Api_Insecure}" | ||||
|     # Set whether curl should use secure or insecure mode | ||||
|     HTTPS_INSECURE="${ISPC_Api_Insecure}" | ||||
|     export HTTPS_INSECURE="${ISPC_Api_Insecure}" | ||||
|   fi | ||||
| } | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										95
									
								
								dnsapi/dns_knot.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								dnsapi/dns_knot.sh
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,95 @@ | ||||
| #!/usr/bin/env sh | ||||
| 
 | ||||
| ########  Public functions ##################### | ||||
| 
 | ||||
| #Usage: dns_knot_add   _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" | ||||
| dns_knot_add() { | ||||
|   fulldomain=$1 | ||||
|   txtvalue=$2 | ||||
|   _checkKey || return 1 | ||||
|   [ -n "${KNOT_SERVER}" ] || KNOT_SERVER="localhost" | ||||
|   # save the dns server and key to the account.conf file. | ||||
|   _saveaccountconf KNOT_SERVER "${KNOT_SERVER}" | ||||
|   _saveaccountconf KNOT_KEY "${KNOT_KEY}" | ||||
| 
 | ||||
|   if ! _get_root "$fulldomain"; then | ||||
|     _err "Domain does not exist." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _info "Adding ${fulldomain}. 60 TXT \"${txtvalue}\"" | ||||
| 
 | ||||
|   knsupdate -y "${KNOT_KEY}" <<EOF | ||||
| server ${KNOT_SERVER} | ||||
| zone ${_domain}. | ||||
| update add ${fulldomain}. 60 TXT "${txtvalue}" | ||||
| send | ||||
| quit | ||||
| EOF | ||||
| 
 | ||||
|   if [ $? -ne 0 ]; then | ||||
|     _err "Error updating domain." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _info "Domain TXT record successfully added." | ||||
|   return 0 | ||||
| } | ||||
| 
 | ||||
| #Usage: dns_knot_rm   _acme-challenge.www.domain.com | ||||
| dns_knot_rm() { | ||||
|   fulldomain=$1 | ||||
|   _checkKey || return 1 | ||||
|   [ -n "${KNOT_SERVER}" ] || KNOT_SERVER="localhost" | ||||
| 
 | ||||
|   if ! _get_root "$fulldomain"; then | ||||
|     _err "Domain does not exist." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _info "Removing ${fulldomain}. TXT" | ||||
| 
 | ||||
|   knsupdate -y "${KNOT_KEY}" <<EOF | ||||
| server ${KNOT_SERVER} | ||||
| zone ${_domain}. | ||||
| update del ${fulldomain}. TXT | ||||
| send | ||||
| quit | ||||
| EOF | ||||
| 
 | ||||
|   if [ $? -ne 0 ]; then | ||||
|     _err "error updating domain" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _info "Domain TXT record successfully deleted." | ||||
|   return 0 | ||||
| } | ||||
| 
 | ||||
| ####################  Private functions below ################################## | ||||
| # _acme-challenge.www.domain.com | ||||
| # returns | ||||
| # _domain=domain.com | ||||
| _get_root() { | ||||
|   domain=$1 | ||||
|   i="$(echo "$fulldomain" | tr '.' ' ' | wc -w)" | ||||
|   i=$(_math "$i" - 1) | ||||
| 
 | ||||
|   while true; do | ||||
|     h=$(printf "%s" "$domain" | cut -d . -f "$i"-100) | ||||
|     if [ -z "$h" ]; then | ||||
|       return 1 | ||||
|     fi | ||||
|     _domain="$h" | ||||
|     return 0 | ||||
|   done | ||||
|   _debug "$domain not found" | ||||
|   return 1 | ||||
| } | ||||
| 
 | ||||
| _checkKey() { | ||||
|   if [ -z "${KNOT_KEY}" ]; then | ||||
|     _err "You must specify a TSIG key to authenticate the request." | ||||
|     return 1 | ||||
|   fi | ||||
| } | ||||
| @ -2,7 +2,7 @@ | ||||
| 
 | ||||
| # dns api wrapper of lexicon for acme.sh | ||||
| 
 | ||||
| lexicon_url="https://github.com/AnalogJ/lexicon" | ||||
| # https://github.com/AnalogJ/lexicon | ||||
| lexicon_cmd="lexicon" | ||||
| 
 | ||||
| wiki="https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api" | ||||
| @ -30,33 +30,38 @@ dns_lexicon_add() { | ||||
|   _savedomainconf PROVIDER "$PROVIDER" | ||||
|   export PROVIDER | ||||
| 
 | ||||
|   Lx_name=$(echo LEXICON_"${PROVIDER}"_USERNAME | tr '[a-z]' '[A-Z]') | ||||
|   # e.g. busybox-ash does not know [:upper:] | ||||
|   # shellcheck disable=SC2018,SC2019 | ||||
|   Lx_name=$(echo LEXICON_"${PROVIDER}"_USERNAME | tr 'a-z' 'A-Z') | ||||
|   Lx_name_v=$(eval echo \$"$Lx_name") | ||||
|   _debug "$Lx_name" "$Lx_name_v" | ||||
|   _secure_debug "$Lx_name" "$Lx_name_v" | ||||
|   if [ "$Lx_name_v" ]; then | ||||
|     _saveaccountconf "$Lx_name" "$Lx_name_v" | ||||
|     eval export "$Lx_name" | ||||
|   fi | ||||
| 
 | ||||
|   Lx_token=$(echo LEXICON_"${PROVIDER}"_TOKEN | tr '[a-z]' '[A-Z]') | ||||
|   # shellcheck disable=SC2018,SC2019 | ||||
|   Lx_token=$(echo LEXICON_"${PROVIDER}"_TOKEN | tr 'a-z' 'A-Z') | ||||
|   Lx_token_v=$(eval echo \$"$Lx_token") | ||||
|   _debug "$Lx_token" "$Lx_token_v" | ||||
|   _secure_debug "$Lx_token" "$Lx_token_v" | ||||
|   if [ "$Lx_token_v" ]; then | ||||
|     _saveaccountconf "$Lx_token" "$Lx_token_v" | ||||
|     eval export "$Lx_token" | ||||
|   fi | ||||
| 
 | ||||
|   Lx_password=$(echo LEXICON_"${PROVIDER}"_PASSWORD | tr '[a-z]' '[A-Z]') | ||||
|   # shellcheck disable=SC2018,SC2019 | ||||
|   Lx_password=$(echo LEXICON_"${PROVIDER}"_PASSWORD | tr 'a-z' 'A-Z') | ||||
|   Lx_password_v=$(eval echo \$"$Lx_password") | ||||
|   _debug "$Lx_password" "$Lx_password_v" | ||||
|   _secure_debug "$Lx_password" "$Lx_password_v" | ||||
|   if [ "$Lx_password_v" ]; then | ||||
|     _saveaccountconf "$Lx_password" "$Lx_password_v" | ||||
|     eval export "$Lx_password" | ||||
|   fi | ||||
| 
 | ||||
|   Lx_domaintoken=$(echo LEXICON_"${PROVIDER}"_DOMAINTOKEN | tr '[a-z]' '[A-Z]') | ||||
|   # shellcheck disable=SC2018,SC2019 | ||||
|   Lx_domaintoken=$(echo LEXICON_"${PROVIDER}"_DOMAINTOKEN | tr 'a-z' 'A-Z') | ||||
|   Lx_domaintoken_v=$(eval echo \$"$Lx_domaintoken") | ||||
|   _debug "$Lx_domaintoken" "$Lx_domaintoken_v" | ||||
|   _secure_debug "$Lx_domaintoken" "$Lx_domaintoken_v" | ||||
|   if [ "$Lx_domaintoken_v" ]; then | ||||
|     eval export "$Lx_domaintoken" | ||||
|     _saveaccountconf "$Lx_domaintoken" "$Lx_domaintoken_v" | ||||
|  | ||||
							
								
								
									
										183
									
								
								dnsapi/dns_linode.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										183
									
								
								dnsapi/dns_linode.sh
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,183 @@ | ||||
| #!/usr/bin/env sh | ||||
| 
 | ||||
| #Author: Philipp Grosswiler <philipp.grosswiler@swiss-design.net> | ||||
| 
 | ||||
| LINODE_API_URL="https://api.linode.com/?api_key=$LINODE_API_KEY&api_action=" | ||||
| 
 | ||||
| ########  Public functions ##################### | ||||
| 
 | ||||
| #Usage: dns_linode_add   _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" | ||||
| dns_linode_add() { | ||||
|   fulldomain="${1}" | ||||
|   txtvalue="${2}" | ||||
| 
 | ||||
|   if ! _Linode_API; then | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _info "Using Linode" | ||||
|   _debug "Calling: dns_linode_add() '${fulldomain}' '${txtvalue}'" | ||||
| 
 | ||||
|   _debug "First detect the root zone" | ||||
|   if ! _get_root "$fulldomain"; then | ||||
|     _err "Domain does not exist." | ||||
|     return 1 | ||||
|   fi | ||||
|   _debug _domain_id "$_domain_id" | ||||
|   _debug _sub_domain "$_sub_domain" | ||||
|   _debug _domain "$_domain" | ||||
| 
 | ||||
|   _parameters="&DomainID=$_domain_id&Type=TXT&Name=$_sub_domain&Target=$txtvalue" | ||||
| 
 | ||||
|   if _rest GET "domain.resource.create" "$_parameters" && [ -n "$response" ]; then | ||||
|     _resource_id=$(printf "%s\n" "$response" | _egrep_o "\"ResourceID\":\s*[0-9]+" | cut -d : -f 2 | tr -d " " | _head_n 1) | ||||
|     _debug _resource_id "$_resource_id" | ||||
| 
 | ||||
|     if [ -z "$_resource_id" ]; then | ||||
|       _err "Error adding the domain resource." | ||||
|       return 1 | ||||
|     fi | ||||
| 
 | ||||
|     _info "Domain resource successfully added." | ||||
|     return 0 | ||||
|   fi | ||||
| 
 | ||||
|   return 1 | ||||
| } | ||||
| 
 | ||||
| #Usage: dns_linode_rm   _acme-challenge.www.domain.com | ||||
| dns_linode_rm() { | ||||
|   fulldomain="${1}" | ||||
| 
 | ||||
|   if ! _Linode_API; then | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _info "Using Linode" | ||||
|   _debug "Calling: dns_linode_rm() '${fulldomain}'" | ||||
| 
 | ||||
|   _debug "First detect the root zone" | ||||
|   if ! _get_root "$fulldomain"; then | ||||
|     _err "Domain does not exist." | ||||
|     return 1 | ||||
|   fi | ||||
|   _debug _domain_id "$_domain_id" | ||||
|   _debug _sub_domain "$_sub_domain" | ||||
|   _debug _domain "$_domain" | ||||
| 
 | ||||
|   _parameters="&DomainID=$_domain_id" | ||||
| 
 | ||||
|   if _rest GET "domain.resource.list" "$_parameters" && [ -n "$response" ]; then | ||||
|     response="$(echo "$response" | tr -d "\n" | tr '{' "|" | sed 's/|/&{/g' | tr "|" "\n")" | ||||
| 
 | ||||
|     resource="$(echo "$response" | _egrep_o "{.*\"NAME\":\s*\"$_sub_domain\".*}")" | ||||
|     if [ "$resource" ]; then | ||||
|       _resource_id=$(printf "%s\n" "$resource" | _egrep_o "\"RESOURCEID\":\s*[0-9]+" | _head_n 1 | cut -d : -f 2 | tr -d \ ) | ||||
|       if [ "$_resource_id" ]; then | ||||
|         _debug _resource_id "$_resource_id" | ||||
| 
 | ||||
|         _parameters="&DomainID=$_domain_id&ResourceID=$_resource_id" | ||||
| 
 | ||||
|         if _rest GET "domain.resource.delete" "$_parameters" && [ -n "$response" ]; then | ||||
|           _resource_id=$(printf "%s\n" "$response" | _egrep_o "\"ResourceID\":\s*[0-9]+" | cut -d : -f 2 | tr -d " " | _head_n 1) | ||||
|           _debug _resource_id "$_resource_id" | ||||
| 
 | ||||
|           if [ -z "$_resource_id" ]; then | ||||
|             _err "Error deleting the domain resource." | ||||
|             return 1 | ||||
|           fi | ||||
| 
 | ||||
|           _info "Domain resource successfully deleted." | ||||
|           return 0 | ||||
|         fi | ||||
|       fi | ||||
| 
 | ||||
|       return 1 | ||||
|     fi | ||||
| 
 | ||||
|     return 0 | ||||
|   fi | ||||
| 
 | ||||
|   return 1 | ||||
| } | ||||
| 
 | ||||
| ####################  Private functions below ################################## | ||||
| 
 | ||||
| _Linode_API() { | ||||
|   if [ -z "$LINODE_API_KEY" ]; then | ||||
|     LINODE_API_KEY="" | ||||
| 
 | ||||
|     _err "You didn't specify the Linode API key yet." | ||||
|     _err "Please create your key and try again." | ||||
| 
 | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _saveaccountconf LINODE_API_KEY "$LINODE_API_KEY" | ||||
| } | ||||
| 
 | ||||
| ####################  Private functions below ################################## | ||||
| #_acme-challenge.www.domain.com | ||||
| #returns | ||||
| # _sub_domain=_acme-challenge.www | ||||
| # _domain=domain.com | ||||
| # _domain_id=12345 | ||||
| _get_root() { | ||||
|   domain=$1 | ||||
|   i=2 | ||||
|   p=1 | ||||
| 
 | ||||
|   if _rest GET "domain.list"; then | ||||
|     response="$(echo "$response" | tr -d "\n" | tr '{' "|" | sed 's/|/&{/g' | tr "|" "\n")" | ||||
|     while true; do | ||||
|       h=$(printf "%s" "$domain" | cut -d . -f $i-100) | ||||
|       _debug h "$h" | ||||
|       if [ -z "$h" ]; then | ||||
|         #not valid | ||||
|         return 1 | ||||
|       fi | ||||
| 
 | ||||
|       hostedzone="$(echo "$response" | _egrep_o "{.*\"DOMAIN\":\s*\"$h\".*}")" | ||||
|       if [ "$hostedzone" ]; then | ||||
|         _domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o "\"DOMAINID\":\s*[0-9]+" | _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 | ||||
|   fi | ||||
|   return 1 | ||||
| } | ||||
| 
 | ||||
| #method method action data | ||||
| _rest() { | ||||
|   mtd="$1" | ||||
|   ep="$2" | ||||
|   data="$3" | ||||
| 
 | ||||
|   _debug mtd "$mtd" | ||||
|   _debug ep "$ep" | ||||
| 
 | ||||
|   export _H1="Accept: application/json" | ||||
|   export _H2="Content-Type: application/json" | ||||
| 
 | ||||
|   if [ "$mtd" != "GET" ]; then | ||||
|     # both POST and DELETE. | ||||
|     _debug data "$data" | ||||
|     response="$(_post "$data" "$LINODE_API_URL$ep" "" "$mtd")" | ||||
|   else | ||||
|     response="$(_get "$LINODE_API_URL$ep$data")" | ||||
|   fi | ||||
| 
 | ||||
|   if [ "$?" != "0" ]; then | ||||
|     _err "error $ep" | ||||
|     return 1 | ||||
|   fi | ||||
|   _debug2 response "$response" | ||||
|   return 0 | ||||
| } | ||||
| @ -46,12 +46,12 @@ dns_lua_add() { | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   count=$(printf "%s\n" "$response" | _egrep_o "\"name\":\"$fulldomain\"" | wc -l) | ||||
|   count=$(printf "%s\n" "$response" | _egrep_o "\"name\":\"$fulldomain.\",\"type\":\"TXT\"" | wc -l | tr -d " ") | ||||
|   _debug count "$count" | ||||
|   if [ "$count" = "0" ]; then | ||||
|     _info "Adding record" | ||||
|     if _LUA_rest POST "zones/$_domain_id/records" "{\"type\":\"TXT\",\"name\":\"$fulldomain.\",\"content\":\"$txtvalue\",\"ttl\":120}"; then | ||||
|       if printf -- "%s" "$response" | grep "$fulldomain" >/dev/null; then | ||||
|       if _contains "$response" "$fulldomain"; then | ||||
|         _info "Added" | ||||
|         #todo: check if the record takes effect | ||||
|         return 0 | ||||
| @ -63,11 +63,11 @@ dns_lua_add() { | ||||
|     _err "Add txt record error." | ||||
|   else | ||||
|     _info "Updating record" | ||||
|     record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":[^,]*,\"name\":\"$fulldomain.\",\"type\":\"TXT\"" | cut -d: -f2 | cut -d, -f1) | ||||
|     record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":[^,]*,\"name\":\"$fulldomain.\",\"type\":\"TXT\"" | _head_n 1 | cut -d: -f2 | cut -d, -f1) | ||||
|     _debug "record_id" "$record_id" | ||||
| 
 | ||||
|     _LUA_rest PUT "zones/$_domain_id/records/$record_id" "{\"id\":\"$record_id\",\"type\":\"TXT\",\"name\":\"$fulldomain.\",\"content\":\"$txtvalue\",\"zone_id\":\"$_domain_id\",\"ttl\":120}" | ||||
|     if [ "$?" = "0" ]; then | ||||
|     _LUA_rest PUT "zones/$_domain_id/records/$record_id" "{\"id\":$record_id,\"type\":\"TXT\",\"name\":\"$fulldomain.\",\"content\":\"$txtvalue\",\"zone_id\":$_domain_id,\"ttl\":120}" | ||||
|     if [ "$?" = "0" ] && _contains "$response" "updated_at"; then | ||||
|       _info "Updated!" | ||||
|       #todo: check if the record takes effect | ||||
|       return 0 | ||||
| @ -81,10 +81,39 @@ dns_lua_add() { | ||||
| #fulldomain | ||||
| dns_lua_rm() { | ||||
|   fulldomain=$1 | ||||
|   txtvalue=$2 | ||||
|   _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" | ||||
|   _LUA_rest GET "zones/${_domain_id}/records" | ||||
| 
 | ||||
|   count=$(printf "%s\n" "$response" | _egrep_o "\"name\":\"$fulldomain.\",\"type\":\"TXT\"" | wc -l | 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\":[^,]*,\"name\":\"$fulldomain.\",\"type\":\"TXT\"" | _head_n 1 | cut -d: -f2 | cut -d, -f1) | ||||
|     _debug "record_id" "$record_id" | ||||
|     if [ -z "$record_id" ]; then | ||||
|       _err "Can not get record id to remove." | ||||
|       return 1 | ||||
|     fi | ||||
|     if ! _LUA_rest DELETE "/zones/$_domain_id/records/$record_id"; then | ||||
|       _err "Delete record error." | ||||
|       return 1 | ||||
|     fi | ||||
|     _contains "$response" "$record_id" | ||||
|   fi | ||||
| } | ||||
| 
 | ||||
| ####################  Private functions bellow ################################## | ||||
| ####################  Private functions below ################################## | ||||
| #_acme-challenge.www.domain.com | ||||
| #returns | ||||
| # _sub_domain=_acme-challenge.www | ||||
| @ -99,6 +128,7 @@ _get_root() { | ||||
|   fi | ||||
|   while true; do | ||||
|     h=$(printf "%s" "$domain" | cut -d . -f $i-100) | ||||
|     _debug h "$h" | ||||
|     if [ -z "$h" ]; then | ||||
|       #not valid | ||||
|       return 1 | ||||
| @ -106,6 +136,7 @@ _get_root() { | ||||
| 
 | ||||
|     if _contains "$response" "\"name\":\"$h\""; then | ||||
|       _domain_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":[^,]*,\"name\":\"$h\"" | cut -d : -f 2 | cut -d , -f 1) | ||||
|       _debug _domain_id "$_domain_id" | ||||
|       if [ "$_domain_id" ]; then | ||||
|         _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) | ||||
|         _domain="$h" | ||||
| @ -125,9 +156,9 @@ _LUA_rest() { | ||||
|   data="$3" | ||||
|   _debug "$ep" | ||||
| 
 | ||||
|   _H1="Accept: application/json" | ||||
|   _H2="Authorization: Basic $LUA_auth" | ||||
|   if [ "$data" ]; then | ||||
|   export _H1="Accept: application/json" | ||||
|   export _H2="Authorization: Basic $LUA_auth" | ||||
|   if [ "$m" != "GET" ]; then | ||||
|     _debug data "$data" | ||||
|     response="$(_post "$data" "$LUA_Api/$ep" "" "$m")" | ||||
|   else | ||||
|  | ||||
							
								
								
									
										43
									
								
								dnsapi/dns_me.sh
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										43
									
								
								dnsapi/dns_me.sh
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							| @ -78,10 +78,39 @@ dns_me_add() { | ||||
| #fulldomain | ||||
| dns_me_rm() { | ||||
|   fulldomain=$1 | ||||
|   txtvalue=$2 | ||||
|   _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" | ||||
|   _me_rest GET "${_domain_id}/records?recordName=$_sub_domain&type=TXT" | ||||
| 
 | ||||
|   count=$(printf "%s\n" "$response" | _egrep_o "\"totalRecords\":[^,]*" | 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 | 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 ! _me_rest DELETE "$_domain_id/records/$record_id"; then | ||||
|       _err "Delete record error." | ||||
|       return 1 | ||||
|     fi | ||||
|     _contains "$response" '' | ||||
|   fi | ||||
| } | ||||
| 
 | ||||
| ####################  Private functions bellow ################################## | ||||
| ####################  Private functions below ################################## | ||||
| #_acme-challenge.www.domain.com | ||||
| #returns | ||||
| # _sub_domain=_acme-challenge.www | ||||
| @ -103,7 +132,7 @@ _get_root() { | ||||
|     fi | ||||
| 
 | ||||
|     if _contains "$response" "\"name\":\"$h\""; then | ||||
|       _domain_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":[^,]*" | head -n 1 | cut -d : -f 2) | ||||
|       _domain_id=$(printf "%s\n" "$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" | ||||
| @ -124,13 +153,13 @@ _me_rest() { | ||||
|   _debug "$ep" | ||||
| 
 | ||||
|   cdate=$(date -u +"%a, %d %b %Y %T %Z") | ||||
|   hmac=$(printf "%s" "$cdate" | _hmac sha1 "$(_hex "$ME_Secret")" hex) | ||||
|   hmac=$(printf "%s" "$cdate" | _hmac sha1 "$(printf "%s" "$ME_Secret" | _hex_dump | tr -d " ")" hex) | ||||
| 
 | ||||
|   _H1="x-dnsme-apiKey: $ME_Key" | ||||
|   _H2="x-dnsme-requestDate: $cdate" | ||||
|   _H3="x-dnsme-hmac: $hmac" | ||||
|   export _H1="x-dnsme-apiKey: $ME_Key" | ||||
|   export _H2="x-dnsme-requestDate: $cdate" | ||||
|   export _H3="x-dnsme-hmac: $hmac" | ||||
| 
 | ||||
|   if [ "$data" ]; then | ||||
|   if [ "$m" != "GET" ]; then | ||||
|     _debug data "$data" | ||||
|     response="$(_post "$data" "$ME_Api/$ep" "" "$m")" | ||||
|   else | ||||
|  | ||||
| @ -5,48 +5,31 @@ | ||||
| #So, here must be a method   dns_myapi_add() | ||||
| #Which will be called by acme.sh to add the txt record to your api system. | ||||
| #returns 0 means success, otherwise error. | ||||
| 
 | ||||
| # | ||||
| #Author: Neilpang | ||||
| #Report Bugs here: https://github.com/Neilpang/acme.sh | ||||
| # | ||||
| ########  Public functions ##################### | ||||
| 
 | ||||
| #Usage: dns_myapi_add   _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" | ||||
| dns_myapi_add() { | ||||
|   fulldomain=$1 | ||||
|   txtvalue=$2 | ||||
|   _info "Using myapi" | ||||
|   _debug fulldomain "$fulldomain" | ||||
|   _debug txtvalue "$txtvalue" | ||||
|   _err "Not implemented!" | ||||
|   return 1 | ||||
| } | ||||
| 
 | ||||
| #fulldomain | ||||
| #Usage: fulldomain txtvalue | ||||
| #Remove the txt record after validation. | ||||
| dns_myapi_rm() { | ||||
|   fulldomain=$1 | ||||
| 
 | ||||
|   txtvalue=$2 | ||||
|   _info "Using myapi" | ||||
|   _debug fulldomain "$fulldomain" | ||||
|   _debug txtvalue "$txtvalue" | ||||
| } | ||||
| 
 | ||||
| ####################  Private functions bellow ################################## | ||||
| _info() { | ||||
|   if [ -z "$2" ]; then | ||||
|     echo "[$(date)] $1" | ||||
|   else | ||||
|     echo "[$(date)] $1='$2'" | ||||
|   fi | ||||
| } | ||||
| 
 | ||||
| _err() { | ||||
|   _info "$@" >&2 | ||||
|   return 1 | ||||
| } | ||||
| 
 | ||||
| _debug() { | ||||
|   if [ -z "$DEBUG" ]; then | ||||
|     return | ||||
|   fi | ||||
|   _err "$@" | ||||
|   return 0 | ||||
| } | ||||
| 
 | ||||
| _debug2() { | ||||
|   if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then | ||||
|     _debug "$@" | ||||
|   fi | ||||
|   return | ||||
| } | ||||
| ####################  Private functions below ################################## | ||||
|  | ||||
							
								
								
									
										193
									
								
								dnsapi/dns_namecom.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										193
									
								
								dnsapi/dns_namecom.sh
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,193 @@ | ||||
| #!/usr/bin/env sh | ||||
| 
 | ||||
| #Author: RaidneII | ||||
| #Created 06/28/2017 | ||||
| #Utilize name.com API to finish dns-01 verifications. | ||||
| ########  Public functions ##################### | ||||
| 
 | ||||
| Namecom_API="https://api.name.com/api" | ||||
| 
 | ||||
| #Usage: dns_namecom_add   _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" | ||||
| dns_namecom_add() { | ||||
|   fulldomain=$1 | ||||
|   txtvalue=$2 | ||||
| 
 | ||||
|   # First we need name.com credentials. | ||||
|   if [ -z "$Namecom_Username" ]; then | ||||
|     Namecom_Username="" | ||||
|     _err "Username for name.com is missing." | ||||
|     _err "Please specify that in your environment variable." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   if [ -z "$Namecom_Token" ]; then | ||||
|     Namecom_Token="" | ||||
|     _err "API token for name.com is missing." | ||||
|     _err "Please specify that in your environment variable." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   # Save them in configuration. | ||||
|   _saveaccountconf Namecom_Username "$Namecom_Username" | ||||
|   _saveaccountconf Namecom_Token "$Namecom_Token" | ||||
| 
 | ||||
|   # Login in using API | ||||
|   if ! _namecom_login; then | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   # Find domain in domain list. | ||||
|   if ! _namecom_get_root "$fulldomain"; then | ||||
|     _err "Unable to find domain specified." | ||||
|     _namecom_logout | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   # Add TXT record. | ||||
|   _namecom_addtxt_json="{\"hostname\":\"$_sub_domain\",\"type\":\"TXT\",\"content\":\"$txtvalue\",\"ttl\":\"300\",\"priority\":\"10\"}" | ||||
|   if _namecom_rest POST "dns/create/$_domain" "$_namecom_addtxt_json"; then | ||||
|     retcode=$(printf "%s\n" "$response" | _egrep_o "\"code\":100") | ||||
|     if [ "$retcode" ]; then | ||||
|       _info "Successfully added TXT record, ready for validation." | ||||
|       _namecom_logout | ||||
|       return 0 | ||||
|     else | ||||
|       _err "Unable to add the DNS record." | ||||
|       _namecom_logout | ||||
|       return 1 | ||||
|     fi | ||||
|   fi | ||||
| } | ||||
| 
 | ||||
| #Usage: fulldomain txtvalue | ||||
| #Remove the txt record after validation. | ||||
| dns_namecom_rm() { | ||||
|   fulldomain=$1 | ||||
|   txtvalue=$2 | ||||
| 
 | ||||
|   if ! _namecom_login; then | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   # Find domain in domain list. | ||||
|   if ! _namecom_get_root "$fulldomain"; then | ||||
|     _err "Unable to find domain specified." | ||||
|     _namecom_logout | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   # Get the record id. | ||||
|   if _namecom_rest GET "dns/list/$_domain"; then | ||||
|     retcode=$(printf "%s\n" "$response" | _egrep_o "\"code\":100") | ||||
|     if [ "$retcode" ]; then | ||||
|       _record_id=$(printf "%s\n" "$response" | _egrep_o "\"record_id\":\"[0-9]+\",\"name\":\"$fulldomain\",\"type\":\"TXT\"" | cut -d \" -f 4) | ||||
|       _debug record_id "$_record_id" | ||||
|       _info "Successfully retrieved the record id for ACME challenge." | ||||
|     else | ||||
|       _err "Unable to retrieve the record id." | ||||
|       _namecom_logout | ||||
|       return 1 | ||||
|     fi | ||||
|   fi | ||||
| 
 | ||||
|   # Remove the DNS record using record id. | ||||
|   _namecom_rmtxt_json="{\"record_id\":\"$_record_id\"}" | ||||
|   if _namecom_rest POST "dns/delete/$_domain" "$_namecom_rmtxt_json"; then | ||||
|     retcode=$(printf "%s\n" "$response" | _egrep_o "\"code\":100") | ||||
|     if [ "$retcode" ]; then | ||||
|       _info "Successfully removed the TXT record." | ||||
|       _namecom_logout | ||||
|       return 0 | ||||
|     else | ||||
|       _err "Unable to remove the DNS record." | ||||
|       _namecom_logout | ||||
|       return 1 | ||||
|     fi | ||||
|   fi | ||||
| } | ||||
| 
 | ||||
| ####################  Private functions below ################################## | ||||
| _namecom_rest() { | ||||
|   method=$1 | ||||
|   param=$2 | ||||
|   data=$3 | ||||
| 
 | ||||
|   export _H1="Content-Type: application/json" | ||||
|   export _H2="Api-Session-Token: $sessionkey" | ||||
|   if [ "$method" != "GET" ]; then | ||||
|     response="$(_post "$data" "$Namecom_API/$param" "" "$method")" | ||||
|   else | ||||
|     response="$(_get "$Namecom_API/$param")" | ||||
|   fi | ||||
| 
 | ||||
|   if [ "$?" != "0" ]; then | ||||
|     _err "error $param" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _debug2 response "$response" | ||||
|   return 0 | ||||
| } | ||||
| 
 | ||||
| _namecom_login() { | ||||
|   namecom_login_json="{\"username\":\"$Namecom_Username\",\"api_token\":\"$Namecom_Token\"}" | ||||
| 
 | ||||
|   if _namecom_rest POST "login" "$namecom_login_json"; then | ||||
|     retcode=$(printf "%s\n" "$response" | _egrep_o "\"code\":100") | ||||
|     if [ "$retcode" ]; then | ||||
|       _info "Successfully logged in. Fetching session token..." | ||||
|       sessionkey=$(printf "%s\n" "$response" | _egrep_o "\"session_token\":\".+" | cut -d \" -f 4) | ||||
|       if [ ! -z "$sessionkey" ]; then | ||||
|         _debug sessionkey "$sessionkey" | ||||
|         _info "Session key obtained." | ||||
|       else | ||||
|         _err "Unable to get session key." | ||||
|         return 1 | ||||
|       fi | ||||
|     else | ||||
|       _err "Logging in failed." | ||||
|       return 1 | ||||
|     fi | ||||
|   fi | ||||
| } | ||||
| 
 | ||||
| _namecom_logout() { | ||||
|   if _namecom_rest GET "logout"; then | ||||
|     retcode=$(printf "%s\n" "$response" | _egrep_o "\"code\":100") | ||||
|     if [ "$retcode" ]; then | ||||
|       _info "Successfully logged out." | ||||
|     else | ||||
|       _err "Error logging out." | ||||
|       return 1 | ||||
|     fi | ||||
|   fi | ||||
| } | ||||
| 
 | ||||
| _namecom_get_root() { | ||||
|   domain=$1 | ||||
|   i=2 | ||||
|   p=1 | ||||
| 
 | ||||
|   if ! _namecom_rest GET "domain/list"; then | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   # Need to exclude the last field (tld) | ||||
|   numfields=$(echo "$domain" | _egrep_o "\." | wc -l) | ||||
|   while [ $i -le "$numfields" ]; do | ||||
|     host=$(printf "%s" "$domain" | cut -d . -f $i-100) | ||||
|     _debug host "$host" | ||||
|     if [ -z "$host" ]; then | ||||
|       return 1 | ||||
|     fi | ||||
| 
 | ||||
|     if _contains "$response" "$host"; then | ||||
|       _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) | ||||
|       _domain="$host" | ||||
|       return 0 | ||||
|     fi | ||||
|     p=$i | ||||
|     i=$(_math "$i" + 1) | ||||
|   done | ||||
|   return 1 | ||||
| } | ||||
							
								
								
									
										158
									
								
								dnsapi/dns_nsone.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										158
									
								
								dnsapi/dns_nsone.sh
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,158 @@ | ||||
| #!/usr/bin/env sh | ||||
| 
 | ||||
| # bug reports to dev@1e.ca | ||||
| 
 | ||||
| # | ||||
| #NS1_Key="sdfsdfsdfljlbjkljlkjsdfoiwje" | ||||
| # | ||||
| 
 | ||||
| NS1_Api="https://api.nsone.net/v1" | ||||
| 
 | ||||
| ########  Public functions ##################### | ||||
| 
 | ||||
| #Usage: add  _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" | ||||
| dns_nsone_add() { | ||||
|   fulldomain=$1 | ||||
|   txtvalue=$2 | ||||
| 
 | ||||
|   if [ -z "$NS1_Key" ]; then | ||||
|     NS1_Key="" | ||||
|     _err "You didn't specify nsone dns api key yet." | ||||
|     _err "Please create you key and try again." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   #save the api key and email to the account conf file. | ||||
|   _saveaccountconf NS1_Key "$NS1_Key" | ||||
| 
 | ||||
|   _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 txt records" | ||||
|   _nsone_rest GET "zones/${_domain}" | ||||
| 
 | ||||
|   if ! _contains "$response" "\"records\":"; then | ||||
|     _err "Error" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   count=$(printf "%s\n" "$response" | _egrep_o "\"domain\":\"$fulldomain\",[^{]*\"type\":\"TXT\"" | wc -l | tr -d " ") | ||||
|   _debug count "$count" | ||||
|   if [ "$count" = "0" ]; then | ||||
|     _info "Adding record" | ||||
| 
 | ||||
|     if _nsone_rest PUT "zones/$_domain/$fulldomain/TXT" "{\"answers\":[{\"answer\":[\"$txtvalue\"]}],\"type\":\"TXT\",\"domain\":\"$fulldomain\",\"zone\":\"$_domain\"}"; then | ||||
|       if _contains "$response" "$fulldomain"; then | ||||
|         _info "Added" | ||||
|         #todo: check if the record takes effect | ||||
|         return 0 | ||||
|       else | ||||
|         _err "Add txt record error." | ||||
|         return 1 | ||||
|       fi | ||||
|     fi | ||||
|     _err "Add txt record error." | ||||
|   else | ||||
|     _info "Updating record" | ||||
|     record_id=$(printf "%s\n" "$response" | _egrep_o "\"domain\":\"$fulldomain.\",[^{]*\"type\":\"TXT\",\"id\":\"[^,]*\"" | _head_n 1 | cut -d: -f7 | cut -d, -f1) | ||||
|     _debug "record_id" "$record_id" | ||||
| 
 | ||||
|     _nsone_rest POST "zones/$_domain/$fulldomain/TXT" "{\"answers\": [{\"answer\": [\"$txtvalue\"]}],\"type\": \"TXT\",\"domain\":\"$fulldomain\",\"zone\": \"$_domain\"}" | ||||
|     if [ "$?" = "0" ] && _contains "$response" "$fulldomain"; then | ||||
|       _info "Updated!" | ||||
|       #todo: check if the record takes effect | ||||
|       return 0 | ||||
|     fi | ||||
|     _err "Update error" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #fulldomain | ||||
| dns_nsone_rm() { | ||||
|   fulldomain=$1 | ||||
|   txtvalue=$2 | ||||
|   _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 txt records" | ||||
|   _nsone_rest GET "zones/${_domain}/$fulldomain/TXT" | ||||
| 
 | ||||
|   count=$(printf "%s\n" "$response" | _egrep_o "\"domain\":\"$fulldomain\",.*\"type\":\"TXT\"" | wc -l | tr -d " ") | ||||
|   _debug count "$count" | ||||
|   if [ "$count" = "0" ]; then | ||||
|     _info "Don't need to remove." | ||||
|   else | ||||
|     if ! _nsone_rest DELETE "zones/${_domain}/$fulldomain/TXT"; then | ||||
|       _err "Delete record error." | ||||
|       return 1 | ||||
|     fi | ||||
|     _contains "$response" "" | ||||
|   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=2 | ||||
|   p=1 | ||||
|   if ! _nsone_rest GET "zones"; then | ||||
|     return 1 | ||||
|   fi | ||||
|   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 _contains "$response" "\"zone\":\"$h\""; then | ||||
|       _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) | ||||
|       _domain="$h" | ||||
|       return 0 | ||||
|     fi | ||||
|     p=$i | ||||
|     i=$(_math "$i" + 1) | ||||
|   done | ||||
|   return 1 | ||||
| } | ||||
| 
 | ||||
| _nsone_rest() { | ||||
|   m=$1 | ||||
|   ep="$2" | ||||
|   data="$3" | ||||
|   _debug "$ep" | ||||
| 
 | ||||
|   export _H1="Accept: application/json" | ||||
|   export _H2="X-NSONE-Key: $NS1_Key" | ||||
|   if [ "$m" != "GET" ]; then | ||||
|     _debug data "$data" | ||||
|     response="$(_post "$data" "$NS1_Api/$ep" "" "$m")" | ||||
|   else | ||||
|     response="$(_get "$NS1_Api/$ep")" | ||||
|   fi | ||||
| 
 | ||||
|   if [ "$?" != "0" ]; then | ||||
|     _err "error $ep" | ||||
|     return 1 | ||||
|   fi | ||||
|   _debug2 response "$response" | ||||
|   return 0 | ||||
| } | ||||
| @ -44,7 +44,7 @@ EOF | ||||
|   return 0 | ||||
| } | ||||
| 
 | ||||
| ####################  Private functions bellow ################################## | ||||
| ####################  Private functions below ################################## | ||||
| 
 | ||||
| _checkKeyFile() { | ||||
|   if [ -z "${NSUPDATE_KEY}" ]; then | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| #!/usr/bin/env sh | ||||
| 
 | ||||
| #Applcation Key | ||||
| #Application Key | ||||
| #OVH_AK="sdfsdfsdfljlbjkljlkjsdfoiwje" | ||||
| # | ||||
| #Application Secret | ||||
| @ -119,7 +119,7 @@ dns_ovh_add() { | ||||
| 
 | ||||
|   _info "Checking authentication" | ||||
| 
 | ||||
|   response="$(_ovh_rest GET "domain/")" | ||||
|   response="$(_ovh_rest GET "domain")" | ||||
|   if _contains "$response" "INVALID_CREDENTIAL"; then | ||||
|     _err "The consumer key is invalid: $OVH_CK" | ||||
|     _err "Please retry to create a new one." | ||||
| @ -182,7 +182,7 @@ dns_ovh_rm() { | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| ####################  Private functions bellow ################################## | ||||
| ####################  Private functions below ################################## | ||||
| 
 | ||||
| _ovh_authentication() { | ||||
| 
 | ||||
| @ -191,7 +191,7 @@ _ovh_authentication() { | ||||
|   _H3="" | ||||
|   _H4="" | ||||
| 
 | ||||
|   _ovhdata='{"accessRules": [{"method": "GET","path": "/*"},{"method": "POST","path": "/*"},{"method": "PUT","path": "/*"},{"method": "DELETE","path": "/*"}],"redirection":"'$ovh_success'"}' | ||||
|   _ovhdata='{"accessRules": [{"method": "GET","path": "/auth/time"},{"method": "GET","path": "/domain"},{"method": "GET","path": "/domain/zone/*"},{"method": "GET","path": "/domain/zone/*/record"},{"method": "POST","path": "/domain/zone/*/record"},{"method": "POST","path": "/domain/zone/*/refresh"},{"method": "PUT","path": "/domain/zone/*/record/*"}],"redirection":"'$ovh_success'"}' | ||||
| 
 | ||||
|   response="$(_post "$_ovhdata" "$OVH_API/auth/credential")" | ||||
|   _debug3 response "$response" | ||||
| @ -207,7 +207,7 @@ _ovh_authentication() { | ||||
|     _err "Unable to get consumerKey" | ||||
|     return 1 | ||||
|   fi | ||||
|   _debug consumerKey "$consumerKey" | ||||
|   _secure_debug consumerKey "$consumerKey" | ||||
| 
 | ||||
|   OVH_CK="$consumerKey" | ||||
|   _saveaccountconf OVH_CK "$OVH_CK" | ||||
| @ -238,7 +238,7 @@ _get_root() { | ||||
|       return 1 | ||||
|     fi | ||||
| 
 | ||||
|     if ! _contains "$response" "This service does not exist" >/dev/null; then | ||||
|     if ! _contains "$response" "This service does not exist" >/dev/null && ! _contains "$response" "NOT_GRANTED_CALL" >/dev/null; then | ||||
|       _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) | ||||
|       _domain="$h" | ||||
|       return 0 | ||||
| @ -269,16 +269,16 @@ _ovh_rest() { | ||||
|   _ovh_t="$(_ovh_timestamp)" | ||||
|   _debug2 _ovh_t "$_ovh_t" | ||||
|   _ovh_p="$OVH_AS+$OVH_CK+$m+$_ovh_url+$data+$_ovh_t" | ||||
|   _debug _ovh_p "$_ovh_p" | ||||
|   _secure_debug _ovh_p "$_ovh_p" | ||||
|   _ovh_hex="$(printf "%s" "$_ovh_p" | _digest sha1 hex)" | ||||
|   _debug2 _ovh_hex "$_ovh_hex" | ||||
| 
 | ||||
|   _H1="X-Ovh-Application: $OVH_AK" | ||||
|   _H2="X-Ovh-Signature: \$1\$$_ovh_hex" | ||||
|   export _H1="X-Ovh-Application: $OVH_AK" | ||||
|   export _H2="X-Ovh-Signature: \$1\$$_ovh_hex" | ||||
|   _debug2 _H2 "$_H2" | ||||
|   _H3="X-Ovh-Timestamp: $_ovh_t" | ||||
|   _H4="X-Ovh-Consumer: $OVH_CK" | ||||
|   _H5="Content-Type: application/json;charset=utf-8" | ||||
|   export _H3="X-Ovh-Timestamp: $_ovh_t" | ||||
|   export _H4="X-Ovh-Consumer: $OVH_CK" | ||||
|   export _H5="Content-Type: application/json;charset=utf-8" | ||||
|   if [ "$data" ] || [ "$m" = "POST" ] || [ "$m" = "PUT" ]; then | ||||
|     _debug data "$data" | ||||
|     response="$(_post "$data" "$_ovh_url" "" "$m")" | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| #!/usr/bin/env sh | ||||
| 
 | ||||
| #PowerDNS Emdedded API | ||||
| #PowerDNS Embedded API | ||||
| #https://doc.powerdns.com/md/httpapi/api_spec/ | ||||
| # | ||||
| #PDNS_Url="http://ns.example.com:8081" | ||||
| @ -130,7 +130,7 @@ notify_slaves() { | ||||
|   return 0 | ||||
| } | ||||
| 
 | ||||
| ####################  Private functions bellow ################################## | ||||
| ####################  Private functions below ################################## | ||||
| #_acme-challenge.www.domain.com | ||||
| #returns | ||||
| # _domain=domain.com | ||||
| @ -165,7 +165,7 @@ _pdns_rest() { | ||||
|   ep=$2 | ||||
|   data=$3 | ||||
| 
 | ||||
|   _H1="X-API-Key: $PDNS_Token" | ||||
|   export _H1="X-API-Key: $PDNS_Token" | ||||
| 
 | ||||
|   if [ ! "$method" = "GET" ]; then | ||||
|     _debug data "$data" | ||||
|  | ||||
							
								
								
									
										170
									
								
								dnsapi/dns_servercow.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										170
									
								
								dnsapi/dns_servercow.sh
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,170 @@ | ||||
| #!/usr/bin/env sh | ||||
| 
 | ||||
| ########## | ||||
| # Custom servercow.de DNS API v1 for use with [acme.sh](https://github.com/Neilpang/acme.sh) | ||||
| # | ||||
| # Usage: | ||||
| # export SERVERCOW_API_Username=username | ||||
| # export SERVERCOW_API_Password=password | ||||
| # acme.sh --issue -d example.com --dns dns_servercow | ||||
| # | ||||
| # Issues: | ||||
| # Any issues / questions / suggestions can be posted here: | ||||
| # https://github.com/jhartlep/servercow-dns-api/issues | ||||
| # | ||||
| # Author: Jens Hartlep | ||||
| ########## | ||||
| 
 | ||||
| SERVERCOW_API="https://api.servercow.de/dns/v1/domains" | ||||
| 
 | ||||
| # Usage dns_servercow_add _acme-challenge.www.domain.com "abcdefghijklmnopqrstuvwxyz" | ||||
| dns_servercow_add() { | ||||
|   fulldomain=$1 | ||||
|   txtvalue=$2 | ||||
| 
 | ||||
|   _info "Using servercow" | ||||
|   _debug fulldomain "$fulldomain" | ||||
|   _debug txtvalue "$txtvalue" | ||||
| 
 | ||||
|   SERVERCOW_API_Username="${SERVERCOW_API_Username:-$(_readaccountconf_mutable SERVERCOW_API_Username)}" | ||||
|   SERVERCOW_API_Password="${SERVERCOW_API_Password:-$(_readaccountconf_mutable SERVERCOW_API_Password)}" | ||||
|   if [ -z "$SERVERCOW_API_Username" ] || [ -z "$SERVERCOW_API_Password" ]; then | ||||
|     SERVERCOW_API_Username="" | ||||
|     SERVERCOW_API_Password="" | ||||
|     _err "You don't specify servercow api username and password yet." | ||||
|     _err "Please create your username and password and try again." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   # save the credentials to the account conf file | ||||
|   _saveaccountconf_mutable SERVERCOW_API_Username "$SERVERCOW_API_Username" | ||||
|   _saveaccountconf_mutable SERVERCOW_API_Password "$SERVERCOW_API_Password" | ||||
| 
 | ||||
|   _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" | ||||
| 
 | ||||
|   if _servercow_api POST "$_domain" "{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"ttl\":20}"; then | ||||
|     if printf -- "%s" "$response" | grep "ok" >/dev/null; then | ||||
|       _info "Added, 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_servercow_rm() { | ||||
|   fulldomain=$1 | ||||
|   txtvalue=$2 | ||||
| 
 | ||||
|   _info "Using servercow" | ||||
|   _debug fulldomain "$fulldomain" | ||||
|   _debug txtvalue "$fulldomain" | ||||
| 
 | ||||
|   SERVERCOW_API_Username="${SERVERCOW_API_Username:-$(_readaccountconf_mutable SERVERCOW_API_Username)}" | ||||
|   SERVERCOW_API_Password="${SERVERCOW_API_Password:-$(_readaccountconf_mutable SERVERCOW_API_Password)}" | ||||
|   if [ -z "$SERVERCOW_API_Username" ] || [ -z "$SERVERCOW_API_Password" ]; then | ||||
|     SERVERCOW_API_Username="" | ||||
|     SERVERCOW_API_Password="" | ||||
|     _err "You don't specify servercow api username and password yet." | ||||
|     _err "Please create your username and password and try again." | ||||
|     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" | ||||
| 
 | ||||
|   if _servercow_api DELETE "$_domain" "{\"type\":\"TXT\",\"name\":\"$fulldomain\"}"; then | ||||
|     if printf -- "%s" "$response" | grep "ok" >/dev/null; then | ||||
|       _info "Deleted, OK" | ||||
|       _contains "$response" '"message":"ok"' | ||||
|     else | ||||
|       _err "delete txt record error." | ||||
|       return 1 | ||||
|     fi | ||||
|   fi | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| ####################  Private functions below ################################## | ||||
| 
 | ||||
| # _acme-challenge.www.domain.com | ||||
| # returns | ||||
| #  _sub_domain=_acme-challenge.www | ||||
| #  _domain=domain.com | ||||
| _get_root() { | ||||
|   fulldomain=$1 | ||||
|   i=2 | ||||
|   p=1 | ||||
| 
 | ||||
|   while true; do | ||||
|     _domain=$(printf "%s" "$fulldomain" | cut -d . -f $i-100) | ||||
| 
 | ||||
|     _debug _domain "$_domain" | ||||
|     if [ -z "$_domain" ]; then | ||||
|       # not valid | ||||
|       return 1 | ||||
|     fi | ||||
| 
 | ||||
|     if ! _servercow_api GET "$_domain"; then | ||||
|       return 1 | ||||
|     fi | ||||
| 
 | ||||
|     if ! _contains "$response" '"error":"no such domain in user context"' >/dev/null; then | ||||
|       _sub_domain=$(printf "%s" "$fulldomain" | cut -d . -f 1-$p) | ||||
|       if [ -z "$_sub_domain" ]; then | ||||
|         # not valid | ||||
|         return 1 | ||||
|       fi | ||||
| 
 | ||||
|       return 0 | ||||
|     fi | ||||
| 
 | ||||
|     p=$i | ||||
|     i=$(_math "$i" + 1) | ||||
|   done | ||||
| 
 | ||||
|   return 1 | ||||
| } | ||||
| 
 | ||||
| _servercow_api() { | ||||
|   method=$1 | ||||
|   domain=$2 | ||||
|   data="$3" | ||||
| 
 | ||||
|   export _H1="Content-Type: application/json" | ||||
|   export _H2="X-Auth-Username: $SERVERCOW_API_Username" | ||||
|   export _H3="X-Auth-Password: $SERVERCOW_API_Password" | ||||
| 
 | ||||
|   if [ "$method" != "GET" ]; then | ||||
|     _debug data "$data" | ||||
|     response="$(_post "$data" "$SERVERCOW_API/$domain" "" "$method")" | ||||
|   else | ||||
|     response="$(_get "$SERVERCOW_API/$domain")" | ||||
|   fi | ||||
| 
 | ||||
|   if [ "$?" != "0" ]; then | ||||
|     _err "error $domain" | ||||
|     return 1 | ||||
|   fi | ||||
|   _debug2 response "$response" | ||||
|   return 0 | ||||
| } | ||||
							
								
								
									
										202
									
								
								dnsapi/dns_unoeuro.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										202
									
								
								dnsapi/dns_unoeuro.sh
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,202 @@ | ||||
| #!/usr/bin/env sh | ||||
| 
 | ||||
| # | ||||
| #UNO_Key="sdfsdfsdfljlbjkljlkjsdfoiwje" | ||||
| # | ||||
| #UNO_User="UExxxxxx" | ||||
| 
 | ||||
| Uno_Api="https://api.unoeuro.com/1" | ||||
| 
 | ||||
| ########  Public functions ##################### | ||||
| 
 | ||||
| #Usage: add  _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" | ||||
| dns_unoeuro_add() { | ||||
|   fulldomain=$1 | ||||
|   txtvalue=$2 | ||||
| 
 | ||||
|   UNO_Key="${UNO_Key:-$(_readaccountconf_mutable UNO_Key)}" | ||||
|   UNO_User="${UNO_User:-$(_readaccountconf_mutable UNO_User)}" | ||||
|   if [ -z "$UNO_Key" ] || [ -z "$UNO_User" ]; then | ||||
|     UNO_Key="" | ||||
|     UNO_User="" | ||||
|     _err "You haven't specified a UnoEuro api key and account yet." | ||||
|     _err "Please create your key and try again." | ||||
|     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" | ||||
| 
 | ||||
|   _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" | ||||
|   _uno_rest GET "my/products/$h/dns/records" | ||||
| 
 | ||||
|   if ! _contains "$response" "\"status\": 200" >/dev/null; then | ||||
|     _err "Error" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   if ! _contains "$response" "$_sub_domain" >/dev/null; then | ||||
|     _info "Adding record" | ||||
| 
 | ||||
|     if _uno_rest POST "my/products/$h/dns/records" "{\"name\":\"$fulldomain\",\"type\":\"TXT\",\"data\":\"$txtvalue\",\"ttl\":120}"; then | ||||
|       if _contains "$response" "\"status\": 200" >/dev/null; then | ||||
|         _info "Added, OK" | ||||
|         return 0 | ||||
|       else | ||||
|         _err "Add txt record error." | ||||
|         return 1 | ||||
|       fi | ||||
|     fi | ||||
|     _err "Add txt record error." | ||||
|   else | ||||
|     _info "Updating record" | ||||
|     record_line_number=$(echo "$response" | grep -n "$_sub_domain" | cut -d : -f 1) | ||||
|     record_line_number=$(_math "$record_line_number" - 1) | ||||
|     record_id=$(echo "$response" | _head_n "$record_line_number" | _tail_n 1 1 | _egrep_o "[0-9]{1,}") | ||||
|     _debug "record_id" "$record_id" | ||||
| 
 | ||||
|     _uno_rest PUT "my/products/$h/dns/records/$record_id" "{\"name\":\"$fulldomain\",\"type\":\"TXT\",\"data\":\"$txtvalue\",\"ttl\":120}" | ||||
|     if _contains "$response" "\"status\": 200" >/dev/null; then | ||||
|       _info "Updated, OK" | ||||
|       return 0 | ||||
|     fi | ||||
|     _err "Update error" | ||||
|     return 1 | ||||
|   fi | ||||
| } | ||||
| 
 | ||||
| #fulldomain txtvalue | ||||
| dns_unoeuro_rm() { | ||||
|   fulldomain=$1 | ||||
|   txtvalue=$2 | ||||
| 
 | ||||
|   UNO_Key="${UNO_Key:-$(_readaccountconf_mutable UNO_Key)}" | ||||
|   UNO_User="${UNO_User:-$(_readaccountconf_mutable UNO_User)}" | ||||
|   if [ -z "$UNO_Key" ] || [ -z "$UNO_User" ]; then | ||||
|     UNO_Key="" | ||||
|     UNO_User="" | ||||
|     _err "You haven't specified a UnoEuro api key and account yet." | ||||
|     _err "Please create your key and try again." | ||||
|     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 | ||||
| 
 | ||||
|   _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" | ||||
|   _uno_rest GET "my/products/$h/dns/records" | ||||
| 
 | ||||
|   if ! _contains "$response" "\"status\": 200"; then | ||||
|     _err "Error" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   if ! _contains "$response" "$_sub_domain"; then | ||||
|     _info "Don't need to remove." | ||||
|   else | ||||
|     record_line_number=$(echo "$response" | grep -n "$_sub_domain" | cut -d : -f 1) | ||||
|     record_line_number=$(_math "$record_line_number" - 1) | ||||
|     record_id=$(echo "$response" | _head_n "$record_line_number" | _tail_n 1 1 | _egrep_o "[0-9]{1,}") | ||||
|     _debug "record_id" "$record_id" | ||||
| 
 | ||||
|     if [ -z "$record_id" ]; then | ||||
|       _err "Can not get record id to remove." | ||||
|       return 1 | ||||
|     fi | ||||
| 
 | ||||
|     if ! _uno_rest DELETE "my/products/$h/dns/records/$record_id"; then | ||||
|       _err "Delete record error." | ||||
|       return 1 | ||||
|     fi | ||||
|     _contains "$response" "\"status\": 200" | ||||
|   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=2 | ||||
|   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 ! _uno_rest GET "my/products/$h/dns/records"; then | ||||
|       return 1 | ||||
|     fi | ||||
| 
 | ||||
|     if _contains "$response" "\"status\": 200"; then | ||||
|       _domain_id=$h | ||||
|       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 | ||||
| } | ||||
| 
 | ||||
| _uno_rest() { | ||||
|   m=$1 | ||||
|   ep="$2" | ||||
|   data="$3" | ||||
|   _debug "$ep" | ||||
| 
 | ||||
|   export _H1="Content-Type: application/json" | ||||
| 
 | ||||
|   if [ "$m" != "GET" ]; then | ||||
|     _debug data "$data" | ||||
|     response="$(_post "$data" "$Uno_Api/$UNO_User/$UNO_Key/$ep" "" "$m")" | ||||
|   else | ||||
|     response="$(_get "$Uno_Api/$UNO_User/$UNO_Key/$ep")" | ||||
|   fi | ||||
| 
 | ||||
|   if [ "$?" != "0" ]; then | ||||
|     _err "error $ep" | ||||
|     return 1 | ||||
|   fi | ||||
|   _debug2 response "$response" | ||||
|   return 0 | ||||
| } | ||||
							
								
								
									
										149
									
								
								dnsapi/dns_vscale.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										149
									
								
								dnsapi/dns_vscale.sh
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,149 @@ | ||||
| #!/usr/bin/env sh | ||||
| 
 | ||||
| #This is the vscale.io api wrapper for acme.sh | ||||
| # | ||||
| #Author: Alex Loban | ||||
| #Report Bugs here: https://github.com/LAV45/acme.sh | ||||
| 
 | ||||
| #VSCALE_API_KEY="sdfsdfsdfljlbjkljlkjsdfoiwje" | ||||
| VSCALE_API_URL="https://api.vscale.io/v1" | ||||
| 
 | ||||
| ########  Public functions ##################### | ||||
| 
 | ||||
| #Usage: dns_myapi_add   _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" | ||||
| dns_vscale_add() { | ||||
|   fulldomain=$1 | ||||
|   txtvalue=$2 | ||||
| 
 | ||||
|   if [ -z "$VSCALE_API_KEY" ]; then | ||||
|     VSCALE_API_KEY="" | ||||
|     _err "You didn't specify the VSCALE api key yet." | ||||
|     _err "Please create you key and try again." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _saveaccountconf VSCALE_API_KEY "$VSCALE_API_KEY" | ||||
| 
 | ||||
|   _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" | ||||
| 
 | ||||
|   _vscale_tmpl_json="{\"type\":\"TXT\",\"name\":\"$_sub_domain.$_domain\",\"content\":\"$txtvalue\"}" | ||||
| 
 | ||||
|   if _vscale_rest POST "domains/$_domain_id/records/" "$_vscale_tmpl_json"; then | ||||
|     response=$(printf "%s\n" "$response" | _egrep_o "{\"error\": \".+\"" | cut -d : -f 2) | ||||
|     if [ -z "$response" ]; then | ||||
|       _info "txt record updated success." | ||||
|       return 0 | ||||
|     fi | ||||
|   fi | ||||
| 
 | ||||
|   return 1 | ||||
| } | ||||
| 
 | ||||
| #fulldomain txtvalue | ||||
| dns_vscale_rm() { | ||||
|   fulldomain=$1 | ||||
|   txtvalue=$2 | ||||
| 
 | ||||
|   _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" | ||||
|   _vscale_rest GET "domains/$_domain_id/records/" | ||||
| 
 | ||||
|   if [ -n "$response" ]; then | ||||
|     record_id=$(printf "%s\n" "$response" | _egrep_o "\"TXT\", \"id\": [0-9]+, \"name\": \"$_sub_domain.$_domain\"" | cut -d : -f 2 | tr -d ", \"name\"") | ||||
|     _debug record_id "$record_id" | ||||
|     if [ -z "$record_id" ]; then | ||||
|       _err "Can not get record id to remove." | ||||
|       return 1 | ||||
|     fi | ||||
|     if _vscale_rest DELETE "domains/$_domain_id/records/$record_id" && [ -z "$response" ]; then | ||||
|       _info "txt record deleted success." | ||||
|       return 0 | ||||
|     fi | ||||
|     _debug response "$response" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   return 1 | ||||
| } | ||||
| 
 | ||||
| ####################  Private functions below ################################## | ||||
| #_acme-challenge.www.domain.com | ||||
| #returns | ||||
| # _sub_domain=_acme-challenge.www | ||||
| # _domain=domain.com | ||||
| # _domain_id=12345 | ||||
| _get_root() { | ||||
|   domain=$1 | ||||
|   i=2 | ||||
|   p=1 | ||||
| 
 | ||||
|   if _vscale_rest GET "domains/"; then | ||||
|     response="$(echo "$response" | tr -d "\n" | sed 's/{/\n&/g')" | ||||
|     while true; do | ||||
|       h=$(printf "%s" "$domain" | cut -d . -f $i-100) | ||||
|       _debug h "$h" | ||||
|       if [ -z "$h" ]; then | ||||
|         #not valid | ||||
|         return 1 | ||||
|       fi | ||||
| 
 | ||||
|       hostedzone="$(echo "$response" | _egrep_o "{.*\"name\":\s*\"$h\".*}")" | ||||
|       if [ "$hostedzone" ]; then | ||||
|         _domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o "\"id\":\s*[0-9]+" | _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 | ||||
|   fi | ||||
|   return 1 | ||||
| } | ||||
| 
 | ||||
| #method uri qstr data | ||||
| _vscale_rest() { | ||||
|   mtd="$1" | ||||
|   ep="$2" | ||||
|   data="$3" | ||||
| 
 | ||||
|   _debug mtd "$mtd" | ||||
|   _debug ep "$ep" | ||||
| 
 | ||||
|   export _H1="Accept: application/json" | ||||
|   export _H2="Content-Type: application/json" | ||||
|   export _H3="X-Token: ${VSCALE_API_KEY}" | ||||
| 
 | ||||
|   if [ "$mtd" != "GET" ]; then | ||||
|     # both POST and DELETE. | ||||
|     _debug data "$data" | ||||
|     response="$(_post "$data" "$VSCALE_API_URL/$ep" "" "$mtd")" | ||||
|   else | ||||
|     response="$(_get "$VSCALE_API_URL/$ep")" | ||||
|   fi | ||||
| 
 | ||||
|   if [ "$?" != "0" ]; then | ||||
|     _err "error $ep" | ||||
|     return 1 | ||||
|   fi | ||||
|   _debug2 response "$response" | ||||
|   return 0 | ||||
| } | ||||
							
								
								
									
										107
									
								
								dnsapi/dns_yandex.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										107
									
								
								dnsapi/dns_yandex.sh
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,107 @@ | ||||
| #!/usr/bin/env sh | ||||
| # Author: non7top@gmail.com | ||||
| # 07 Jul 2017 | ||||
| # report bugs at https://github.com/non7top/acme.sh | ||||
| 
 | ||||
| # Values to export: | ||||
| # export PDD_Token="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" | ||||
| 
 | ||||
| ########  Public functions ##################### | ||||
| 
 | ||||
| #Usage: dns_myapi_add   _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" | ||||
| dns_yandex_add() { | ||||
|   fulldomain="${1}" | ||||
|   txtvalue="${2}" | ||||
|   _debug "Calling: dns_yandex_add() '${fulldomain}' '${txtvalue}'" | ||||
|   _PDD_credentials || return 1 | ||||
|   export _H1="PddToken: $PDD_Token" | ||||
| 
 | ||||
|   curDomain=$(_PDD_get_domain "$fulldomain") | ||||
|   _debug "Found suitable domain in pdd: $curDomain" | ||||
|   curSubdomain="$(echo "${fulldomain}" | sed -e "s@.${curDomain}\$@@")" | ||||
|   curData="domain=${curDomain}&type=TXT&subdomain=${curSubdomain}&ttl=360&content=${txtvalue}" | ||||
|   curUri="https://pddimp.yandex.ru/api2/admin/dns/add" | ||||
|   curResult="$(_post "${curData}" "${curUri}")" | ||||
|   _debug "Result: $curResult" | ||||
| } | ||||
| 
 | ||||
| #Usage: dns_myapi_rm   _acme-challenge.www.domain.com | ||||
| dns_yandex_rm() { | ||||
|   fulldomain="${1}" | ||||
|   _debug "Calling: dns_yandex_rm() '${fulldomain}'" | ||||
|   _PDD_credentials || return 1 | ||||
|   export _H1="PddToken: $PDD_Token" | ||||
|   record_id=$(pdd_get_record_id "${fulldomain}") | ||||
|   _debug "Result: $record_id" | ||||
| 
 | ||||
|   curDomain=$(_PDD_get_domain "$fulldomain") | ||||
|   _debug "Found suitable domain in pdd: $curDomain" | ||||
|   curSubdomain="$(echo "${fulldomain}" | sed -e "s@.${curDomain}\$@@")" | ||||
| 
 | ||||
|   curUri="https://pddimp.yandex.ru/api2/admin/dns/del" | ||||
|   curData="domain=${curDomain}&record_id=${record_id}" | ||||
|   curResult="$(_post "${curData}" "${curUri}")" | ||||
|   _debug "Result: $curResult" | ||||
| } | ||||
| 
 | ||||
| ####################  Private functions below ################################## | ||||
| 
 | ||||
| _PDD_get_domain() { | ||||
|   fulldomain="${1}" | ||||
|   __page=1 | ||||
|   __last=0 | ||||
|   while [ $__last -eq 0 ]; do | ||||
|     uri1="https://pddimp.yandex.ru/api2/admin/domain/domains?page=${__page}&on_page=20" | ||||
|     res1=$(_get "$uri1" | _normalizeJson) | ||||
|     #_debug "$res1" | ||||
|     __found=$(echo "$res1" | sed -n -e 's#.* "found": \([^,]*\),.*#\1#p') | ||||
|     _debug "found: $__found results on page" | ||||
|     if [ "$__found" -lt 20 ]; then | ||||
|       _debug "last page: $__page" | ||||
|       __last=1 | ||||
|     fi | ||||
| 
 | ||||
|     __all_domains="$__all_domains $(echo "$res1" | sed -e "s@,@\n@g" | grep '"name"' | cut -d: -f2 | sed -e 's@"@@g')" | ||||
| 
 | ||||
|     __page=$(_math $__page + 1) | ||||
|   done | ||||
| 
 | ||||
|   k=2 | ||||
|   while [ $k -lt 10 ]; do | ||||
|     __t=$(echo "$fulldomain" | cut -d . -f $k-100) | ||||
|     _debug "finding zone for domain $__t" | ||||
|     for d in $__all_domains; do | ||||
|       if [ "$d" = "$__t" ]; then | ||||
|         echo "$__t" | ||||
|         return | ||||
|       fi | ||||
|     done | ||||
|     k=$(_math $k + 1) | ||||
|   done | ||||
|   _err "No suitable domain found in your account" | ||||
|   return 1 | ||||
| } | ||||
| 
 | ||||
| _PDD_credentials() { | ||||
|   if [ -z "${PDD_Token}" ]; then | ||||
|     PDD_Token="" | ||||
|     _err "You need to export PDD_Token=xxxxxxxxxxxxxxxxx" | ||||
|     _err "You can get it at https://pddimp.yandex.ru/api2/admin/get_token" | ||||
|     return 1 | ||||
|   else | ||||
|     _saveaccountconf PDD_Token "${PDD_Token}" | ||||
|   fi | ||||
| } | ||||
| 
 | ||||
| pdd_get_record_id() { | ||||
|   fulldomain="${1}" | ||||
| 
 | ||||
|   curDomain=$(_PDD_get_domain "$fulldomain") | ||||
|   _debug "Found suitable domain in pdd: $curDomain" | ||||
|   curSubdomain="$(echo "${fulldomain}" | sed -e "s@.${curDomain}\$@@")" | ||||
| 
 | ||||
|   curUri="https://pddimp.yandex.ru/api2/admin/dns/list?domain=${curDomain}" | ||||
|   curResult="$(_get "${curUri}" | _normalizeJson)" | ||||
|   _debug "Result: $curResult" | ||||
|   echo "$curResult" | _egrep_o "{[^{]*\"content\":[^{]*\"subdomain\":\"${curSubdomain}\"" | sed -n -e 's#.* "record_id": \(.*\),[^,]*#\1#p' | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user