mirror of
				https://github.com/hiskang/acme.sh
				synced 2025-10-31 10:27:22 +00:00 
			
		
		
		
	Merge remote-tracking branch 'upstream/master'
This commit is contained in:
		
						commit
						e82ea94bb6
					
				
							
								
								
									
										9
									
								
								.github/PULL_REQUEST_TEMPLATE.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								.github/PULL_REQUEST_TEMPLATE.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +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. | ||||
| 
 | ||||
| --> | ||||
							
								
								
									
										40
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										40
									
								
								.travis.yml
									
									
									
									
									
								
							| @ -1,4 +1,9 @@ | ||||
| language: shell | ||||
| sudo: required | ||||
| 
 | ||||
| os: | ||||
|   - linux | ||||
|   - osx | ||||
| 
 | ||||
| env: | ||||
|   global: | ||||
| @ -11,12 +16,37 @@ addons: | ||||
|     packages: | ||||
|     - 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"; | ||||
|     fi | ||||
|    | ||||
| script: | ||||
|   - curl -sSL $SHFMT_URL -o ~/shfmt | ||||
|   - chmod +x ~/shfmt | ||||
|   - shellcheck -V | ||||
|   - shellcheck -e SC2021,SC2126,SC2034 **/*.sh && echo "shellcheck OK" | ||||
|   - ~/shfmt -l -w -i 2 . && echo "shfmt OK" || git diff --exit-code || (echo "Run shfmt to fix the formatting issues" && false) | ||||
|   - echo "TEST_LOCAL=$TEST_LOCAL" | ||||
|   - echo "NGROK_TOKEN=$(echo "$NGROK_TOKEN" | wc -c)" | ||||
|   - 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 **/*.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" -a "$NGROK_TOKEN" ]; then sudo NGROK_TOKEN="$NGROK_TOKEN" ./letest.sh ; fi | ||||
|   - if [ "$TRAVIS_OS_NAME" = "osx" -a "$NGROK_TOKEN" ]; then sudo NGROK_TOKEN="$NGROK_TOKEN" OPENSSL_BIN="$OPENSSL_BIN" ./letest.sh ; fi | ||||
| 
 | ||||
| 
 | ||||
| matrix: | ||||
|   fast_finish: true | ||||
|  | ||||
							
								
								
									
										193
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										193
									
								
								README.md
									
									
									
									
									
								
							| @ -1,21 +1,26 @@ | ||||
| # An ACME Shell script: acme.sh [](https://travis-ci.org/Neilpang/acme.sh) | ||||
| - An ACME protocol client written purely in Shell (Unix shell) language. | ||||
| - Fully ACME protocol implementation. | ||||
| - Simple, powerful and very easy to use. You only need 3 minutes to learn. | ||||
| - Full ACME protocol implementation. | ||||
| - Simple, powerful and very easy to use. You only need 3 minutes to learn it. | ||||
| - Bash, dash and sh compatible. | ||||
| - Simplest shell script for Let's Encrypt free certificate client. | ||||
| - Purely written in Shell with no dependencies on python or Let's Encrypt official client. | ||||
| - Just one script, to issue, renew and install your certificates automatically. | ||||
| - 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. | ||||
| 
 | ||||
| It's probably the `easiest&smallest&smartest` shell script to automatically issue & renew the free certificates from Let's Encrypt. | ||||
| 
 | ||||
| 
 | ||||
| Wiki: https://github.com/Neilpang/acme.sh/wiki | ||||
| 
 | ||||
| 
 | ||||
| Twitter: [@neilpangxa](https://twitter.com/neilpangxa) | ||||
| 
 | ||||
| 
 | ||||
| # [中文说明](https://github.com/Neilpang/acme.sh/wiki/%E8%AF%B4%E6%98%8E) | ||||
| 
 | ||||
| 
 | ||||
| # Tested OS | ||||
| 
 | ||||
| | NO | Status| Platform| | ||||
| |----|-------|---------| | ||||
| |1|[](https://github.com/Neilpang/letest#here-are-the-latest-status)| Ubuntu | ||||
| @ -37,42 +42,41 @@ 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): | ||||
| 
 | ||||
| https://github.com/Neilpang/acmetest | ||||
| 
 | ||||
| # Supported Mode | ||||
| 
 | ||||
| 1. Webroot mode | ||||
| 2. Standalone mode | ||||
| 3. Apache mode | ||||
| 4. Dns mode | ||||
| # Supported modes | ||||
| 
 | ||||
| - Webroot mode | ||||
| - Standalone mode | ||||
| - Apache mode | ||||
| - DNS mode | ||||
| 
 | ||||
| 
 | ||||
| # 1. How to install | ||||
| 
 | ||||
| ### 1. Install online: | ||||
| ### 1. Install online | ||||
| 
 | ||||
| Check this project: https://github.com/Neilpang/get.acme.sh | ||||
| 
 | ||||
| ```bash | ||||
| curl https://get.acme.sh | sh | ||||
| 
 | ||||
| ``` | ||||
| 
 | ||||
| Or: | ||||
| 
 | ||||
| ```bash | ||||
| wget -O -  https://get.acme.sh | sh | ||||
| 
 | ||||
| ``` | ||||
| 
 | ||||
| 
 | ||||
| ### 2. Or, Install from git: | ||||
| ### 2. Or, Install from git | ||||
| 
 | ||||
| Clone this project:  | ||||
| Clone this project and launch installation: | ||||
| 
 | ||||
| ```bash | ||||
| git clone https://github.com/Neilpang/acme.sh.git | ||||
| @ -87,9 +91,9 @@ Advanced Installation:  https://github.com/Neilpang/acme.sh/wiki/How-to-install | ||||
| The installer will perform 3 actions: | ||||
| 
 | ||||
| 1. Create and copy `acme.sh` to your home dir (`$HOME`): `~/.acme.sh/`. | ||||
| All certs will be placed in this folder. | ||||
| All certs will be placed in this folder too. | ||||
| 2. Create alias for: `acme.sh=~/.acme.sh/acme.sh`. | ||||
| 3. Create everyday cron job to check and renew the cert if needed. | ||||
| 3. Create daily cron job to check and renew the certs if needed. | ||||
| 
 | ||||
| Cron entry example: | ||||
| 
 | ||||
| @ -97,18 +101,17 @@ Cron entry example: | ||||
| 0 0 * * * "/home/user/.acme.sh"/acme.sh --cron --home "/home/user/.acme.sh" > /dev/null | ||||
| ``` | ||||
| 
 | ||||
| After the installation, you must close current terminal and reopen again to make the alias take effect. | ||||
| After the installation, you must close the current terminal and reopen it to make the alias take effect. | ||||
| 
 | ||||
| Ok, you are ready to issue certs now. | ||||
| 
 | ||||
| Ok, you are ready to issue cert now. | ||||
| Show help message: | ||||
| 
 | ||||
| ``` | ||||
| 
 | ||||
| root@v1:~# acme.sh -h | ||||
| 
 | ||||
| ``` | ||||
| 
 | ||||
| # 2. Just issue a cert: | ||||
| # 2. Just issue a cert | ||||
| 
 | ||||
| **Example 1:** Single domain. | ||||
| 
 | ||||
| @ -124,51 +127,54 @@ acme.sh --issue -d example.com -d www.example.com -d cp.example.com -w /home/www | ||||
| 
 | ||||
| The parameter `/home/wwwroot/example.com` is the web root folder. You **MUST** have `write access` to this folder. | ||||
| 
 | ||||
| Second argument **"example.com"** is the main domain you want to issue cert for. | ||||
| You must have at least a domain there. | ||||
| Second argument **"example.com"** is the main domain you want to issue the cert for. | ||||
| You must have at least one domain there. | ||||
| 
 | ||||
| You must point and bind all the domains to the same webroot dir: `/home/wwwroot/example.com`. | ||||
| 
 | ||||
| Generate/issued certs will be placed in `~/.acme.sh/example.com/` | ||||
| Generated/issued certs will be placed in `~/.acme.sh/example.com/` | ||||
| 
 | ||||
| The issued cert will be renewed every **60** days automatically. | ||||
| The issued cert will be renewed automatically every **60** days. | ||||
| 
 | ||||
| More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert | ||||
| 
 | ||||
| 
 | ||||
| # 3. Install the issued cert to apache/nginx etc. | ||||
| # 3. Install the issued cert to Apache/Nginx etc. | ||||
| 
 | ||||
| After you issue a cert, you probably want to install/copy the cert to your nginx/apache or other servers.  | ||||
| You **MUST** use this command to copy the certs to the target files,  **Do NOT** use the certs files in **.acme.sh/** folder, they are for internal use only, the folder structure may change in future. | ||||
| After you issue a cert, you probably want to install/copy the cert to your Apache/Nginx or other servers. | ||||
| You **MUST** use this command to copy the certs to the target files, **DO NOT** use the certs files in **~/.acme.sh/** folder, they are for internal use only, the folder structure may change in the future. | ||||
| 
 | ||||
| **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" | ||||
| ``` | ||||
| 
 | ||||
| **apache** example | ||||
| **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" | ||||
| --fullchainpath /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 force-reload" | ||||
| ``` | ||||
| 
 | ||||
| Only the domain is required, all the other parameters are optional. | ||||
| 
 | ||||
| Install/copy the issued cert/key to the production apache or nginx path. | ||||
| 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 will be automatically reloaded by the command: `service apache2 reload` or `service nginx reload`. | ||||
| 
 | ||||
| # 4. Use Standalone server to issue cert | ||||
| 
 | ||||
| **(requires you be root/sudoer, or you have permission to listen tcp 80 port)** | ||||
| **(requires you to be root/sudoer or have permission to listen on port 80 (TCP))** | ||||
| 
 | ||||
| The tcp `80` port **MUST** be free to listen, otherwise you will be prompted to free the `80` port and try again. | ||||
| Port `80` (TCP) **MUST** be free to listen on, otherwise you will be prompted to free it and try again. | ||||
| 
 | ||||
| ```bash | ||||
| acme.sh --issue --standalone -d example.com -d www.example.com -d cp.example.com | ||||
| @ -176,13 +182,14 @@ acme.sh --issue --standalone -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 | ||||
| 
 | ||||
| # 5. Use Standalone tls server to issue cert | ||||
| 
 | ||||
| **(requires you be root/sudoer, or you have permission to listen tcp 443 port)** | ||||
| # 5. Use Standalone TLS server to issue cert | ||||
| 
 | ||||
| **(requires you to be root/sudoer or have permission to listen on port 443 (TCP))** | ||||
| 
 | ||||
| acme.sh supports `tls-sni-01` validation. | ||||
| 
 | ||||
| The tcp `443` port **MUST** be free to listen, otherwise you will be prompted to free the `443` port and try again. | ||||
| Port `443` (TCP) **MUST** be free to listen on, otherwise you will be prompted to free it and try again. | ||||
| 
 | ||||
| ```bash | ||||
| acme.sh --issue --tls -d example.com -d www.example.com -d cp.example.com | ||||
| @ -190,31 +197,33 @@ acme.sh --issue --tls -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 | ||||
| 
 | ||||
| 
 | ||||
| # 6. Use Apache mode | ||||
| 
 | ||||
| **(requires you be root/sudoer, since it is required to interact with apache server)** | ||||
| **(requires you to be root/sudoer, since it is required to interact with Apache server)** | ||||
| 
 | ||||
| If you are running a web server, apache or nginx, it is recommended to use the `Webroot mode`. | ||||
| If you are running a web server, Apache or Nginx, it is recommended to use the `Webroot mode`. | ||||
| 
 | ||||
| Particularly, if you are running an apache server, you should use apache mode instead. This mode doesn't write any files to your web root folder. | ||||
| Particularly, if you are running an Apache server, you should use Apache mode instead. This mode doesn't write any files to your web root folder. | ||||
| 
 | ||||
| Just set string "apache" as the second argument, it will force use of apache plugin automatically. | ||||
| Just set string "apache" as the second argument and it will force use of apache plugin automatically. | ||||
| 
 | ||||
| ``` | ||||
| acme.sh --issue --apache -d example.com -d www.example.com -d user.example.com | ||||
| 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 DNS mode: | ||||
| 
 | ||||
| Support the `dns-01` challenge. | ||||
| 
 | ||||
| ```bash | ||||
| acme.sh --issue --dns -d example.com -d www.example.com -d user.example.com | ||||
| acme.sh --issue --dns -d example.com -d www.example.com -d cp.example.com | ||||
| ``` | ||||
| 
 | ||||
| You should get the output like below: | ||||
| You should get an output like below: | ||||
| 
 | ||||
| ``` | ||||
| Add the following txt record: | ||||
| @ -226,7 +235,6 @@ Domain:_acme-challenge.www.example.com | ||||
| Txt value:9ihDbjxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | ||||
| 
 | ||||
| Please add those txt records to the domains. Waiting for the dns to take effect. | ||||
| 
 | ||||
| ``` | ||||
| 
 | ||||
| Then just rerun with `renew` argument: | ||||
| @ -237,49 +245,57 @@ acme.sh --renew -d example.com | ||||
| 
 | ||||
| Ok, it's finished. | ||||
| 
 | ||||
| 
 | ||||
| # 8. Automatic DNS API integration | ||||
| 
 | ||||
| If your DNS provider supports API access, we can use API to automatically issue the certs. | ||||
| If your DNS provider supports API access, we can use that API to automatically issue the certs. | ||||
| 
 | ||||
| You don't have do anything manually! | ||||
| You don't have to do anything manually! | ||||
| 
 | ||||
| ### Currently acme.sh supports: | ||||
| 
 | ||||
| 1. Cloudflare.com API | ||||
| 2. Dnspod.cn API | ||||
| 3. Cloudxns.com API | ||||
| 4. Godaddy.com API | ||||
| 5. OVH, kimsufi, soyoustart and runabove API | ||||
| 6. AWS Route 53, see: https://github.com/Neilpang/acme.sh/issues/65 | ||||
| 7. PowerDNS API | ||||
| 8. 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.) | ||||
| 9. LuaDNS.com API | ||||
| 10. DNSMadeEasy.com API | ||||
| 1. CloudFlare.com API | ||||
| 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. LuaDNS.com API | ||||
| 1. DNSMadeEasy.com API | ||||
| 1. nsupdate API | ||||
| 1. aliyun.com(阿里云) API | ||||
| 1. ISPConfig 3.1 API | ||||
| 1. Alwaysdata.com API | ||||
| 1. Linode.com API | ||||
| 1. FreeDNS (https://freedns.afraid.org/) | ||||
| 
 | ||||
| ##### More APIs are coming soon... | ||||
| **More APIs coming soon...** | ||||
| 
 | ||||
| If your DNS provider is not on the supported list above, you can write your own script API easily. If you do please consider submitting a [Pull Request](https://github.com/Neilpang/acme.sh/pulls) and contribute to the project. | ||||
| If your DNS provider is not on the supported list above, you can write your own DNS API script easily. If you do, please consider submitting a [Pull Request](https://github.com/Neilpang/acme.sh/pulls) and contribute it to the project. | ||||
| 
 | ||||
| For more details: [How to use dns api](dnsapi) | ||||
| For more details: [How to use DNS API](dnsapi) | ||||
| 
 | ||||
| # 9. Issue ECC certificate: | ||||
| 
 | ||||
| `Let's Encrypt` now can issue **ECDSA** certificates. | ||||
| # 9. Issue ECC certificates | ||||
| 
 | ||||
| And we also support it. | ||||
| `Let's Encrypt` can now issue **ECDSA** certificates. | ||||
| 
 | ||||
| And we support them too! | ||||
| 
 | ||||
| Just set the `length` parameter with a prefix `ec-`. | ||||
| 
 | ||||
| For example: | ||||
| 
 | ||||
| ### Single domain ECC cerfiticate: | ||||
| ### Single domain ECC cerfiticate | ||||
| 
 | ||||
| ```bash | ||||
| acme.sh --issue -w /home/wwwroot/example.com -d example.com --keylength ec-256 | ||||
| ``` | ||||
| 
 | ||||
| SAN multi domain ECC certificate: | ||||
| ### SAN multi domain ECC certificate | ||||
| 
 | ||||
| ```bash | ||||
| acme.sh --issue -w /home/wwwroot/example.com -d example.com -d www.example.com --keylength ec-256 | ||||
| @ -294,7 +310,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 cert | ||||
| # 10. 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. | ||||
| 
 | ||||
| @ -305,29 +321,37 @@ acme.sh --renew  -d  example.com --force | ||||
| ``` | ||||
| 
 | ||||
| or, for ECC cert: | ||||
| 
 | ||||
| ``` | ||||
| acme.sh --renew -d example.com --force --ecc | ||||
| ``` | ||||
| 
 | ||||
| 
 | ||||
| # 11. How to upgrade `acme.sh` | ||||
| acme.sh is in developing, 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: | ||||
| 
 | ||||
| ``` | ||||
| acme.sh --upgrade | ||||
| ``` | ||||
| 
 | ||||
| You can enable auto upgrade: | ||||
| You can also enable auto upgrade: | ||||
| 
 | ||||
| ``` | ||||
| acme.sh --upgrade --auto-upgrade | ||||
| ``` | ||||
| Then **acme.sh** will keep up to date automatically. | ||||
| 
 | ||||
| Then **acme.sh** will be kept up to date automatically. | ||||
| 
 | ||||
| Disable auto upgrade: | ||||
| 
 | ||||
| ``` | ||||
| acme.sh --upgrade --auto-upgrade 0 | ||||
| ``` | ||||
| 
 | ||||
| 
 | ||||
| # 12. Issue a cert from an existing CSR | ||||
| 
 | ||||
| https://github.com/Neilpang/acme.sh/wiki/Issue-a-cert-from-existing-CSR | ||||
| @ -339,22 +363,25 @@ Speak ACME language using shell, directly to "Let's Encrypt". | ||||
| 
 | ||||
| TODO: | ||||
| 
 | ||||
| # Acknowledgment | ||||
| 
 | ||||
| # 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 | ||||
| 
 | ||||
| License is GPLv3 | ||||
| 
 | ||||
| Please Star and Fork me. | ||||
| 
 | ||||
| [Issues](https://github.com/Neilpang/acme.sh/issues) and [pull requests](https://github.com/Neilpang/acme.sh/pulls) are welcomed. | ||||
| [Issues](https://github.com/Neilpang/acme.sh/issues) and [pull requests](https://github.com/Neilpang/acme.sh/pulls) are welcome. | ||||
| 
 | ||||
| 
 | ||||
| # Donate | ||||
| 
 | ||||
| 1. PayPal: donate@acme.sh | ||||
| 
 | ||||
| [Donate List](https://github.com/Neilpang/acme.sh/wiki/Donate-list) | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										1
									
								
								deploy/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								deploy/README.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| #Using deploy api | ||||
							
								
								
									
										81
									
								
								deploy/kong.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								deploy/kong.sh
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,81 @@ | ||||
| #!/usr/bin/env sh | ||||
| 
 | ||||
| # This deploy hook will deploy ssl cert on kong proxy engine based on api request_host parameter. | ||||
| # Note that ssl plugin should be available on Kong instance | ||||
| # The hook will match cdomain to request_host, in case of multiple domain it will always take the first | ||||
| # one (acme.sh behaviour). | ||||
| # If ssl config already exist it will update only cert and key not touching other parameter | ||||
| # If ssl config doesn't exist it will only upload cert and key and not set other parameter | ||||
| # Not that we deploy full chain | ||||
| # See https://getkong.org/plugins/dynamic-ssl/ for other options | ||||
| # 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 uuid linked to the domain | ||||
|   uuid=$(_get "$KONG_URL/apis?request_host=$_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 "$uuid" ]; then | ||||
|     _err "Unable to get Kong uuid for domain $_cdomain" | ||||
|     _err "Make sure that KONG_URL is correctly configured" | ||||
|     _err "Make sure that a Kong api request_host match the domain" | ||||
|     _err "Kong url: $KONG_URL" | ||||
|     return 1 | ||||
|   fi | ||||
|   #Save kong url if it's succesful (First run case) | ||||
|   _saveaccountconf KONG_URL "$KONG_URL" | ||||
|   #Generate DEIM | ||||
|   delim="-----MultipartDelimeter$(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) | ||||
|   #set name to ssl | ||||
|   content="--$delim${nl}Content-Disposition: form-data; name=\"name\"${nl}${nl}ssl" | ||||
|   #add key | ||||
|   content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"config.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=\"config.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 ssl plugins is aready enabled (if not => POST else => PATCH) | ||||
|   ssl_uuid=$(_get "$KONG_URL/apis/$uuid/plugins" | _egrep_o '"id":"[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"[a-zA-Z0-9\-\,\"_\:]*"name":"ssl"' | _egrep_o '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}') | ||||
|   _debug ssl_uuid "$ssl_uuid" | ||||
|   if [ -z "$ssl_uuid" ]; then | ||||
|     #Post certificate to Kong | ||||
|     response=$(_post "$content" "$KONG_URL/apis/$uuid/plugins" "" "POST") | ||||
|   else | ||||
|     #patch | ||||
|     response=$(_post "$content" "$KONG_URL/apis/$uuid/plugins/$ssl_uuid" "" "PATCH") | ||||
|   fi | ||||
|   if ! [ "$(echo "$response" | _egrep_o "ssl")" = "ssl" ]; then | ||||
|     _err "An error occured with cert upload. Check response:" | ||||
|     _err "$response" | ||||
|     return 1 | ||||
|   fi | ||||
|   _debug response "$response" | ||||
|   _info "Certificate successfully deployed" | ||||
| } | ||||
							
								
								
									
										327
									
								
								dnsapi/README.md
									
									
									
									
									
								
							
							
						
						
									
										327
									
								
								dnsapi/README.md
									
									
									
									
									
								
							| @ -1,99 +1,80 @@ | ||||
| # How to use dns api | ||||
| # How to use DNS API | ||||
| 
 | ||||
| ## Use CloudFlare domain api to automatically issue cert | ||||
| ## 1. Use CloudFlare domain API to automatically issue cert | ||||
| 
 | ||||
| For now, we support clourflare integeration. | ||||
| 
 | ||||
| First you need to login to your clourflare account to get your api key. | ||||
| First you need to login to your CloudFlare account to get your API key. | ||||
| 
 | ||||
| ``` | ||||
| export CF_Key="sdfsdfsdfljlbjkljlkjsdfoiwje" | ||||
| 
 | ||||
| export CF_Email="xxxx@sss.com" | ||||
| 
 | ||||
| ``` | ||||
| 
 | ||||
| Ok, let's issue cert now: | ||||
| Ok, let's issue a cert now: | ||||
| ``` | ||||
| acme.sh --issue --dns dns_cf -d example.com -d www.example.com | ||||
| ``` | ||||
| 
 | ||||
| The `CF_Key` and `CF_Email`  will be saved in `~/.acme.sh/account.conf`, when next time you use cloudflare api, it will reuse this key. | ||||
| The `CF_Key` and `CF_Email` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. | ||||
| 
 | ||||
| 
 | ||||
| ## 2. Use DNSPod.cn domain API to automatically issue cert | ||||
| 
 | ||||
| ## Use Dnspod.cn domain api to automatically issue cert | ||||
| 
 | ||||
| For now, we support dnspod.cn integeration. | ||||
| 
 | ||||
| First you need to login to your dnspod.cn account to get your api key and key id. | ||||
| First you need to login to your DNSPod account to get your API Key and ID. | ||||
| 
 | ||||
| ``` | ||||
| export DP_Id="1234" | ||||
| 
 | ||||
| export DP_Key="sADDsdasdgdsf" | ||||
| 
 | ||||
| ``` | ||||
| 
 | ||||
| Ok, let's issue cert now: | ||||
| Ok, let's issue a cert now: | ||||
| ``` | ||||
| acme.sh --issue --dns dns_dp -d example.com -d www.example.com | ||||
| ``` | ||||
| 
 | ||||
| The `DP_Id` and `DP_Key`  will be saved in `~/.acme.sh/account.conf`, when next time you use dnspod.cn api, it will reuse this key. | ||||
| The `DP_Id` and `DP_Key` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. | ||||
| 
 | ||||
| 
 | ||||
| ## Use Cloudxns.com domain api to automatically issue cert | ||||
| ## 3. Use CloudXNS.com domain API to automatically issue cert | ||||
| 
 | ||||
| For now, we support Cloudxns.com integeration. | ||||
| 
 | ||||
| First you need to login to your Cloudxns.com account to get your api key and key secret. | ||||
| First you need to login to your CloudXNS account to get your API Key and Secret. | ||||
| 
 | ||||
| ``` | ||||
| export CX_Key="1234" | ||||
| 
 | ||||
| export CX_Secret="sADDsdasdgdsf" | ||||
| 
 | ||||
| ``` | ||||
| 
 | ||||
| Ok, let's issue cert now: | ||||
| Ok, let's issue a cert now: | ||||
| ``` | ||||
| acme.sh --issue --dns dns_cx -d example.com -d www.example.com | ||||
| ``` | ||||
| 
 | ||||
| The `CX_Key` and `CX_Secret`  will be saved in `~/.acme.sh/account.conf`, when next time you use Cloudxns.com api, it will reuse this key. | ||||
| The `CX_Key` and `CX_Secret` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. | ||||
| 
 | ||||
| 
 | ||||
| ## Use Godaddy.com domain api to automatically issue cert | ||||
| ## 4. Use GoDaddy.com domain API to automatically issue cert | ||||
| 
 | ||||
| We support Godaddy integration. | ||||
| 
 | ||||
| First you need to login to your Godaddy account to get your api key and api secret. | ||||
| First you need to login to your GoDaddy account to get your API Key and Secret. | ||||
| 
 | ||||
| https://developer.godaddy.com/keys/ | ||||
| 
 | ||||
| Please Create a Production key, instead of a Test key. | ||||
| 
 | ||||
| Please create a Production key, instead of a Test key. | ||||
| 
 | ||||
| ``` | ||||
| export GD_Key="sdfsdfsdfljlbjkljlkjsdfoiwje" | ||||
| 
 | ||||
| export GD_Secret="asdfsdafdsfdsfdsfdsfdsafd" | ||||
| 
 | ||||
| ``` | ||||
| 
 | ||||
| Ok, let's issue cert now: | ||||
| Ok, let's issue a cert now: | ||||
| ``` | ||||
| acme.sh --issue --dns dns_gd -d example.com -d www.example.com | ||||
| ``` | ||||
| 
 | ||||
| The `GD_Key` and `GD_Secret`  will be saved in `~/.acme.sh/account.conf`, when next time you use cloudflare api, it will reuse this key. | ||||
| The `GD_Key` and `GD_Secret` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. | ||||
| 
 | ||||
| ## Use PowerDNS embedded api to automatically issue cert | ||||
| 
 | ||||
| We support PowerDNS embedded API integration. | ||||
| ## 5. Use PowerDNS embedded API to automatically issue cert | ||||
| 
 | ||||
| First you need to enable api and set your api-token in PowerDNS configuration. | ||||
| First you need to login to your PowerDNS account to enable the API and set your API-Token in the configuration. | ||||
| 
 | ||||
| https://doc.powerdns.com/md/httpapi/README/ | ||||
| 
 | ||||
| @ -102,29 +83,237 @@ export PDNS_Url="http://ns.example.com:8081" | ||||
| export PDNS_ServerId="localhost" | ||||
| export PDNS_Token="0123456789ABCDEF" | ||||
| export PDNS_Ttl=60 | ||||
| 
 | ||||
| ``` | ||||
| 
 | ||||
| Ok, let's issue cert now: | ||||
| Ok, let's issue a cert now: | ||||
| ``` | ||||
| acme.sh --issue --dns dns_pdns -d example.com -d www.example.com | ||||
| ``` | ||||
| 
 | ||||
| The `PDNS_Url`, `PDNS_ServerId`, `PDNS_Token` and `PDNS_Ttl` will be saved in `~/.acme.sh/account.conf`. | ||||
| The `PDNS_Url`, `PDNS_ServerId`, `PDNS_Token` and `PDNS_Ttl` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. | ||||
| 
 | ||||
| ## Use OVH/kimsufi/soyoustart/runabove API | ||||
| 
 | ||||
| ## 6. Use OVH/kimsufi/soyoustart/runabove API to automatically issue cert | ||||
| 
 | ||||
| https://github.com/Neilpang/acme.sh/wiki/How-to-use-OVH-domain-api | ||||
| 
 | ||||
| # Use custom api | ||||
| 
 | ||||
| If your api is not supported yet,  you can write your own dns api. | ||||
| ## 7. Use nsupdate to automatically issue cert | ||||
| 
 | ||||
| Let's assume you want to name it 'myapi', | ||||
| First, generate a key for updating the zone | ||||
| ``` | ||||
| b=$(dnssec-keygen -a hmac-sha512 -b 512 -n USER -K /tmp foo) | ||||
| cat > /etc/named/keys/update.key <<EOF | ||||
| key "update" { | ||||
|     algorithm hmac-sha512; | ||||
|     secret "$(awk '/^Key/{print $2}' /tmp/$b.private)"; | ||||
| }; | ||||
| EOF | ||||
| rm -f /tmp/$b.{private,key} | ||||
| ``` | ||||
| 
 | ||||
| Include this key in your named configuration | ||||
| ``` | ||||
| include "/etc/named/keys/update.key"; | ||||
| ``` | ||||
| 
 | ||||
| Next, configure your zone to allow dynamic updates. | ||||
| 
 | ||||
| Depending on your named version, use either | ||||
| ``` | ||||
| zone "example.com" { | ||||
|     type master; | ||||
|     allow-update { key "update"; }; | ||||
| }; | ||||
| ``` | ||||
| or | ||||
| ``` | ||||
| zone "example.com" { | ||||
|     type master; | ||||
|     update-policy { | ||||
|         grant update subdomain example.com.; | ||||
|     }; | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| Finally, make the DNS server and update Key available to `acme.sh` | ||||
| 
 | ||||
| ``` | ||||
| export NSUPDATE_SERVER="dns.example.com" | ||||
| export NSUPDATE_KEY="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa==" | ||||
| ``` | ||||
| 
 | ||||
| Ok, let's issue a cert now: | ||||
| ``` | ||||
| acme.sh --issue --dns dns_nsupdate -d example.com -d www.example.com | ||||
| ``` | ||||
| 
 | ||||
| The `NSUPDATE_SERVER` and `NSUPDATE_KEY` settings will be saved in `~/.acme.sh/account.conf` and will be reused when needed. | ||||
| 
 | ||||
| 
 | ||||
| ## 8. Use LuaDNS domain API | ||||
| 
 | ||||
| Get your API token at https://api.luadns.com/settings | ||||
| 
 | ||||
| ``` | ||||
| export LUA_Key="sdfsdfsdfljlbjkljlkjsdfoiwje" | ||||
| export LUA_Email="xxxx@sss.com" | ||||
| ``` | ||||
| 
 | ||||
| To issue a cert: | ||||
| ``` | ||||
| acme.sh --issue --dns dns_lua -d example.com -d www.example.com | ||||
| ``` | ||||
| 
 | ||||
| The `LUA_Key` and `LUA_Email` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. | ||||
| 
 | ||||
| 
 | ||||
| ## 9. Use DNSMadeEasy domain API | ||||
| 
 | ||||
| Get your API credentials at https://cp.dnsmadeeasy.com/account/info | ||||
| 
 | ||||
| ``` | ||||
| export ME_Key="sdfsdfsdfljlbjkljlkjsdfoiwje" | ||||
| export ME_Secret="qdfqsdfkjdskfj" | ||||
| ``` | ||||
| 
 | ||||
| To issue a cert: | ||||
| ``` | ||||
| acme.sh --issue --dns dns_me -d example.com -d www.example.com | ||||
| ``` | ||||
| 
 | ||||
| The `ME_Key` and `ME_Secret` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. | ||||
| 
 | ||||
| 
 | ||||
| ## 10. Use Amazon Route53 domain API | ||||
| 
 | ||||
| https://github.com/Neilpang/acme.sh/wiki/How-to-use-Amazon-Route53-API | ||||
| 
 | ||||
| ``` | ||||
| export  AWS_ACCESS_KEY_ID=XXXXXXXXXX | ||||
| export  AWS_SECRET_ACCESS_KEY=XXXXXXXXXXXXXXX | ||||
| ``` | ||||
| 
 | ||||
| To issue a cert: | ||||
| ``` | ||||
| acme.sh --issue --dns dns_aws -d example.com -d www.example.com | ||||
| ``` | ||||
| 
 | ||||
| The `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. | ||||
| 
 | ||||
| ## 11. Use Aliyun domain API to automatically issue cert | ||||
| 
 | ||||
| First you need to login to your Aliyun account to get your API key. | ||||
| [https://ak-console.aliyun.com/#/accesskey](https://ak-console.aliyun.com/#/accesskey) | ||||
| 
 | ||||
| ``` | ||||
| export Ali_Key="sdfsdfsdfljlbjkljlkjsdfoiwje" | ||||
| export Ali_Secret="jlsdflanljkljlfdsaklkjflsa" | ||||
| ``` | ||||
| 
 | ||||
| Ok, let's issue a cert now: | ||||
| ``` | ||||
| acme.sh --issue --dns dns_ali -d example.com -d www.example.com | ||||
| ``` | ||||
| 
 | ||||
| The `Ali_Key` and `Ali_Secret` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. | ||||
| 
 | ||||
| ## 12. Use ISPConfig 3.1 API | ||||
| 
 | ||||
| This only works for ISPConfig 3.1 (and newer). | ||||
| 
 | ||||
| Create a Remote User in the ISPConfig Control Panel. The Remote User must have access to at least `DNS zone functions` and `DNS txt functions`. | ||||
| 
 | ||||
| ``` | ||||
| export ISPC_User="xxx" | ||||
| export ISPC_Password="xxx" | ||||
| export ISPC_Api="https://ispc.domain.tld:8080/remote/json.php" | ||||
| export ISPC_Api_Insecure=1 | ||||
| ``` | ||||
| If you have installed ISPConfig on a different port, then alter the 8080 accordingly. | ||||
| Leaver ISPC_Api_Insecure set to 1 if you have not a valid ssl cert for your installation. Change it to 0 if you have a valid ssl cert. | ||||
| 
 | ||||
| To issue a cert: | ||||
| ``` | ||||
| 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 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 automaitcally | ||||
| validate with acme.sh at FreeDNS. | ||||
| 
 | ||||
| # Use custom API | ||||
| 
 | ||||
| If your API is not supported yet, you can write your own DNS API. | ||||
| 
 | ||||
| Let's assume you want to name it 'myapi': | ||||
| 
 | ||||
| 1. Create a bash script named `~/.acme.sh/dns_myapi.sh`, | ||||
| 2. In the script, you must have a function named `dns_myapi_add()`. Which will be called by acme.sh to add dns records. | ||||
| 3. Then you can use your api to issue cert like: | ||||
| 2. In the script you must have a function named `dns_myapi_add()` which will be called by acme.sh to add the DNS records. | ||||
| 3. Then you can use your API to issue cert like this: | ||||
| 
 | ||||
| ``` | ||||
| acme.sh --issue --dns dns_myapi -d example.com -d www.example.com | ||||
| @ -132,45 +321,7 @@ 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) | ||||
| 
 | ||||
| # Use lexicon dns api | ||||
| 
 | ||||
| # Use lexicon DNS API | ||||
| 
 | ||||
| https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api | ||||
| 
 | ||||
| ## Use LuaDNS domain API | ||||
| 
 | ||||
| Get your API token at https://api.luadns.com/settings | ||||
| 
 | ||||
| ``` | ||||
| export LUA_Key="sdfsdfsdfljlbjkljlkjsdfoiwje" | ||||
| 
 | ||||
| export LUA_Email="xxxx@sss.com" | ||||
| 
 | ||||
| ``` | ||||
| 
 | ||||
| To issue a cert: | ||||
| ``` | ||||
| acme.sh   --issue   --dns dns_lua --dnssleep 3  -d example.com  -d www.example.com | ||||
| ``` | ||||
| 
 | ||||
| The `LUA_Key` and `LUA_Email`  will be saved in `~/.acme.sh/account.conf`, and will be reused when needed. | ||||
| 
 | ||||
| ## Use DNSMadeEasy domain API | ||||
| 
 | ||||
| Get your API credentials at https://cp.dnsmadeeasy.com/account/info | ||||
| 
 | ||||
| ``` | ||||
| export ME_Key="sdfsdfsdfljlbjkljlkjsdfoiwje" | ||||
| 
 | ||||
| export ME_Secret="qdfqsdfkjdskfj" | ||||
| 
 | ||||
| ``` | ||||
| 
 | ||||
| To issue a cert: | ||||
| ``` | ||||
| acme.sh   --issue   --dns dns_me --dnssleep 3  -d example.com  -d www.example.com | ||||
| ``` | ||||
| 
 | ||||
| The `ME_Key` and `ME_Secret`  will be saved in `~/.acme.sh/account.conf`, and will be reused when needed. | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										147
									
								
								dnsapi/dns_ad.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										147
									
								
								dnsapi/dns_ad.sh
									
									
									
									
									
										Normal 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 | ||||
| } | ||||
							
								
								
									
										187
									
								
								dnsapi/dns_ali.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										187
									
								
								dnsapi/dns_ali.sh
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,187 @@ | ||||
| #!/usr/bin/env sh | ||||
| 
 | ||||
| Ali_API="https://alidns.aliyuncs.com/" | ||||
| 
 | ||||
| #Ali_Key="LTqIA87hOKdjevsf5" | ||||
| #Ali_Secret="0p5EYueFNq501xnCPzKNbx6K51qPH2" | ||||
| 
 | ||||
| #Usage: dns_ali_add   _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" | ||||
| dns_ali_add() { | ||||
|   fulldomain=$1 | ||||
|   txtvalue=$2 | ||||
| 
 | ||||
|   if [ -z "$Ali_Key" ] || [ -z "$Ali_Secret" ]; then | ||||
|     Ali_Key="" | ||||
|     Ali_Secret="" | ||||
|     _err "You don't specify aliyun api key and secret yet." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   #save the api key and secret to the account conf file. | ||||
|   _saveaccountconf Ali_Key "$Ali_Key" | ||||
|   _saveaccountconf Ali_Secret "$Ali_Secret" | ||||
| 
 | ||||
|   _debug "First detect the root zone" | ||||
|   if ! _get_root "$fulldomain"; then | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _debug "Add record" | ||||
|   _add_record_query "$_domain" "$_sub_domain" "$txtvalue" && _ali_rest "Add record" | ||||
| } | ||||
| 
 | ||||
| dns_ali_rm() { | ||||
|   fulldomain=$1 | ||||
|   _clean | ||||
| } | ||||
| 
 | ||||
| ####################  Private functions below ################################## | ||||
| 
 | ||||
| _get_root() { | ||||
|   domain=$1 | ||||
|   i=2 | ||||
|   p=1 | ||||
|   while true; do | ||||
|     h=$(printf "%s" "$domain" | cut -d . -f $i-100) | ||||
|     if [ -z "$h" ]; then | ||||
|       #not valid | ||||
|       return 1 | ||||
|     fi | ||||
| 
 | ||||
|     _describe_records_query "$h" | ||||
|     if ! _ali_rest "Get root" "ignore"; then | ||||
|       return 1 | ||||
|     fi | ||||
| 
 | ||||
|     if _contains "$response" "PageNumber"; then | ||||
|       _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) | ||||
|       _debug _sub_domain "$_sub_domain" | ||||
|       _domain="$h" | ||||
|       _debug _domain "$_domain" | ||||
|       return 0 | ||||
|     fi | ||||
|     p="$i" | ||||
|     i=$(_math "$i" + 1) | ||||
|   done | ||||
|   return 1 | ||||
| } | ||||
| 
 | ||||
| _ali_rest() { | ||||
|   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" | ||||
| 
 | ||||
|   if ! response="$(_get "$url")"; then | ||||
|     _err "Error <$1>" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   if [ -z "$2" ]; then | ||||
|     message="$(printf "%s" "$response" | _egrep_o "\"Message\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")" | ||||
|     if [ -n "$message" ]; then | ||||
|       _err "$message" | ||||
|       return 1 | ||||
|     fi | ||||
|   fi | ||||
| 
 | ||||
|   _debug2 response "$response" | ||||
|   return 0 | ||||
| } | ||||
| 
 | ||||
| _ali_urlencode() { | ||||
|   _str="$1" | ||||
|   _str_len=${#_str} | ||||
|   _u_i=1 | ||||
|   while [ "$_u_i" -le "$_str_len" ]; do | ||||
|     _str_c="$(printf "%s" "$_str" | cut -c "$_u_i")" | ||||
|     case $_str_c in [a-zA-Z0-9.~_-]) | ||||
|       printf "%s" "$_str_c" | ||||
|       ;; | ||||
|     *) | ||||
|       printf "%%%02X" "'$_str_c" | ||||
|       ;; | ||||
|     esac | ||||
|     _u_i="$(_math "$_u_i" + 1)" | ||||
|   done | ||||
| } | ||||
| 
 | ||||
| _ali_nonce() { | ||||
|   #_head_n 1 </dev/urandom | _digest "sha256" hex | cut -c 1-31 | ||||
|   #Not so good... | ||||
|   date +"%s%N" | ||||
| } | ||||
| 
 | ||||
| _check_exist_query() { | ||||
|   query='' | ||||
|   query=$query'AccessKeyId='$Ali_Key | ||||
|   query=$query'&Action=DescribeDomainRecords' | ||||
|   query=$query'&DomainName='$1 | ||||
|   query=$query'&Format=json' | ||||
|   query=$query'&RRKeyWord=_acme-challenge' | ||||
|   query=$query'&SignatureMethod=HMAC-SHA1' | ||||
|   query=$query"&SignatureNonce=$(_ali_nonce)" | ||||
|   query=$query'&SignatureVersion=1.0' | ||||
|   query=$query'&Timestamp='$(_timestamp) | ||||
|   query=$query'&TypeKeyWord=TXT' | ||||
|   query=$query'&Version=2015-01-09' | ||||
| } | ||||
| 
 | ||||
| _add_record_query() { | ||||
|   query='' | ||||
|   query=$query'AccessKeyId='$Ali_Key | ||||
|   query=$query'&Action=AddDomainRecord' | ||||
|   query=$query'&DomainName='$1 | ||||
|   query=$query'&Format=json' | ||||
|   query=$query'&RR='$2 | ||||
|   query=$query'&SignatureMethod=HMAC-SHA1' | ||||
|   query=$query"&SignatureNonce=$(_ali_nonce)" | ||||
|   query=$query'&SignatureVersion=1.0' | ||||
|   query=$query'&Timestamp='$(_timestamp) | ||||
|   query=$query'&Type=TXT' | ||||
|   query=$query'&Value='$3 | ||||
|   query=$query'&Version=2015-01-09' | ||||
| } | ||||
| 
 | ||||
| _delete_record_query() { | ||||
|   query='' | ||||
|   query=$query'AccessKeyId='$Ali_Key | ||||
|   query=$query'&Action=DeleteDomainRecord' | ||||
|   query=$query'&Format=json' | ||||
|   query=$query'&RecordId='$1 | ||||
|   query=$query'&SignatureMethod=HMAC-SHA1' | ||||
|   query=$query"&SignatureNonce=$(_ali_nonce)" | ||||
|   query=$query'&SignatureVersion=1.0' | ||||
|   query=$query'&Timestamp='$(_timestamp) | ||||
|   query=$query'&Version=2015-01-09' | ||||
| } | ||||
| 
 | ||||
| _describe_records_query() { | ||||
|   query='' | ||||
|   query=$query'AccessKeyId='$Ali_Key | ||||
|   query=$query'&Action=DescribeDomainRecords' | ||||
|   query=$query'&DomainName='$1 | ||||
|   query=$query'&Format=json' | ||||
|   query=$query'&SignatureMethod=HMAC-SHA1' | ||||
|   query=$query"&SignatureNonce=$(_ali_nonce)" | ||||
|   query=$query'&SignatureVersion=1.0' | ||||
|   query=$query'&Timestamp='$(_timestamp) | ||||
|   query=$query'&Version=2015-01-09' | ||||
| } | ||||
| 
 | ||||
| _clean() { | ||||
|   _check_exist_query "$_domain" | ||||
|   if ! _ali_rest "Check exist records" "ignore"; then | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   records="$(echo "$response" -n | _egrep_o "\"RecordId\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")" | ||||
|   printf "%s" "$records" \ | ||||
|     | while read -r record_id; do | ||||
|       _delete_record_query "$record_id" | ||||
|       _ali_rest "Delete record $record_id" "ignore" | ||||
|     done | ||||
| } | ||||
| 
 | ||||
| _timestamp() { | ||||
|   date -u +"%Y-%m-%dT%H%%3A%M%%3A%SZ" | ||||
| } | ||||
							
								
								
									
										227
									
								
								dnsapi/dns_aws.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										227
									
								
								dnsapi/dns_aws.sh
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,227 @@ | ||||
| #!/usr/bin/env sh | ||||
| 
 | ||||
| # | ||||
| #AWS_ACCESS_KEY_ID="sdfsdfsdfljlbjkljlkjsdfoiwje" | ||||
| # | ||||
| #AWS_SECRET_ACCESS_KEY="xxxxxxx" | ||||
| 
 | ||||
| #This is the Amazon Route53 api wrapper for acme.sh | ||||
| 
 | ||||
| AWS_HOST="route53.amazonaws.com" | ||||
| AWS_URL="https://$AWS_HOST" | ||||
| 
 | ||||
| AWS_WIKI="https://github.com/Neilpang/acme.sh/wiki/How-to-use-Amazon-Route53-API" | ||||
| 
 | ||||
| ########  Public functions ##################### | ||||
| 
 | ||||
| #Usage: dns_myapi_add   _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" | ||||
| dns_aws_add() { | ||||
|   fulldomain=$1 | ||||
|   txtvalue=$2 | ||||
| 
 | ||||
|   if [ -z "$AWS_ACCESS_KEY_ID" ] || [ -z "$AWS_SECRET_ACCESS_KEY" ]; then | ||||
|     AWS_ACCESS_KEY_ID="" | ||||
|     AWS_SECRET_ACCESS_KEY="" | ||||
|     _err "You don't specify aws route53 api key id and and api key secret yet." | ||||
|     _err "Please create you key and try again. see $(__green $AWS_WIKI)" | ||||
|     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 | ||||
|     _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>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 success." | ||||
|     return 0 | ||||
|   fi | ||||
| 
 | ||||
|   return 1 | ||||
| } | ||||
| 
 | ||||
| #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 below ################################## | ||||
| 
 | ||||
| _get_root() { | ||||
|   domain=$1 | ||||
|   i=2 | ||||
|   p=1 | ||||
| 
 | ||||
|   if aws_rest GET "2013-04-01/hostedzone"; then | ||||
|     _debug "response" "$response" | ||||
|     while true; do | ||||
|       h=$(printf "%s" "$domain" | cut -d . -f $i-100) | ||||
|       if [ -z "$h" ]; then | ||||
|         #not valid | ||||
|         return 1 | ||||
|       fi | ||||
| 
 | ||||
|       if _contains "$response" "<Name>$h.</Name>"; then | ||||
|         hostedzone="$(echo "$response" | _egrep_o "<HostedZone><Id>[^<]*<.Id><Name>$h.<.Name>.*<.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 [ "$_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 | ||||
| aws_rest() { | ||||
|   mtd="$1" | ||||
|   ep="$2" | ||||
|   qsr="$3" | ||||
|   data="$4" | ||||
| 
 | ||||
|   _debug mtd "$mtd" | ||||
|   _debug ep "$ep" | ||||
|   _debug qsr "$qsr" | ||||
|   _debug data "$data" | ||||
| 
 | ||||
|   CanonicalURI="/$ep" | ||||
|   _debug2 CanonicalURI "$CanonicalURI" | ||||
| 
 | ||||
|   CanonicalQueryString="$qsr" | ||||
|   _debug2 CanonicalQueryString "$CanonicalQueryString" | ||||
| 
 | ||||
|   RequestDate="$(date -u +"%Y%m%dT%H%M%SZ")" | ||||
|   _debug2 RequestDate "$RequestDate" | ||||
| 
 | ||||
|   #RequestDate="20161120T141056Z" ############## | ||||
| 
 | ||||
|   export _H1="x-amz-date: $RequestDate" | ||||
| 
 | ||||
|   aws_host="$AWS_HOST" | ||||
|   CanonicalHeaders="host:$aws_host\nx-amz-date:$RequestDate\n" | ||||
|   SignedHeaders="host;x-amz-date" | ||||
|   if [ -n "$AWS_SESSION_TOKEN" ]; then | ||||
|     export _H2="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" | ||||
|   _debug2 RequestPayload "$RequestPayload" | ||||
| 
 | ||||
|   Hash="sha256" | ||||
| 
 | ||||
|   CanonicalRequest="$mtd\n$CanonicalURI\n$CanonicalQueryString\n$CanonicalHeaders\n$SignedHeaders\n$(printf "%s" "$RequestPayload" | _digest "$Hash" hex)" | ||||
|   _debug2 CanonicalRequest "$CanonicalRequest" | ||||
| 
 | ||||
|   HashedCanonicalRequest="$(printf "$CanonicalRequest%s" | _digest "$Hash" hex)" | ||||
|   _debug2 HashedCanonicalRequest "$HashedCanonicalRequest" | ||||
| 
 | ||||
|   Algorithm="AWS4-HMAC-SHA256" | ||||
|   _debug2 Algorithm "$Algorithm" | ||||
| 
 | ||||
|   RequestDateOnly="$(echo "$RequestDate" | cut -c 1-8)" | ||||
|   _debug2 RequestDateOnly "$RequestDateOnly" | ||||
| 
 | ||||
|   Region="us-east-1" | ||||
|   Service="route53" | ||||
| 
 | ||||
|   CredentialScope="$RequestDateOnly/$Region/$Service/aws4_request" | ||||
|   _debug2 CredentialScope "$CredentialScope" | ||||
| 
 | ||||
|   StringToSign="$Algorithm\n$RequestDate\n$CredentialScope\n$HashedCanonicalRequest" | ||||
| 
 | ||||
|   _debug2 StringToSign "$StringToSign" | ||||
| 
 | ||||
|   kSecret="AWS4$AWS_SECRET_ACCESS_KEY" | ||||
| 
 | ||||
|   #kSecret="wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY" ############################ | ||||
| 
 | ||||
|   _debug2 kSecret "$kSecret" | ||||
| 
 | ||||
|   kSecretH="$(printf "%s" "$kSecret" | _hex_dump | tr -d " ")" | ||||
|   _debug2 kSecretH "$kSecretH" | ||||
| 
 | ||||
|   kDateH="$(printf "$RequestDateOnly%s" | _hmac "$Hash" "$kSecretH" hex)" | ||||
|   _debug2 kDateH "$kDateH" | ||||
| 
 | ||||
|   kRegionH="$(printf "$Region%s" | _hmac "$Hash" "$kDateH" hex)" | ||||
|   _debug2 kRegionH "$kRegionH" | ||||
| 
 | ||||
|   kServiceH="$(printf "$Service%s" | _hmac "$Hash" "$kRegionH" hex)" | ||||
|   _debug2 kServiceH "$kServiceH" | ||||
| 
 | ||||
|   kSigningH="$(printf "aws4_request%s" | _hmac "$Hash" "$kServiceH" hex)" | ||||
|   _debug2 kSigningH "$kSigningH" | ||||
| 
 | ||||
|   signature="$(printf "$StringToSign%s" | _hmac "$Hash" "$kSigningH" hex)" | ||||
|   _debug2 signature "$signature" | ||||
| 
 | ||||
|   Authorization="$Algorithm Credential=$AWS_ACCESS_KEY_ID/$CredentialScope, SignedHeaders=$SignedHeaders, Signature=$signature" | ||||
|   _debug2 Authorization "$Authorization" | ||||
| 
 | ||||
|   _H3="Authorization: $Authorization" | ||||
|   _debug _H3 "$_H3" | ||||
| 
 | ||||
|   url="$AWS_URL/$ep" | ||||
| 
 | ||||
|   if [ "$mtd" = "GET" ]; then | ||||
|     response="$(_get "$url")" | ||||
|   else | ||||
|     response="$(_post "$data" "$url")" | ||||
|   fi | ||||
| 
 | ||||
|   _ret="$?" | ||||
|   if [ "$_ret" = "0" ]; then | ||||
|     if _contains "$response" "<ErrorResponse"; then | ||||
|       _err "Response error:$response" | ||||
|       return 1 | ||||
|     fi | ||||
|   fi | ||||
| 
 | ||||
|   return "$_ret" | ||||
| } | ||||
| @ -22,6 +22,12 @@ dns_cf_add() { | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   if ! _contains "$CF_Email" "@"; then | ||||
|     _err "It seems that the CF_Email=$CF_Email is not a valid email address." | ||||
|     _err "Please check and retry." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   #save the api key and email to the account conf file. | ||||
|   _saveaccountconf CF_Key "$CF_Key" | ||||
|   _saveaccountconf CF_Email "$CF_Email" | ||||
| @ -49,9 +55,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." | ||||
| @ -66,9 +70,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" | ||||
| @ -77,13 +79,48 @@ dns_cf_add() { | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #fulldomain | ||||
| #fulldomain txtvalue | ||||
| dns_cf_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" | ||||
|   _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 | ||||
| @ -95,6 +132,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 | ||||
| @ -104,8 +142,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 | ||||
| @ -125,11 +163,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 | ||||
|  | ||||
| @ -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")" | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
							
								
								
									
										375
									
								
								dnsapi/dns_freedns.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										375
									
								
								dnsapi/dns_freedns.sh
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,375 @@ | ||||
| #!/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 folowing 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")" | ||||
| 
 | ||||
|   # Sometimes FreeDNS does not reurn 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 channged?  If so..." | ||||
|         _err "Please export as FREEDNS_User / FREEDNS_Password and try again." | ||||
|       fi | ||||
|       return 1 | ||||
|     fi | ||||
| 
 | ||||
|     # Now convert the tables in the HTML to CSV.  This litte gem from | ||||
|     # http://stackoverflow.com/questions/1403087/how-can-i-convert-an-html-table-to-csv     | ||||
|     subdomain_csv="$(echo "$htmlpage" \ | ||||
|       | grep -i -e '</\?TABLE\|</\?TD\|</\?TR\|</\?TH' \ | ||||
|       | sed 's/^[\ \t]*//g' \ | ||||
|       | tr -d '\n' \ | ||||
|       | sed 's/<\/TR[^>]*>/\n/Ig' \ | ||||
|       | sed 's/<\/\?\(TABLE\|TR\)[^>]*>//Ig' \ | ||||
|       | sed 's/^<T[DH][^>]*>\|<\/\?T[DH][^>]*>$//Ig' \ | ||||
|       | sed 's/<\/T[DH][^>]*><T[DH][^>]*>/,/Ig' \ | ||||
|       | grep 'edit.php?' \ | ||||
|       | grep "$top_domain")" | ||||
|     # 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)" | ||||
|     nl=' | ||||
| ' | ||||
|     i=0 | ||||
|     found=0 | ||||
|     while [ "$i" -lt "$lines" ]; do | ||||
|       i="$(_math "$i" + 1)" | ||||
|       line="$(echo "$subdomain_csv" | cut -d "$nl" -f "$i")" | ||||
|       tmp="$(echo "$line" | cut -d ',' -f 1)" | ||||
|       if [ $found = 0 ] && _startswith "$tmp" "<td>$top_domain"; then | ||||
|         # this line will contain DNSdomainid for the top_domain | ||||
|         DNSdomainid="$(echo "$line" | cut -d ',' -f 2 | sed 's/^.*domain_id=//;s/>.*//')" | ||||
|         found=1 | ||||
|       else | ||||
|         # lines contain DNS records for all subdomains | ||||
|         DNSname="$(echo "$line" | cut -d ',' -f 2 | sed 's/^[^>]*>//;s/<\/a>.*//')" | ||||
|         DNStype="$(echo "$line" | cut -d ',' -f 3)" | ||||
|         if [ "$DNSname" = "$fulldomain" ] && [ "$DNStype" = "TXT" ]; then | ||||
|           DNSdataid="$(echo "$line" | cut -d ',' -f 2 | sed 's/^.*data_id=//;s/>.*//')" | ||||
|           # 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 chalenge subdomain. | ||||
|           DNSvalue="$(echo "$line" | cut -d ',' -f 4 | sed 's/^[^"]*"//;s/".*//;s/<\/td>.*//')" | ||||
|           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) | ||||
|       _freedns_delete_txt_record "$FREEDNS_COOKIE" "$DNSdataid" | ||||
|       if [ "$?" = "0" ]; 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() fuction | ||||
|   FREEDNS_COOKIE="$(_read_conf "$ACCOUNT_CONF_PATH" "FREEDNS_COOKIE")" | ||||
|   _debug "FreeDNS login cookies: $FREEDNS_COOKIE" | ||||
| 
 | ||||
|   # Sometimes FreeDNS does not reurn 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 | ||||
| 
 | ||||
|     # Now convert the tables in the HTML to CSV.  This litte gem from | ||||
|     # http://stackoverflow.com/questions/1403087/how-can-i-convert-an-html-table-to-csv | ||||
|     subdomain_csv="$(echo "$htmlpage" \ | ||||
|       | grep -i -e '</\?TABLE\|</\?TD\|</\?TR\|</\?TH' \ | ||||
|       | sed 's/^[\ \t]*//g' \ | ||||
|       | tr -d '\n' \ | ||||
|       | sed 's/<\/TR[^>]*>/\n/Ig' \ | ||||
|       | sed 's/<\/\?\(TABLE\|TR\)[^>]*>//Ig' \ | ||||
|       | sed 's/^<T[DH][^>]*>\|<\/\?T[DH][^>]*>$//Ig' \ | ||||
|       | sed 's/<\/T[DH][^>]*><T[DH][^>]*>/,/Ig' \ | ||||
|       | grep 'edit.php?' \ | ||||
|       | grep "$fulldomain")" | ||||
|     # 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)" | ||||
|     nl=' | ||||
| ' | ||||
|     i=0 | ||||
|     found=0 | ||||
|     while [ "$i" -lt "$lines" ]; do | ||||
|       i="$(_math "$i" + 1)" | ||||
|       line="$(echo "$subdomain_csv" | cut -d "$nl" -f "$i")" | ||||
|       DNSname="$(echo "$line" | cut -d ',' -f 2 | sed 's/^[^>]*>//;s/<\/a>.*//')" | ||||
|       DNStype="$(echo "$line" | cut -d ',' -f 3)" | ||||
|       if [ "$DNSname" = "$fulldomain" ] && [ "$DNStype" = "TXT" ]; then | ||||
|         DNSdataid="$(echo "$line" | cut -d ',' -f 2 | sed 's/^.*data_id=//;s/>.*//')" | ||||
|         DNSvalue="$(echo "$line" | cut -d ',' -f 4 | sed 's/^[^"]*"//;s/".*//;s/<\/td>.*//')" | ||||
|         _debug "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 subdmoain page from FreeDNS" | ||||
| 
 | ||||
|   htmlpage="$(_get "$url")" | ||||
| 
 | ||||
|   if [ "$?" != "0" ]; then | ||||
|     _err "FreeDNS retrieve subdomins 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 seurity 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 | ||||
| } | ||||
| @ -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" | ||||
|  | ||||
							
								
								
									
										177
									
								
								dnsapi/dns_ispconfig.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										177
									
								
								dnsapi/dns_ispconfig.sh
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,177 @@ | ||||
| #!/usr/bin/env sh | ||||
| 
 | ||||
| # ISPConfig 3.1 API | ||||
| # User must provide login data and URL to the ISPConfig installation incl. port. The remote user in ISPConfig must have access to: | ||||
| # - DNS zone Functions | ||||
| # - DNS txt Functions | ||||
| 
 | ||||
| # Report bugs to https://github.com/sjau/acme.sh | ||||
| 
 | ||||
| # Values to export: | ||||
| # export ISPC_User="remoteUser" | ||||
| # export ISPC_Password="remotePassword" | ||||
| # export ISPC_Api="https://ispc.domain.tld:8080/remote/json.php" | ||||
| # export ISPC_Api_Insecure=1     # Set 1 for insecure and 0 for secure -> difference is whether ssl cert is checked for validity (0) or whether it is just accepted (1) | ||||
| 
 | ||||
| ########  Public functions ##################### | ||||
| 
 | ||||
| #Usage: dns_myapi_add   _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" | ||||
| dns_ispconfig_add() { | ||||
|   fulldomain="${1}" | ||||
|   txtvalue="${2}" | ||||
|   _debug "Calling: dns_ispconfig_add() '${fulldomain}' '${txtvalue}'" | ||||
|   _ISPC_credentials && _ISPC_login && _ISPC_getZoneInfo && _ISPC_addTxt | ||||
| } | ||||
| 
 | ||||
| #Usage: dns_myapi_rm   _acme-challenge.www.domain.com | ||||
| dns_ispconfig_rm() { | ||||
|   fulldomain="${1}" | ||||
|   _debug "Calling: dns_ispconfig_rm() '${fulldomain}'" | ||||
|   _ISPC_credentials && _ISPC_login && _ISPC_rmTxt | ||||
| } | ||||
| 
 | ||||
| ####################  Private functions below ################################## | ||||
| 
 | ||||
| _ISPC_credentials() { | ||||
|   if [ -z "${ISPC_User}" ] || [ -z "$ISPC_Password" ] || [ -z "${ISPC_Api}" ] || [ -z "${ISPC_Api_Insecure}" ]; then | ||||
|     ISPC_User="" | ||||
|     ISPC_Password="" | ||||
|     ISPC_Api="" | ||||
|     ISPC_Api_Insecure="" | ||||
|     _err "You haven't specified the ISPConfig Login data, URL and whether you want check the ISPC SSL cert. Please try again." | ||||
|     return 1 | ||||
|   else | ||||
|     _saveaccountconf ISPC_User "${ISPC_User}" | ||||
|     _saveaccountconf ISPC_Password "${ISPC_Password}" | ||||
|     _saveaccountconf ISPC_Api "${ISPC_Api}" | ||||
|     _saveaccountconf ISPC_Api_Insecure "${ISPC_Api_Insecure}" | ||||
|     # Set whether curl should use secure or insecure mode | ||||
|     export HTTPS_INSECURE="${ISPC_Api_Insecure}" | ||||
|   fi | ||||
| } | ||||
| 
 | ||||
| _ISPC_login() { | ||||
|   _info "Getting Session ID" | ||||
|   curData="{\"username\":\"${ISPC_User}\",\"password\":\"${ISPC_Password}\",\"client_login\":false}" | ||||
|   curResult="$(_post "${curData}" "${ISPC_Api}?login")" | ||||
|   _debug "Calling _ISPC_login: '${curData}' '${ISPC_Api}?login'" | ||||
|   _debug "Result of _ISPC_login: '$curResult'" | ||||
|   if _contains "${curResult}" '"code":"ok"'; then | ||||
|     sessionID=$(echo "${curResult}" | _egrep_o "response.*" | cut -d ':' -f 2 | cut -d '"' -f 2) | ||||
|     _info "Retrieved Session ID." | ||||
|     _debug "Session ID: '${sessionID}'" | ||||
|   else | ||||
|     _err "Couldn't retrieve the Session ID." | ||||
|     return 1 | ||||
|   fi | ||||
| } | ||||
| 
 | ||||
| _ISPC_getZoneInfo() { | ||||
|   _info "Getting Zoneinfo" | ||||
|   zoneEnd=false | ||||
|   curZone="${fulldomain}" | ||||
|   while [ "${zoneEnd}" = false ]; do | ||||
|     # we can strip the first part of the fulldomain, since it's just the _acme-challenge string | ||||
|     curZone="${curZone#*.}" | ||||
|     # suffix . needed for zone -> domain.tld. | ||||
|     curData="{\"session_id\":\"${sessionID}\",\"primary_id\":{\"origin\":\"${curZone}.\"}}" | ||||
|     curResult="$(_post "${curData}" "${ISPC_Api}?dns_zone_get")" | ||||
|     _debug "Calling _ISPC_getZoneInfo: '${curData}' '${ISPC_Api}?login'" | ||||
|     _debug "Result of _ISPC_getZoneInfo: '$curResult'" | ||||
|     if _contains "${curResult}" '"id":"'; then | ||||
|       zoneFound=true | ||||
|       zoneEnd=true | ||||
|       _info "Retrieved zone data." | ||||
|       _debug "Zone data: '${curResult}'" | ||||
|     fi | ||||
|     if [ "${curZone#*.}" != "$curZone" ]; then | ||||
|       _debug2 "$curZone still contains a '.' - so we can check next higher level" | ||||
|     else | ||||
|       zoneEnd=true | ||||
|       _err "Couldn't retrieve zone data." | ||||
|       return 1 | ||||
|     fi | ||||
|   done | ||||
|   if [ "${zoneFound}" ]; then | ||||
|     server_id=$(echo "${curResult}" | _egrep_o "server_id.*" | cut -d ':' -f 2 | cut -d '"' -f 2) | ||||
|     _debug "Server ID: '${server_id}'" | ||||
|     case "${server_id}" in | ||||
|       '' | *[!0-9]*) | ||||
|         _err "Server ID is not numeric." | ||||
|         return 1 | ||||
|         ;; | ||||
|       *) _info "Retrieved Server ID" ;; | ||||
|     esac | ||||
|     zone=$(echo "${curResult}" | _egrep_o "\"id.*" | cut -d ':' -f 2 | cut -d '"' -f 2) | ||||
|     _debug "Zone: '${zone}'" | ||||
|     case "${zone}" in | ||||
|       '' | *[!0-9]*) | ||||
|         _err "Zone ID is not numeric." | ||||
|         return 1 | ||||
|         ;; | ||||
|       *) _info "Retrieved Zone ID" ;; | ||||
|     esac | ||||
|     client_id=$(echo "${curResult}" | _egrep_o "sys_userid.*" | cut -d ':' -f 2 | cut -d '"' -f 2) | ||||
|     _debug "Client ID: '${client_id}'" | ||||
|     case "${client_id}" in | ||||
|       '' | *[!0-9]*) | ||||
|         _err "Client ID is not numeric." | ||||
|         return 1 | ||||
|         ;; | ||||
|       *) _info "Retrieved Client ID." ;; | ||||
|     esac | ||||
|     zoneFound="" | ||||
|     zoneEnd="" | ||||
|   fi | ||||
| } | ||||
| 
 | ||||
| _ISPC_addTxt() { | ||||
|   curSerial="$(date +%s)" | ||||
|   curStamp="$(date +'%F %T')" | ||||
|   params="\"server_id\":\"${server_id}\",\"zone\":\"${zone}\",\"name\":\"${fulldomain}.\",\"type\":\"txt\",\"data\":\"${txtvalue}\",\"aux\":\"0\",\"ttl\":\"3600\",\"active\":\"y\",\"stamp\":\"${curStamp}\",\"serial\":\"${curSerial}\"" | ||||
|   curData="{\"session_id\":\"${sessionID}\",\"client_id\":\"${client_id}\",\"params\":{${params}}}" | ||||
|   curResult="$(_post "${curData}" "${ISPC_Api}?dns_txt_add")" | ||||
|   _debug "Calling _ISPC_addTxt: '${curData}' '${ISPC_Api}?dns_txt_add'" | ||||
|   _debug "Result of _ISPC_addTxt: '$curResult'" | ||||
|   record_id=$(echo "${curResult}" | _egrep_o "\"response.*" | cut -d ':' -f 2 | cut -d '"' -f 2) | ||||
|   _debug "Record ID: '${record_id}'" | ||||
|   case "${record_id}" in | ||||
|     '' | *[!0-9]*) | ||||
|       _err "Couldn't add ACME Challenge TXT record to zone." | ||||
|       return 1 | ||||
|       ;; | ||||
|     *) _info "Added ACME Challenge TXT record to zone." ;; | ||||
|   esac | ||||
| } | ||||
| 
 | ||||
| _ISPC_rmTxt() { | ||||
|   # Need to get the record ID. | ||||
|   curData="{\"session_id\":\"${sessionID}\",\"primary_id\":{\"name\":\"${fulldomain}.\",\"type\":\"TXT\"}}" | ||||
|   curResult="$(_post "${curData}" "${ISPC_Api}?dns_txt_get")" | ||||
|   _debug "Calling _ISPC_rmTxt: '${curData}' '${ISPC_Api}?dns_txt_get'" | ||||
|   _debug "Result of _ISPC_rmTxt: '$curResult'" | ||||
|   if _contains "${curResult}" '"code":"ok"'; then | ||||
|     record_id=$(echo "${curResult}" | _egrep_o "\"id.*" | cut -d ':' -f 2 | cut -d '"' -f 2) | ||||
|     _debug "Record ID: '${record_id}'" | ||||
|     case "${record_id}" in | ||||
|       '' | *[!0-9]*) | ||||
|         _err "Record ID is not numeric." | ||||
|         return 1 | ||||
|         ;; | ||||
|       *) | ||||
|         unset IFS | ||||
|         _info "Retrieved Record ID." | ||||
|         curData="{\"session_id\":\"${sessionID}\",\"primary_id\":\"${record_id}\"}" | ||||
|         curResult="$(_post "${curData}" "${ISPC_Api}?dns_txt_delete")" | ||||
|         _debug "Calling _ISPC_rmTxt: '${curData}' '${ISPC_Api}?dns_txt_delete'" | ||||
|         _debug "Result of _ISPC_rmTxt: '$curResult'" | ||||
|         if _contains "${curResult}" '"code":"ok"'; then | ||||
|           _info "Removed ACME Challenge TXT record from zone." | ||||
|         else | ||||
|           _err "Couldn't remove ACME Challenge TXT record from zone." | ||||
|           return 1 | ||||
|         fi | ||||
|         ;; | ||||
|     esac | ||||
|   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,7 +30,9 @@ 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" | ||||
|   if [ "$Lx_name_v" ]; then | ||||
| @ -38,7 +40,8 @@ dns_lexicon_add() { | ||||
|     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" | ||||
|   if [ "$Lx_token_v" ]; then | ||||
| @ -46,7 +49,8 @@ dns_lexicon_add() { | ||||
|     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" | ||||
|   if [ "$Lx_password_v" ]; then | ||||
| @ -54,7 +58,8 @@ dns_lexicon_add() { | ||||
|     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" | ||||
|   if [ "$Lx_domaintoken_v" ]; then | ||||
|  | ||||
							
								
								
									
										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" | sed 's/{/\n&/g')" | ||||
| 
 | ||||
|     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" | 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 "{.*\"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 | ||||
| @ -84,7 +84,7 @@ dns_lua_rm() { | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| ####################  Private functions bellow ################################## | ||||
| ####################  Private functions below ################################## | ||||
| #_acme-challenge.www.domain.com | ||||
| #returns | ||||
| # _sub_domain=_acme-challenge.www | ||||
| @ -99,6 +99,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 +107,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,8 +127,8 @@ _LUA_rest() { | ||||
|   data="$3" | ||||
|   _debug "$ep" | ||||
| 
 | ||||
|   _H1="Accept: application/json" | ||||
|   _H2="Authorization: Basic $LUA_auth" | ||||
|   export _H1="Accept: application/json" | ||||
|   export _H2="Authorization: Basic $LUA_auth" | ||||
|   if [ "$data" ]; then | ||||
|     _debug data "$data" | ||||
|     response="$(_post "$data" "$LUA_Api/$ep" "" "$m")" | ||||
|  | ||||
| @ -81,7 +81,7 @@ dns_me_rm() { | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| ####################  Private functions bellow ################################## | ||||
| ####################  Private functions below ################################## | ||||
| #_acme-challenge.www.domain.com | ||||
| #returns | ||||
| # _sub_domain=_acme-challenge.www | ||||
| @ -103,7 +103,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,11 +124,11 @@ _me_rest() { | ||||
|   _debug "$ep" | ||||
| 
 | ||||
|   cdate=$(date -u +"%a, %d %b %Y %T %Z") | ||||
|   hmac=$(printf "%s" "$cdate" | _hmac sha1 "$ME_Secret" 1) | ||||
|   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 | ||||
|     _debug data "$data" | ||||
|  | ||||
| @ -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 ################################## | ||||
|  | ||||
							
								
								
									
										58
									
								
								dnsapi/dns_nsupdate.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										58
									
								
								dnsapi/dns_nsupdate.sh
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,58 @@ | ||||
| #!/usr/bin/env sh | ||||
| 
 | ||||
| ########  Public functions ##################### | ||||
| 
 | ||||
| #Usage: dns_nsupdate_add   _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" | ||||
| dns_nsupdate_add() { | ||||
|   fulldomain=$1 | ||||
|   txtvalue=$2 | ||||
|   _checkKeyFile || return 1 | ||||
|   [ -n "${NSUPDATE_SERVER}" ] || NSUPDATE_SERVER="localhost" | ||||
|   # save the dns server and key to the account conf file. | ||||
|   _saveaccountconf NSUPDATE_SERVER "${NSUPDATE_SERVER}" | ||||
|   _saveaccountconf NSUPDATE_KEY "${NSUPDATE_KEY}" | ||||
|   _info "adding ${fulldomain}. 60 in txt \"${txtvalue}\"" | ||||
|   nsupdate -k "${NSUPDATE_KEY}" <<EOF | ||||
| server ${NSUPDATE_SERVER} | ||||
| update add ${fulldomain}. 60 in txt "${txtvalue}" | ||||
| send | ||||
| EOF | ||||
|   if [ $? -ne 0 ]; then | ||||
|     _err "error updating domain" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   return 0 | ||||
| } | ||||
| 
 | ||||
| #Usage: dns_nsupdate_rm   _acme-challenge.www.domain.com | ||||
| dns_nsupdate_rm() { | ||||
|   fulldomain=$1 | ||||
|   _checkKeyFile || return 1 | ||||
|   [ -n "${NSUPDATE_SERVER}" ] || NSUPDATE_SERVER="localhost" | ||||
|   _info "removing ${fulldomain}. txt" | ||||
|   nsupdate -k "${NSUPDATE_KEY}" <<EOF | ||||
| server ${NSUPDATE_SERVER} | ||||
| update delete ${fulldomain}. txt | ||||
| send | ||||
| EOF | ||||
|   if [ $? -ne 0 ]; then | ||||
|     _err "error updating domain" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   return 0 | ||||
| } | ||||
| 
 | ||||
| ####################  Private functions below ################################## | ||||
| 
 | ||||
| _checkKeyFile() { | ||||
|   if [ -z "${NSUPDATE_KEY}" ]; then | ||||
|     _err "you must specify a path to the nsupdate key file" | ||||
|     return 1 | ||||
|   fi | ||||
|   if [ ! -r "${NSUPDATE_KEY}" ]; then | ||||
|     _err "key ${NSUPDATE_KEY} is unreadable" | ||||
|     return 1 | ||||
|   fi | ||||
| } | ||||
| @ -182,7 +182,7 @@ dns_ovh_rm() { | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| ####################  Private functions bellow ################################## | ||||
| ####################  Private functions below ################################## | ||||
| 
 | ||||
| _ovh_authentication() { | ||||
| 
 | ||||
| @ -273,12 +273,12 @@ _ovh_rest() { | ||||
|   _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")" | ||||
|  | ||||
| @ -12,6 +12,8 @@ DEFAULT_PDNS_TTL=60 | ||||
| 
 | ||||
| ########  Public functions ##################### | ||||
| #Usage: add _acme-challenge.www.domain.com "123456789ABCDEF0000000000000000000000000000000000000" | ||||
| #fulldomain | ||||
| #txtvalue | ||||
| dns_pdns_add() { | ||||
|   fulldomain=$1 | ||||
|   txtvalue=$2 | ||||
| @ -50,7 +52,7 @@ dns_pdns_add() { | ||||
|     _saveaccountconf PDNS_Ttl "$PDNS_Ttl" | ||||
|   fi | ||||
| 
 | ||||
|   _debug "First detect the root zone" | ||||
|   _debug "Detect root zone" | ||||
|   if ! _get_root "$fulldomain"; then | ||||
|     _err "invalid domain" | ||||
|     return 1 | ||||
| @ -68,6 +70,18 @@ dns_pdns_add() { | ||||
| dns_pdns_rm() { | ||||
|   fulldomain=$1 | ||||
| 
 | ||||
|   _debug "Detect root zone" | ||||
|   if ! _get_root "$fulldomain"; then | ||||
|     _err "invalid domain" | ||||
|     return 1 | ||||
|   fi | ||||
|   _debug _domain "$_domain" | ||||
| 
 | ||||
|   if ! rm_record "$_domain" "$fulldomain"; then | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   return 0 | ||||
| } | ||||
| 
 | ||||
| set_record() { | ||||
| @ -76,18 +90,47 @@ set_record() { | ||||
|   full=$2 | ||||
|   txtvalue=$3 | ||||
| 
 | ||||
|   if ! _pdns_rest "PATCH" "/api/v1/servers/$PDNS_ServerId/zones/$root." "{\"rrsets\": [{\"name\": \"$full.\", \"changetype\": \"REPLACE\", \"type\": \"TXT\", \"ttl\": $PDNS_Ttl, \"records\": [{\"name\": \"$full.\", \"type\": \"TXT\", \"content\": \"\\\"$txtvalue\\\"\", \"disabled\": false, \"ttl\": $PDNS_Ttl}]}]}"; then | ||||
|   if ! _pdns_rest "PATCH" "/api/v1/servers/$PDNS_ServerId/zones/$root." "{\"rrsets\": [{\"changetype\": \"REPLACE\", \"name\": \"$full.\", \"type\": \"TXT\", \"ttl\": $PDNS_Ttl, \"records\": [{\"name\": \"$full.\", \"type\": \"TXT\", \"content\": \"\\\"$txtvalue\\\"\", \"disabled\": false, \"ttl\": $PDNS_Ttl}]}]}"; then | ||||
|     _err "Set txt record error." | ||||
|     return 1 | ||||
|   fi | ||||
|   if ! _pdns_rest "PUT" "/api/v1/servers/$PDNS_ServerId/zones/$root./notify"; then | ||||
|     _err "Notify servers error." | ||||
| 
 | ||||
|   if ! notify_slaves "$root"; then | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   return 0 | ||||
| } | ||||
| 
 | ||||
| ####################  Private functions bellow ################################## | ||||
| rm_record() { | ||||
|   _info "Remove record" | ||||
|   root=$1 | ||||
|   full=$2 | ||||
| 
 | ||||
|   if ! _pdns_rest "PATCH" "/api/v1/servers/$PDNS_ServerId/zones/$root." "{\"rrsets\": [{\"changetype\": \"DELETE\", \"name\": \"$full.\", \"type\": \"TXT\"}]}"; then | ||||
|     _err "Delete txt record error." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   if ! notify_slaves "$root"; then | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   return 0 | ||||
| } | ||||
| 
 | ||||
| notify_slaves() { | ||||
|   root=$1 | ||||
| 
 | ||||
|   if ! _pdns_rest "PUT" "/api/v1/servers/$PDNS_ServerId/zones/$root./notify"; then | ||||
|     _err "Notify slaves error." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   return 0 | ||||
| } | ||||
| 
 | ||||
| ####################  Private functions below ################################## | ||||
| #_acme-challenge.www.domain.com | ||||
| #returns | ||||
| # _domain=domain.com | ||||
| @ -113,6 +156,7 @@ _get_root() { | ||||
|     i=$(_math $i + 1) | ||||
|   done | ||||
|   _debug "$domain not found" | ||||
| 
 | ||||
|   return 1 | ||||
| } | ||||
| 
 | ||||
| @ -121,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" | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user