Merge pull request #1 from acmesh-official/dev

rebase to upstream
This commit is contained in:
Tom Herbers 2020-02-26 16:14:14 +01:00 committed by GitHub
commit caeb4a4d0b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
69 changed files with 3961 additions and 325 deletions

12
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,12 @@
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: acmesh
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

View File

@ -2,15 +2,15 @@
我很忙, 每天可能只有 几秒钟 时间看你的 issue, 如果不按照我的要求写 issue, 你可能不会得到任何回复, 石沉大海. 我很忙, 每天可能只有 几秒钟 时间看你的 issue, 如果不按照我的要求写 issue, 你可能不会得到任何回复, 石沉大海.
请确保已经更新到最新的代码, 然后贴上来 `--debug 2` 的调试输出. 没有调试信息. 我做不了什么. 请确保已经更新到最新的代码, 然后贴上来 `--debug 2` 的调试输出. 没有调试信息. 我做不了什么.
如何调试 https://github.com/Neilpang/acme.sh/wiki/How-to-debug-acme.sh 如何调试 https://github.com/acmesh-official/acme.sh/wiki/How-to-debug-acme.sh
If it is a bug report: If it is a bug report:
- make sure you are able to repro it on the latest released version. - make sure you are able to repro it on the latest released version.
You can install the latest version by: `acme.sh --upgrade` You can install the latest version by: `acme.sh --upgrade`
- Search the existing issues. - Search the existing issues.
- Refer to the [WIKI](https://wiki.acme.sh). - Refer to the [WIKI](https://wiki.acme.sh).
- Debug info [Debug](https://github.com/Neilpang/acme.sh/wiki/How-to-debug-acme.sh). - Debug info [Debug](https://github.com/acmesh-official/acme.sh/wiki/How-to-debug-acme.sh).
--> -->

View File

@ -3,7 +3,7 @@
Please send to `dev` branch instead. Please send to `dev` branch instead.
Any PR to `master` branch will NOT be merged. Any PR to `master` branch will NOT be merged.
2. For dns api support, read this guide first: https://github.com/Neilpang/acme.sh/wiki/DNS-API-Dev-Guide 2. For dns api support, read this guide first: https://github.com/acmesh-official/acme.sh/wiki/DNS-API-Dev-Guide
You will NOT get any review without passing this guide. You also need to fix the CI errors. You will NOT get any review without passing this guide. You also need to fix the CI errors.
--> -->

View File

@ -28,11 +28,11 @@ script:
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then shellcheck -V ; fi - if [ "$TRAVIS_OS_NAME" = "linux" ]; then shellcheck -V ; fi
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then shellcheck -e SC2181 **/*.sh && echo "shellcheck OK" ; fi - if [ "$TRAVIS_OS_NAME" = "linux" ]; then shellcheck -e SC2181 **/*.sh && echo "shellcheck OK" ; fi
- cd .. - cd ..
- git clone https://github.com/Neilpang/acmetest.git && cp -r acme.sh acmetest/ && cd acmetest - git clone --depth 1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/ && cd acmetest
- if [ "$TRAVIS_OS_NAME" = "linux" -a "$NGROK_TOKEN" ]; then sudo TEST_LOCAL="$TEST_LOCAL" NGROK_TOKEN="$NGROK_TOKEN" ./rundocker.sh testplat ubuntu:latest ; fi - if [ "$TRAVIS_OS_NAME" = "linux" -a "$NGROK_TOKEN" ]; then sudo TEST_LOCAL="$TEST_LOCAL" NGROK_TOKEN="$NGROK_TOKEN" ./rundocker.sh testplat ubuntu:latest ; fi
- if [ "$TRAVIS_OS_NAME" = "osx" -a "$NGROK_TOKEN" ]; then sudo TEST_LOCAL="$TEST_LOCAL" NGROK_TOKEN="$NGROK_TOKEN" ACME_OPENSSL_BIN="$ACME_OPENSSL_BIN" ./letest.sh ; fi - if [ "$TRAVIS_OS_NAME" = "osx" -a "$NGROK_TOKEN" ]; then sudo TEST_LOCAL="$TEST_LOCAL" NGROK_TOKEN="$NGROK_TOKEN" ACME_OPENSSL_BIN="$ACME_OPENSSL_BIN" ./letest.sh ; fi
matrix: matrix:
fast_finish: true fast_finish: true

View File

@ -3,6 +3,7 @@ FROM alpine:3.10
RUN apk update -f \ RUN apk update -f \
&& apk --no-cache add -f \ && apk --no-cache add -f \
openssl \ openssl \
openssh-client \
coreutils \ coreutils \
bind-tools \ bind-tools \
curl \ curl \

124
README.md
View File

@ -1,6 +1,6 @@
# An ACME Shell script: acme.sh [![Build Status](https://travis-ci.org/Neilpang/acme.sh.svg?branch=master)](https://travis-ci.org/Neilpang/acme.sh) # An ACME Shell script: acme.sh [![Build Status](https://travis-ci.org/acmesh-official/acme.sh.svg?branch=master)](https://travis-ci.org/acmesh-official/acme.sh)
[![Join the chat at https://gitter.im/acme-sh/Lobby](https://badges.gitter.im/acme-sh/Lobby.svg)](https://gitter.im/acme-sh/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) <a href="https://opencollective.com/acmesh" alt="Financial Contributors on Open Collective"><img src="https://opencollective.com/acmesh/all/badge.svg?label=financial+contributors" /></a> [![Join the chat at https://gitter.im/acme-sh/Lobby](https://badges.gitter.im/acme-sh/Lobby.svg)](https://gitter.im/acme-sh/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
- An ACME protocol client written purely in Shell (Unix shell) language. - An ACME protocol client written purely in Shell (Unix shell) language.
- Full ACME protocol implementation. - Full ACME protocol implementation.
- Support ACME v1 and ACME v2 - Support ACME v1 and ACME v2
@ -17,14 +17,14 @@
It's probably the `easiest & smartest` shell script to automatically issue & renew the free certificates from Let's Encrypt. It's probably the `easiest & smartest` shell script to automatically issue & renew the free certificates from Let's Encrypt.
Wiki: https://github.com/Neilpang/acme.sh/wiki Wiki: https://github.com/acmesh-official/acme.sh/wiki
For Docker Fans: [acme.sh :two_hearts: Docker ](https://github.com/Neilpang/acme.sh/wiki/Run-acme.sh-in-docker) For Docker Fans: [acme.sh :two_hearts: Docker ](https://github.com/acmesh-official/acme.sh/wiki/Run-acme.sh-in-docker)
Twitter: [@neilpangxa](https://twitter.com/neilpangxa) Twitter: [@neilpangxa](https://twitter.com/neilpangxa)
# [中文说明](https://github.com/Neilpang/acme.sh/wiki/%E8%AF%B4%E6%98%8E) # [中文说明](https://github.com/acmesh-official/acme.sh/wiki/%E8%AF%B4%E6%98%8E)
# Who: # Who:
- [FreeBSD.org](https://blog.crashed.org/letsencrypt-in-freebsd-org/) - [FreeBSD.org](https://blog.crashed.org/letsencrypt-in-freebsd-org/)
@ -40,41 +40,41 @@ Twitter: [@neilpangxa](https://twitter.com/neilpangxa)
- [opnsense.org](https://github.com/opnsense/plugins/tree/master/security/acme-client/src/opnsense/scripts/OPNsense/AcmeClient) - [opnsense.org](https://github.com/opnsense/plugins/tree/master/security/acme-client/src/opnsense/scripts/OPNsense/AcmeClient)
- [CentOS Web Panel](http://centos-webpanel.com/) - [CentOS Web Panel](http://centos-webpanel.com/)
- [lnmp.org](https://lnmp.org/) - [lnmp.org](https://lnmp.org/)
- [more...](https://github.com/Neilpang/acme.sh/wiki/Blogs-and-tutorials) - [more...](https://github.com/acmesh-official/acme.sh/wiki/Blogs-and-tutorials)
# Tested OS # Tested OS
| NO | Status| Platform| | NO | Status| Platform|
|----|-------|---------| |----|-------|---------|
|1|[![](https://neilpang.github.io/acmetest/status/ubuntu-latest.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)| Ubuntu |1|[![](https://acmesh-official.github.io/acmetest/status/ubuntu-latest.svg)](https://github.com/acmesh-official/letest#here-are-the-latest-status)| Ubuntu
|2|[![](https://neilpang.github.io/acmetest/status/debian-latest.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)| Debian |2|[![](https://acmesh-official.github.io/acmetest/status/debian-latest.svg)](https://github.com/acmesh-official/letest#here-are-the-latest-status)| Debian
|3|[![](https://neilpang.github.io/acmetest/status/centos-latest.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|CentOS |3|[![](https://acmesh-official.github.io/acmetest/status/centos-latest.svg)](https://github.com/acmesh-official/letest#here-are-the-latest-status)|CentOS
|4|[![](https://neilpang.github.io/acmetest/status/windows-cygwin.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|Windows (cygwin with curl, openssl and crontab included) |4|[![](https://acmesh-official.github.io/acmetest/status/windows-cygwin.svg)](https://github.com/acmesh-official/letest#here-are-the-latest-status)|Windows (cygwin with curl, openssl and crontab included)
|5|[![](https://neilpang.github.io/acmetest/status/freebsd.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|FreeBSD |5|[![](https://acmesh-official.github.io/acmetest/status/freebsd.svg)](https://github.com/acmesh-official/letest#here-are-the-latest-status)|FreeBSD
|6|[![](https://neilpang.github.io/acmetest/status/pfsense.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|pfsense |6|[![](https://acmesh-official.github.io/acmetest/status/pfsense.svg)](https://github.com/acmesh-official/letest#here-are-the-latest-status)|pfsense
|7|[![](https://neilpang.github.io/acmetest/status/opensuse-latest.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|openSUSE |7|[![](https://acmesh-official.github.io/acmetest/status/opensuse-latest.svg)](https://github.com/acmesh-official/letest#here-are-the-latest-status)|openSUSE
|8|[![](https://neilpang.github.io/acmetest/status/alpine-latest.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|Alpine Linux (with curl) |8|[![](https://acmesh-official.github.io/acmetest/status/alpine-latest.svg)](https://github.com/acmesh-official/letest#here-are-the-latest-status)|Alpine Linux (with curl)
|9|[![](https://neilpang.github.io/acmetest/status/base-archlinux.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|Archlinux |9|[![](https://acmesh-official.github.io/acmetest/status/base-archlinux.svg)](https://github.com/acmesh-official/letest#here-are-the-latest-status)|Archlinux
|10|[![](https://neilpang.github.io/acmetest/status/fedora-latest.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|fedora |10|[![](https://acmesh-official.github.io/acmetest/status/fedora-latest.svg)](https://github.com/acmesh-official/letest#here-are-the-latest-status)|fedora
|11|[![](https://neilpang.github.io/acmetest/status/kalilinux-kali-linux-docker.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|Kali Linux |11|[![](https://acmesh-official.github.io/acmetest/status/kalilinux-kali-linux-docker.svg)](https://github.com/acmesh-official/letest#here-are-the-latest-status)|Kali Linux
|12|[![](https://neilpang.github.io/acmetest/status/oraclelinux-latest.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|Oracle Linux |12|[![](https://acmesh-official.github.io/acmetest/status/oraclelinux-latest.svg)](https://github.com/acmesh-official/letest#here-are-the-latest-status)|Oracle Linux
|13|[![](https://neilpang.github.io/acmetest/status/proxmox.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)| Proxmox https://pve.proxmox.com/wiki/HTTPSCertificateConfiguration#Let.27s_Encrypt_using_acme.sh |13|[![](https://acmesh-official.github.io/acmetest/status/proxmox.svg)](https://github.com/acmesh-official/letest#here-are-the-latest-status)| Proxmox https://pve.proxmox.com/wiki/HTTPSCertificateConfiguration#Let.27s_Encrypt_using_acme.sh
|14|-----| Cloud Linux https://github.com/Neilpang/le/issues/111 |14|-----| Cloud Linux https://github.com/acmesh-official/acme.sh/issues/111
|15|[![](https://neilpang.github.io/acmetest/status/openbsd.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|OpenBSD |15|[![](https://acmesh-official.github.io/acmetest/status/openbsd.svg)](https://github.com/acmesh-official/letest#here-are-the-latest-status)|OpenBSD
|16|[![](https://neilpang.github.io/acmetest/status/mageia.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|Mageia |16|[![](https://acmesh-official.github.io/acmetest/status/mageia.svg)](https://github.com/acmesh-official/letest#here-are-the-latest-status)|Mageia
|17|-----| OpenWRT: Tested and working. See [wiki page](https://github.com/Neilpang/acme.sh/wiki/How-to-run-on-OpenWRT) |17|-----| OpenWRT: Tested and working. See [wiki page](https://github.com/acmesh-official/acme.sh/wiki/How-to-run-on-OpenWRT)
|18|[![](https://neilpang.github.io/acmetest/status/solaris.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|SunOS/Solaris |18|[![](https://acmesh-official.github.io/acmetest/status/solaris.svg)](https://github.com/acmesh-official/letest#here-are-the-latest-status)|SunOS/Solaris
|19|[![](https://neilpang.github.io/acmetest/status/gentoo-stage3-amd64.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|Gentoo Linux |19|[![](https://acmesh-official.github.io/acmetest/status/gentoo-stage3-amd64.svg)](https://github.com/acmesh-official/letest#here-are-the-latest-status)|Gentoo Linux
|20|[![Build Status](https://travis-ci.org/Neilpang/acme.sh.svg?branch=master)](https://travis-ci.org/Neilpang/acme.sh)|Mac OSX |20|[![Build Status](https://travis-ci.org/acmesh-official/acme.sh.svg?branch=master)](https://travis-ci.org/acmesh-official/acme.sh)|Mac OSX
For all build statuses, check our [weekly build project](https://github.com/Neilpang/acmetest): For all build statuses, check our [weekly build project](https://github.com/acmesh-official/acmetest):
https://github.com/Neilpang/acmetest https://github.com/acmesh-official/acmetest
# Supported CA # Supported CA
- Letsencrypt.org CA(default) - Letsencrypt.org CA(default)
- [BuyPass.com CA](https://github.com/Neilpang/acme.sh/wiki/BuyPass.com-CA) - [BuyPass.com CA](https://github.com/acmesh-official/acme.sh/wiki/BuyPass.com-CA)
- [Pebble strict Mode](https://github.com/letsencrypt/pebble) - [Pebble strict Mode](https://github.com/letsencrypt/pebble)
# Supported modes # Supported modes
@ -85,15 +85,15 @@ https://github.com/Neilpang/acmetest
- Apache mode - Apache mode
- Nginx mode - Nginx mode
- DNS mode - DNS mode
- [DNS alias mode](https://github.com/Neilpang/acme.sh/wiki/DNS-alias-mode) - [DNS alias mode](https://github.com/acmesh-official/acme.sh/wiki/DNS-alias-mode)
- [Stateless mode](https://github.com/Neilpang/acme.sh/wiki/Stateless-Mode) - [Stateless mode](https://github.com/acmesh-official/acme.sh/wiki/Stateless-Mode)
# 1. How to install # 1. How to install
### 1. Install online ### 1. Install online
Check this project: https://github.com/Neilpang/get.acme.sh Check this project: https://github.com/acmesh-official/get.acme.sh
```bash ```bash
curl https://get.acme.sh | sh curl https://get.acme.sh | sh
@ -111,14 +111,14 @@ wget -O - https://get.acme.sh | sh
Clone this project and launch installation: Clone this project and launch installation:
```bash ```bash
git clone https://github.com/Neilpang/acme.sh.git git clone https://github.com/acmesh-official/acme.sh.git
cd ./acme.sh cd ./acme.sh
./acme.sh --install ./acme.sh --install
``` ```
You `don't have to be root` then, although `it is recommended`. You `don't have to be root` then, although `it is recommended`.
Advanced Installation: https://github.com/Neilpang/acme.sh/wiki/How-to-install Advanced Installation: https://github.com/acmesh-official/acme.sh/wiki/How-to-install
The installer will perform 3 actions: The installer will perform 3 actions:
@ -180,7 +180,7 @@ The certs will be placed in `~/.acme.sh/example.com/`
The certs will be renewed automatically every **60** days. The certs will be renewed automatically every **60** days.
More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert More examples: https://github.com/acmesh-official/acme.sh/wiki/How-to-issue-a-cert
# 3. Install the cert to Apache/Nginx etc. # 3. Install the cert to Apache/Nginx etc.
@ -226,7 +226,7 @@ Port `80` (TCP) **MUST** be free to listen on, otherwise you will be prompted to
acme.sh --issue --standalone -d example.com -d www.example.com -d cp.example.com 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 More examples: https://github.com/acmesh-official/acme.sh/wiki/How-to-issue-a-cert
# 5. Use Standalone ssl server to issue cert # 5. Use Standalone ssl server to issue cert
@ -238,7 +238,7 @@ Port `443` (TCP) **MUST** be free to listen on, otherwise you will be prompted t
acme.sh --issue --alpn -d example.com -d www.example.com -d cp.example.com acme.sh --issue --alpn -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 More examples: https://github.com/acmesh-official/acme.sh/wiki/How-to-issue-a-cert
# 6. Use Apache mode # 6. Use Apache mode
@ -257,9 +257,9 @@ acme.sh --issue --apache -d example.com -d www.example.com -d cp.example.com
**This apache mode is only to issue the cert, it will not change your apache config files. **This apache mode is only to issue the cert, it will not change your apache config files.
You will need to configure your website config files to use the cert by yourself. You will need to configure your website config files to use the cert by yourself.
We don't want to mess your apache server, don't worry.** We don't want to mess with your apache server, don't worry.**
More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert More examples: https://github.com/acmesh-official/acme.sh/wiki/How-to-issue-a-cert
# 7. Use Nginx mode # 7. Use Nginx mode
@ -281,9 +281,9 @@ acme.sh --issue --nginx -d example.com -d www.example.com -d cp.example.com
**This nginx mode is only to issue the cert, it will not change your nginx config files. **This nginx mode is only to issue the cert, it will not change your nginx config files.
You will need to configure your website config files to use the cert by yourself. You will need to configure your website config files to use the cert by yourself.
We don't want to mess your nginx server, don't worry.** We don't want to mess with your nginx server, don't worry.**
More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert More examples: https://github.com/acmesh-official/acme.sh/wiki/How-to-issue-a-cert
# 8. Automatic DNS API integration # 8. Automatic DNS API integration
@ -293,11 +293,11 @@ You don't have to do anything manually!
### Currently acme.sh supports most of the dns providers: ### Currently acme.sh supports most of the dns providers:
https://github.com/Neilpang/acme.sh/wiki/dnsapi https://github.com/acmesh-official/acme.sh/wiki/dnsapi
# 9. Use DNS manual mode: # 9. Use DNS manual mode:
See: https://github.com/Neilpang/acme.sh/wiki/dns-manual-mode first. See: https://github.com/acmesh-official/acme.sh/wiki/dns-manual-mode first.
If your dns provider doesn't support any api access, you can add the txt record by your hand. If your dns provider doesn't support any api access, you can add the txt record by your hand.
@ -430,12 +430,12 @@ acme.sh --upgrade --auto-upgrade 0
# 15. Issue a cert from an existing CSR # 15. Issue a cert from an existing CSR
https://github.com/Neilpang/acme.sh/wiki/Issue-a-cert-from-existing-CSR https://github.com/acmesh-official/acme.sh/wiki/Issue-a-cert-from-existing-CSR
# 16. Send notifications in cronjob # 16. Send notifications in cronjob
https://github.com/Neilpang/acme.sh/wiki/notify https://github.com/acmesh-official/acme.sh/wiki/notify
# 17. Under the Hood # 17. Under the Hood
@ -451,13 +451,43 @@ TODO:
2. ACME protocol: https://github.com/ietf-wg-acme/acme 2. ACME protocol: https://github.com/ietf-wg-acme/acme
## Contributors
### Code Contributors
This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)].
<a href="https://github.com/acmesh-official/acme.sh/graphs/contributors"><img src="https://opencollective.com/acmesh/contributors.svg?width=890&button=false" /></a>
### Financial Contributors
Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/acmesh/contribute)]
#### Individuals
<a href="https://opencollective.com/acmesh"><img src="https://opencollective.com/acmesh/individuals.svg?width=890"></a>
#### Organizations
Support this project with your organization. Your logo will show up here with a link to your website. [[Contribute](https://opencollective.com/acmesh/contribute)]
<a href="https://opencollective.com/acmesh/organization/0/website"><img src="https://opencollective.com/acmesh/organization/0/avatar.svg"></a>
<a href="https://opencollective.com/acmesh/organization/1/website"><img src="https://opencollective.com/acmesh/organization/1/avatar.svg"></a>
<a href="https://opencollective.com/acmesh/organization/2/website"><img src="https://opencollective.com/acmesh/organization/2/avatar.svg"></a>
<a href="https://opencollective.com/acmesh/organization/3/website"><img src="https://opencollective.com/acmesh/organization/3/avatar.svg"></a>
<a href="https://opencollective.com/acmesh/organization/4/website"><img src="https://opencollective.com/acmesh/organization/4/avatar.svg"></a>
<a href="https://opencollective.com/acmesh/organization/5/website"><img src="https://opencollective.com/acmesh/organization/5/avatar.svg"></a>
<a href="https://opencollective.com/acmesh/organization/6/website"><img src="https://opencollective.com/acmesh/organization/6/avatar.svg"></a>
<a href="https://opencollective.com/acmesh/organization/7/website"><img src="https://opencollective.com/acmesh/organization/7/avatar.svg"></a>
<a href="https://opencollective.com/acmesh/organization/8/website"><img src="https://opencollective.com/acmesh/organization/8/avatar.svg"></a>
<a href="https://opencollective.com/acmesh/organization/9/website"><img src="https://opencollective.com/acmesh/organization/9/avatar.svg"></a>
# 19. License & Others # 19. License & Others
License is GPLv3 License is GPLv3
Please Star and Fork me. Please Star and Fork me.
[Issues](https://github.com/Neilpang/acme.sh/issues) and [pull requests](https://github.com/Neilpang/acme.sh/pulls) are welcome. [Issues](https://github.com/acmesh-official/acme.sh/issues) and [pull requests](https://github.com/acmesh-official/acme.sh/pulls) are welcome.
# 20. Donate # 20. Donate
@ -465,4 +495,4 @@ Your donation makes **acme.sh** better:
1. PayPal/Alipay(支付宝)/Wechat(微信): [https://donate.acme.sh/](https://donate.acme.sh/) 1. PayPal/Alipay(支付宝)/Wechat(微信): [https://donate.acme.sh/](https://donate.acme.sh/)
[Donate List](https://github.com/Neilpang/acme.sh/wiki/Donate-list) [Donate List](https://github.com/acmesh-official/acme.sh/wiki/Donate-list)

368
acme.sh
View File

@ -1,12 +1,12 @@
#!/usr/bin/env sh #!/usr/bin/env sh
VER=2.8.2 VER=2.8.6
PROJECT_NAME="acme.sh" PROJECT_NAME="acme.sh"
PROJECT_ENTRY="acme.sh" PROJECT_ENTRY="acme.sh"
PROJECT="https://github.com/Neilpang/$PROJECT_NAME" PROJECT="https://github.com/acmesh-official/$PROJECT_NAME"
DEFAULT_INSTALL_HOME="$HOME/.$PROJECT_NAME" DEFAULT_INSTALL_HOME="$HOME/.$PROJECT_NAME"
@ -90,6 +90,9 @@ DEBUG_LEVEL_3=3
DEBUG_LEVEL_DEFAULT=$DEBUG_LEVEL_1 DEBUG_LEVEL_DEFAULT=$DEBUG_LEVEL_1
DEBUG_LEVEL_NONE=0 DEBUG_LEVEL_NONE=0
DOH_CLOUDFLARE=1
DOH_GOOGLE=2
HIDDEN_VALUE="[hidden](please add '--output-insecure' to see this value)" HIDDEN_VALUE="[hidden](please add '--output-insecure' to see this value)"
SYSLOG_ERROR="user.error" SYSLOG_ERROR="user.error"
@ -123,17 +126,19 @@ NOTIFY_MODE_CERT=1
NOTIFY_MODE_DEFAULT=$NOTIFY_MODE_BULK NOTIFY_MODE_DEFAULT=$NOTIFY_MODE_BULK
_DEBUG_WIKI="https://github.com/Neilpang/acme.sh/wiki/How-to-debug-acme.sh" _DEBUG_WIKI="https://github.com/acmesh-official/acme.sh/wiki/How-to-debug-acme.sh"
_PREPARE_LINK="https://github.com/Neilpang/acme.sh/wiki/Install-preparations" _PREPARE_LINK="https://github.com/acmesh-official/acme.sh/wiki/Install-preparations"
_STATELESS_WIKI="https://github.com/Neilpang/acme.sh/wiki/Stateless-Mode" _STATELESS_WIKI="https://github.com/acmesh-official/acme.sh/wiki/Stateless-Mode"
_DNS_ALIAS_WIKI="https://github.com/Neilpang/acme.sh/wiki/DNS-alias-mode" _DNS_ALIAS_WIKI="https://github.com/acmesh-official/acme.sh/wiki/DNS-alias-mode"
_DNS_MANUAL_WIKI="https://github.com/Neilpang/acme.sh/wiki/dns-manual-mode" _DNS_MANUAL_WIKI="https://github.com/acmesh-official/acme.sh/wiki/dns-manual-mode"
_NOTIFY_WIKI="https://github.com/Neilpang/acme.sh/wiki/notify" _NOTIFY_WIKI="https://github.com/acmesh-official/acme.sh/wiki/notify"
_SUDO_WIKI="https://github.com/acmesh-official/acme.sh/wiki/sudo"
_DNS_MANUAL_ERR="The dns manual mode can not renew automatically, you must issue it again manually. You'd better use the other modes instead." _DNS_MANUAL_ERR="The dns manual mode can not renew automatically, you must issue it again manually. You'd better use the other modes instead."
@ -148,7 +153,7 @@ fi
__green() { __green() {
if [ "${__INTERACTIVE}${ACME_NO_COLOR:-0}" = "10" -o "${ACME_FORCE_COLOR}" = "1" ]; then if [ "${__INTERACTIVE}${ACME_NO_COLOR:-0}" = "10" -o "${ACME_FORCE_COLOR}" = "1" ]; then
printf '\033[1;31;32m%b\033[0m' "$1" printf '\33[1;32m%b\33[0m' "$1"
return return
fi fi
printf -- "%b" "$1" printf -- "%b" "$1"
@ -156,7 +161,7 @@ __green() {
__red() { __red() {
if [ "${__INTERACTIVE}${ACME_NO_COLOR:-0}" = "10" -o "${ACME_FORCE_COLOR}" = "1" ]; then if [ "${__INTERACTIVE}${ACME_NO_COLOR:-0}" = "10" -o "${ACME_FORCE_COLOR}" = "1" ]; then
printf '\033[1;31;40m%b\033[0m' "$1" printf '\33[1;31m%b\33[0m' "$1"
return return
fi fi
printf -- "%b" "$1" printf -- "%b" "$1"
@ -173,7 +178,7 @@ _printargs() {
printf -- "%s" "$1='$2'" printf -- "%s" "$1='$2'"
fi fi
printf "\n" printf "\n"
# return the saved exit status # return the saved exit status
return "$_exitstatus" return "$_exitstatus"
} }
@ -202,7 +207,7 @@ _dlg_versions() {
echo "socat:" echo "socat:"
if _exists "socat"; then if _exists "socat"; then
socat -h 2>&1 socat -V 2>&1
else else
_debug "socat doesn't exists." _debug "socat doesn't exists."
fi fi
@ -260,6 +265,37 @@ _usage() {
printf "\n" >&2 printf "\n" >&2
} }
__debug_bash_helper() {
# At this point only do for --debug 3
if [ "${DEBUG:-$DEBUG_LEVEL_NONE}" -lt "$DEBUG_LEVEL_3" ]; then
return
fi
# Return extra debug info when running with bash, otherwise return empty
# string.
if [ -z "${BASH_VERSION}" ]; then
return
fi
# We are a bash shell at this point, return the filename, function name, and
# line number as a string
_dbh_saveIFS=$IFS
IFS=" "
# Must use eval or syntax error happens under dash. The eval should use
# single quotes as older versions of busybox had a bug with double quotes and
# eval.
# Use 'caller 1' as we want one level up the stack as we should be called
# by one of the _debug* functions
eval '_dbh_called=($(caller 1))'
IFS=$_dbh_saveIFS
eval '_dbh_file=${_dbh_called[2]}'
if [ -n "${_script_home}" ]; then
# Trim off the _script_home directory name
eval '_dbh_file=${_dbh_file#$_script_home/}'
fi
eval '_dbh_function=${_dbh_called[1]}'
eval '_dbh_lineno=${_dbh_called[0]}'
printf "%-40s " "$_dbh_file:${_dbh_function}:${_dbh_lineno}"
}
_debug() { _debug() {
if [ "${LOG_LEVEL:-$DEFAULT_LOG_LEVEL}" -ge "$LOG_LEVEL_1" ]; then if [ "${LOG_LEVEL:-$DEFAULT_LOG_LEVEL}" -ge "$LOG_LEVEL_1" ]; then
_log "$@" _log "$@"
@ -268,7 +304,8 @@ _debug() {
_syslog "$SYSLOG_DEBUG" "$@" _syslog "$SYSLOG_DEBUG" "$@"
fi fi
if [ "${DEBUG:-$DEBUG_LEVEL_NONE}" -ge "$DEBUG_LEVEL_1" ]; then if [ "${DEBUG:-$DEBUG_LEVEL_NONE}" -ge "$DEBUG_LEVEL_1" ]; then
_printargs "$@" >&2 _bash_debug=$(__debug_bash_helper)
_printargs "${_bash_debug}$@" >&2
fi fi
} }
@ -301,7 +338,8 @@ _debug2() {
_syslog "$SYSLOG_DEBUG" "$@" _syslog "$SYSLOG_DEBUG" "$@"
fi fi
if [ "${DEBUG:-$DEBUG_LEVEL_NONE}" -ge "$DEBUG_LEVEL_2" ]; then if [ "${DEBUG:-$DEBUG_LEVEL_NONE}" -ge "$DEBUG_LEVEL_2" ]; then
_printargs "$@" >&2 _bash_debug=$(__debug_bash_helper)
_printargs "${_bash_debug}$@" >&2
fi fi
} }
@ -333,7 +371,8 @@ _debug3() {
_syslog "$SYSLOG_DEBUG" "$@" _syslog "$SYSLOG_DEBUG" "$@"
fi fi
if [ "${DEBUG:-$DEBUG_LEVEL_NONE}" -ge "$DEBUG_LEVEL_3" ]; then if [ "${DEBUG:-$DEBUG_LEVEL_NONE}" -ge "$DEBUG_LEVEL_3" ]; then
_printargs "$@" >&2 _bash_debug=$(__debug_bash_helper)
_printargs "${_bash_debug}$@" >&2
fi fi
} }
@ -1695,18 +1734,37 @@ _post() {
if [ "$HTTPS_INSECURE" ]; then if [ "$HTTPS_INSECURE" ]; then
_CURL="$_CURL --insecure " _CURL="$_CURL --insecure "
fi fi
if [ "$httpmethod" = "HEAD" ]; then
_CURL="$_CURL -I "
fi
_debug "_CURL" "$_CURL" _debug "_CURL" "$_CURL"
if [ "$needbase64" ]; then if [ "$needbase64" ]; then
if [ "$_postContentType" ]; then if [ "$body" ]; then
response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "Content-Type: $_postContentType" -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$_post_url" | _base64)" if [ "$_postContentType" ]; then
response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "Content-Type: $_postContentType" -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$_post_url" | _base64)"
else
response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$_post_url" | _base64)"
fi
else else
response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$_post_url" | _base64)" if [ "$_postContentType" ]; then
response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "Content-Type: $_postContentType" -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" "$_post_url" | _base64)"
else
response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" "$_post_url" | _base64)"
fi
fi fi
else else
if [ "$_postContentType" ]; then if [ "$body" ]; then
response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "Content-Type: $_postContentType" -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$_post_url")" if [ "$_postContentType" ]; then
response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "Content-Type: $_postContentType" -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$_post_url")"
else
response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$_post_url")"
fi
else else
response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$_post_url")" if [ "$_postContentType" ]; then
response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "Content-Type: $_postContentType" -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" "$_post_url")"
else
response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" "$_post_url")"
fi
fi fi
fi fi
_ret="$?" _ret="$?"
@ -1722,6 +1780,9 @@ _post() {
if [ "$HTTPS_INSECURE" ]; then if [ "$HTTPS_INSECURE" ]; then
_WGET="$_WGET --no-check-certificate " _WGET="$_WGET --no-check-certificate "
fi fi
if [ "$httpmethod" = "HEAD" ]; then
_WGET="$_WGET --read-timeout=3.0 --tries=2 "
fi
_debug "_WGET" "$_WGET" _debug "_WGET" "$_WGET"
if [ "$needbase64" ]; then if [ "$needbase64" ]; then
if [ "$httpmethod" = "POST" ]; then if [ "$httpmethod" = "POST" ]; then
@ -1744,6 +1805,12 @@ _post() {
else else
response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --post-data="$body" "$_post_url" 2>"$HTTP_HEADER")" response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --post-data="$body" "$_post_url" 2>"$HTTP_HEADER")"
fi fi
elif [ "$httpmethod" = "HEAD" ]; then
if [ "$_postContentType" ]; then
response="$($_WGET --spider -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --header "Content-Type: $_postContentType" --post-data="$body" "$_post_url" 2>"$HTTP_HEADER")"
else
response="$($_WGET --spider -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --post-data="$body" "$_post_url" 2>"$HTTP_HEADER")"
fi
else else
if [ "$_postContentType" ]; then if [ "$_postContentType" ]; then
response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --header "Content-Type: $_postContentType" --method $httpmethod --body-data="$body" "$_post_url" 2>"$HTTP_HEADER")" response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --header "Content-Type: $_postContentType" --method $httpmethod --body-data="$body" "$_post_url" 2>"$HTTP_HEADER")"
@ -1876,7 +1943,7 @@ _send_signed_request() {
if [ "$ACME_NEW_NONCE" ]; then if [ "$ACME_NEW_NONCE" ]; then
_debug2 "Get nonce with HEAD. ACME_NEW_NONCE" "$ACME_NEW_NONCE" _debug2 "Get nonce with HEAD. ACME_NEW_NONCE" "$ACME_NEW_NONCE"
nonceurl="$ACME_NEW_NONCE" nonceurl="$ACME_NEW_NONCE"
if _post "" "$nonceurl" "" "HEAD" "$__request_conent_type"; then if _post "" "$nonceurl" "" "HEAD" "$__request_conent_type" >/dev/null; then
_headers="$(cat "$HTTP_HEADER")" _headers="$(cat "$HTTP_HEADER")"
_debug2 _headers "$_headers" _debug2 _headers "$_headers"
_CACHED_NONCE="$(echo "$_headers" | grep -i "Replay-Nonce:" | _head_n 1 | tr -d "\r\n " | cut -d ':' -f 2)" _CACHED_NONCE="$(echo "$_headers" | grep -i "Replay-Nonce:" | _head_n 1 | tr -d "\r\n " | cut -d ':' -f 2)"
@ -1952,7 +2019,7 @@ _send_signed_request() {
_debug code "$code" _debug code "$code"
_debug2 original "$response" _debug2 original "$response"
if echo "$responseHeaders" | grep -i "Content-Type: application/json" >/dev/null 2>&1; then if echo "$responseHeaders" | grep -i "Content-Type: *application/json" >/dev/null 2>&1; then
response="$(echo "$response" | _normalizeJson)" response="$(echo "$response" | _normalizeJson)"
fi fi
_debug2 response "$response" _debug2 response "$response"
@ -1973,8 +2040,10 @@ _send_signed_request() {
continue continue
fi fi
fi fi
break return 0
done done
_info "Giving up sending to CA server after $MAX_REQUEST_RETRY_TIMES retries."
return 1
} }
@ -2097,7 +2166,7 @@ _getdeployconf() {
return 0 # do nothing return 0 # do nothing
fi fi
_saved=$(_readdomainconf "SAVED_$_rac_key") _saved=$(_readdomainconf "SAVED_$_rac_key")
eval "export $_rac_key=$_saved" eval "export $_rac_key=\"$_saved\""
} }
#_saveaccountconf key value base64encode #_saveaccountconf key value base64encode
@ -2346,7 +2415,7 @@ __initHome() {
if [ -z "$ACCOUNT_CONF_PATH" ]; then if [ -z "$ACCOUNT_CONF_PATH" ]; then
ACCOUNT_CONF_PATH="$_DEFAULT_ACCOUNT_CONF_PATH" ACCOUNT_CONF_PATH="$_DEFAULT_ACCOUNT_CONF_PATH"
fi fi
_debug3 ACCOUNT_CONF_PATH "$ACCOUNT_CONF_PATH"
DEFAULT_LOG_FILE="$LE_CONFIG_HOME/$PROJECT_NAME.log" DEFAULT_LOG_FILE="$LE_CONFIG_HOME/$PROJECT_NAME.log"
DEFAULT_CA_HOME="$LE_CONFIG_HOME/ca" DEFAULT_CA_HOME="$LE_CONFIG_HOME/ca"
@ -2797,6 +2866,11 @@ _setNginx() {
_debug NGINX_CONF "$NGINX_CONF" _debug NGINX_CONF "$NGINX_CONF"
NGINX_CONF="$(echo "$NGINX_CONF" | cut -d = -f 2)" NGINX_CONF="$(echo "$NGINX_CONF" | cut -d = -f 2)"
_debug NGINX_CONF "$NGINX_CONF" _debug NGINX_CONF "$NGINX_CONF"
if [ -z "$NGINX_CONF" ]; then
_err "Can not find nginx conf."
NGINX_CONF=""
return 1
fi
if [ ! -f "$NGINX_CONF" ]; then if [ ! -f "$NGINX_CONF" ]; then
_err "'$NGINX_CONF' doesn't exist." _err "'$NGINX_CONF' doesn't exist."
NGINX_CONF="" NGINX_CONF=""
@ -3265,6 +3339,11 @@ _on_issue_success() {
if [ "$_chk_post_hook" ]; then if [ "$_chk_post_hook" ]; then
_info "Run post hook:'$_chk_post_hook'" _info "Run post hook:'$_chk_post_hook'"
if ! ( if ! (
export CERT_PATH
export CERT_KEY_PATH
export CA_CERT_PATH
export CERT_FULLCHAIN_PATH
export Le_Domain="$_main_domain"
cd "$DOMAIN_PATH" && eval "$_chk_post_hook" cd "$DOMAIN_PATH" && eval "$_chk_post_hook"
); then ); then
_err "Error when run post hook." _err "Error when run post hook."
@ -3276,6 +3355,11 @@ _on_issue_success() {
if [ "$IS_RENEW" ] && [ "$_chk_renew_hook" ]; then if [ "$IS_RENEW" ] && [ "$_chk_renew_hook" ]; then
_info "Run renew hook:'$_chk_renew_hook'" _info "Run renew hook:'$_chk_renew_hook'"
if ! ( if ! (
export CERT_PATH
export CERT_KEY_PATH
export CA_CERT_PATH
export CERT_FULLCHAIN_PATH
export Le_Domain="$_main_domain"
cd "$DOMAIN_PATH" && eval "$_chk_renew_hook" cd "$DOMAIN_PATH" && eval "$_chk_renew_hook"
); then ); then
_err "Error when run renew hook." _err "Error when run renew hook."
@ -3283,7 +3367,7 @@ _on_issue_success() {
fi fi
fi fi
if _hasfield "$Le_Webroot" "$W_DNS"; then if _hasfield "$Le_Webroot" "$W_DNS" && [ -z "$FORCE_DNS_MANUAL" ]; then
_err "$_DNS_MANUAL_WARN" _err "$_DNS_MANUAL_WARN"
fi fi
@ -3363,7 +3447,7 @@ _regAccount() {
fi fi
_debug2 responseHeaders "$responseHeaders" _debug2 responseHeaders "$responseHeaders"
_accUri="$(echo "$responseHeaders" | grep -i "^Location:" | _head_n 1 | cut -d ' ' -f 2 | tr -d "\r\n")" _accUri="$(echo "$responseHeaders" | grep -i "^Location:" | _head_n 1 | cut -d ':' -f 2- | tr -d "\r\n ")"
_debug "_accUri" "$_accUri" _debug "_accUri" "$_accUri"
if [ -z "$_accUri" ]; then if [ -z "$_accUri" ]; then
_err "Can not find account id url." _err "Can not find account id url."
@ -3591,7 +3675,7 @@ __trigger_validation() {
} }
#endpoint domain type #endpoint domain type
_ns_lookup() { _ns_lookup_impl() {
_ns_ep="$1" _ns_ep="$1"
_ns_domain="$2" _ns_domain="$2"
_ns_type="$3" _ns_type="$3"
@ -3615,7 +3699,7 @@ _ns_lookup_cf() {
_cf_ld="$1" _cf_ld="$1"
_cf_ld_type="$2" _cf_ld_type="$2"
_cf_ep="https://cloudflare-dns.com/dns-query" _cf_ep="https://cloudflare-dns.com/dns-query"
_ns_lookup "$_cf_ep" "$_cf_ld" "$_cf_ld_type" _ns_lookup_impl "$_cf_ep" "$_cf_ld" "$_cf_ld_type"
} }
#domain, type #domain, type
@ -3623,11 +3707,49 @@ _ns_purge_cf() {
_cf_d="$1" _cf_d="$1"
_cf_d_type="$2" _cf_d_type="$2"
_debug "Cloudflare purge $_cf_d_type record for domain $_cf_d" _debug "Cloudflare purge $_cf_d_type record for domain $_cf_d"
_cf_purl="https://1.0.0.1/api/v1/purge?domain=$_cf_d&type=$_cf_d_type" _cf_purl="https://cloudflare-dns.com/api/v1/purge?domain=$_cf_d&type=$_cf_d_type"
response="$(_post "" "$_cf_purl")" response="$(_post "" "$_cf_purl")"
_debug2 response "$response" _debug2 response "$response"
} }
#checks if cf server is available
_ns_is_available_cf() {
if _get "https://cloudflare-dns.com" >/dev/null 2>&1; then
return 0
else
return 1
fi
}
#domain, type
_ns_lookup_google() {
_cf_ld="$1"
_cf_ld_type="$2"
_cf_ep="https://dns.google/resolve"
_ns_lookup_impl "$_cf_ep" "$_cf_ld" "$_cf_ld_type"
}
#domain, type
_ns_lookup() {
if [ -z "$DOH_USE" ]; then
_debug "Detect dns server first."
if _ns_is_available_cf; then
_debug "Use cloudflare doh server"
export DOH_USE=$DOH_CLOUDFLARE
else
_debug "Use google doh server"
export DOH_USE=$DOH_GOOGLE
fi
fi
if [ "$DOH_USE" = "$DOH_CLOUDFLARE" ] || [ -z "$DOH_USE" ]; then
_ns_lookup_cf "$@"
else
_ns_lookup_google "$@"
fi
}
#txtdomain, alias, txt #txtdomain, alias, txt
__check_txt() { __check_txt() {
_c_txtdomain="$1" _c_txtdomain="$1"
@ -3636,7 +3758,7 @@ __check_txt() {
_debug "_c_txtdomain" "$_c_txtdomain" _debug "_c_txtdomain" "$_c_txtdomain"
_debug "_c_aliasdomain" "$_c_aliasdomain" _debug "_c_aliasdomain" "$_c_aliasdomain"
_debug "_c_txt" "$_c_txt" _debug "_c_txt" "$_c_txt"
_answers="$(_ns_lookup_cf "$_c_aliasdomain" TXT)" _answers="$(_ns_lookup "$_c_aliasdomain" TXT)"
_contains "$_answers" "$_c_txt" _contains "$_answers" "$_c_txt"
} }
@ -3645,7 +3767,13 @@ __check_txt() {
__purge_txt() { __purge_txt() {
_p_txtdomain="$1" _p_txtdomain="$1"
_debug _p_txtdomain "$_p_txtdomain" _debug _p_txtdomain "$_p_txtdomain"
_ns_purge_cf "$_p_txtdomain" "TXT" if [ "$DOH_USE" = "$DOH_CLOUDFLARE" ] || [ -z "$DOH_USE" ]; then
_ns_purge_cf "$_p_txtdomain" "TXT"
else
_debug "no purge api for google dns api, just sleep 5 secs"
_sleep 5
fi
} }
#wait and check each dns entries #wait and check each dns entries
@ -3682,20 +3810,22 @@ _check_dns_entries() {
fi fi
_left=1 _left=1
_info "Not valid yet, let's wait 10 seconds and check next one." _info "Not valid yet, let's wait 10 seconds and check next one."
_sleep 10
__purge_txt "$txtdomain" __purge_txt "$txtdomain"
if [ "$txtdomain" != "$aliasDomain" ]; then if [ "$txtdomain" != "$aliasDomain" ]; then
__purge_txt "$aliasDomain" __purge_txt "$aliasDomain"
fi fi
_sleep 10
done done
if [ "$_left" ]; then if [ "$_left" ]; then
_info "Let's wait 10 seconds and check again". _info "Let's wait 10 seconds and check again".
_sleep 10 _sleep 10
else else
_info "All success, let's return" _info "All success, let's return"
break return 0
fi fi
done done
_info "Timed out waiting for DNS."
return 1
} }
@ -3876,7 +4006,7 @@ issue() {
_on_issue_err "$_post_hook" _on_issue_err "$_post_hook"
return 1 return 1
fi fi
Le_LinkOrder="$(echo "$responseHeaders" | grep -i '^Location.*$' | _tail_n 1 | tr -d "\r\n" | cut -d " " -f 2)" Le_LinkOrder="$(echo "$responseHeaders" | grep -i '^Location.*$' | _tail_n 1 | tr -d "\r\n " | cut -d ":" -f 2-)"
_debug Le_LinkOrder "$Le_LinkOrder" _debug Le_LinkOrder "$Le_LinkOrder"
Le_OrderFinalize="$(echo "$response" | _egrep_o '"finalize" *: *"[^"]*"' | cut -d '"' -f 4)" Le_OrderFinalize="$(echo "$response" | _egrep_o '"finalize" *: *"[^"]*"' | cut -d '"' -f 4)"
_debug Le_OrderFinalize "$Le_OrderFinalize" _debug Le_OrderFinalize "$Le_OrderFinalize"
@ -3955,7 +4085,18 @@ $_authorizations_map"
fi fi
if [ "$ACME_VERSION" = "2" ]; then if [ "$ACME_VERSION" = "2" ]; then
response="$(echo "$_authorizations_map" | grep "^$(_idn "$d")," | sed "s/$d,//")" _idn_d="$(_idn "$d")"
_candindates="$(echo "$_authorizations_map" | grep -i "^$_idn_d,")"
_debug2 _candindates "$_candindates"
if [ "$(echo "$_candindates" | wc -l)" -gt 1 ]; then
for _can in $_candindates; do
if _startswith "$(echo "$_can" | tr '.' '|')" "$(echo "$_idn_d" | tr '.' '|'),"; then
_candindates="$_can"
break
fi
done
fi
response="$(echo "$_candindates" | sed "s/$_idn_d,//")"
_debug2 "response" "$response" _debug2 "response" "$response"
if [ -z "$response" ]; then if [ -z "$response" ]; then
_err "get to authz error." _err "get to authz error."
@ -3978,45 +4119,59 @@ $_authorizations_map"
entry="$(echo "$response" | _egrep_o '[^\{]*"type":"'$vtype'"[^\}]*')" entry="$(echo "$response" | _egrep_o '[^\{]*"type":"'$vtype'"[^\}]*')"
_debug entry "$entry" _debug entry "$entry"
keyauthorization=""
if [ -z "$entry" ]; then if [ -z "$entry" ]; then
_err "Error, can not get domain token entry $d" if ! _startswith "$d" '*.'; then
_supported_vtypes="$(echo "$response" | _egrep_o "\"challenges\":\[[^]]*]" | tr '{' "\n" | grep type | cut -d '"' -f 4 | tr "\n" ' ')" _debug "Not a wildcard domain, lets check whether the validation is already valid."
if [ "$_supported_vtypes" ]; then if echo "$response" | grep '"status":"valid"' >/dev/null 2>&1; then
_err "The supported validation types are: $_supported_vtypes, but you specified: $vtype" _debug "$d is already valid."
keyauthorization="$STATE_VERIFIED"
_debug keyauthorization "$keyauthorization"
fi
fi
if [ -z "$keyauthorization" ]; then
_err "Error, can not get domain token entry $d for $vtype"
_supported_vtypes="$(echo "$response" | _egrep_o "\"challenges\":\[[^]]*]" | tr '{' "\n" | grep type | cut -d '"' -f 4 | tr "\n" ' ')"
if [ "$_supported_vtypes" ]; then
_err "The supported validation types are: $_supported_vtypes, but you specified: $vtype"
fi
_clearup
_on_issue_err "$_post_hook"
return 1
fi fi
_clearup
_on_issue_err "$_post_hook"
return 1
fi fi
token="$(echo "$entry" | _egrep_o '"token":"[^"]*' | cut -d : -f 2 | tr -d '"')"
_debug token "$token"
if [ -z "$token" ]; then if [ -z "$keyauthorization" ]; then
_err "Error, can not get domain token $entry" token="$(echo "$entry" | _egrep_o '"token":"[^"]*' | cut -d : -f 2 | tr -d '"')"
_clearup _debug token "$token"
_on_issue_err "$_post_hook"
return 1
fi
if [ "$ACME_VERSION" = "2" ]; then
uri="$(echo "$entry" | _egrep_o '"url":"[^"]*' | cut -d '"' -f 4 | _head_n 1)"
else
uri="$(echo "$entry" | _egrep_o '"uri":"[^"]*' | cut -d '"' -f 4)"
fi
_debug uri "$uri"
if [ -z "$uri" ]; then if [ -z "$token" ]; then
_err "Error, can not get domain uri. $entry" _err "Error, can not get domain token $entry"
_clearup _clearup
_on_issue_err "$_post_hook" _on_issue_err "$_post_hook"
return 1 return 1
fi fi
keyauthorization="$token.$thumbprint" if [ "$ACME_VERSION" = "2" ]; then
_debug keyauthorization "$keyauthorization" uri="$(echo "$entry" | _egrep_o '"url":"[^"]*' | cut -d '"' -f 4 | _head_n 1)"
else
uri="$(echo "$entry" | _egrep_o '"uri":"[^"]*' | cut -d '"' -f 4)"
fi
_debug uri "$uri"
if printf "%s" "$response" | grep '"status":"valid"' >/dev/null 2>&1; then if [ -z "$uri" ]; then
_debug "$d is already verified." _err "Error, can not get domain uri. $entry"
keyauthorization="$STATE_VERIFIED" _clearup
_on_issue_err "$_post_hook"
return 1
fi
keyauthorization="$token.$thumbprint"
_debug keyauthorization "$keyauthorization" _debug keyauthorization "$keyauthorization"
if printf "%s" "$response" | grep '"status":"valid"' >/dev/null 2>&1; then
_debug "$d is already verified."
keyauthorization="$STATE_VERIFIED"
_debug keyauthorization "$keyauthorization"
fi
fi fi
dvlist="$d$sep$keyauthorization$sep$uri$sep$vtype$sep$_currentRoot" dvlist="$d$sep$keyauthorization$sep$uri$sep$vtype$sep$_currentRoot"
@ -4380,7 +4535,7 @@ $_authorizations_map"
return 1 return 1
fi fi
if [ -z "$Le_LinkOrder" ]; then if [ -z "$Le_LinkOrder" ]; then
Le_LinkOrder="$(echo "$responseHeaders" | grep -i '^Location.*$' | _tail_n 1 | tr -d "\r\n" | cut -d " " -f 2)" Le_LinkOrder="$(echo "$responseHeaders" | grep -i '^Location.*$' | _tail_n 1 | tr -d "\r\n" | cut -d ":" -f 2-)"
fi fi
_savedomainconf "Le_LinkOrder" "$Le_LinkOrder" _savedomainconf "Le_LinkOrder" "$Le_LinkOrder"
@ -4929,18 +5084,14 @@ list() {
if [ "$_raw" ]; then if [ "$_raw" ]; then
printf "%s\n" "Main_Domain${_sep}KeyLength${_sep}SAN_Domains${_sep}Created${_sep}Renew" printf "%s\n" "Main_Domain${_sep}KeyLength${_sep}SAN_Domains${_sep}Created${_sep}Renew"
for di in "${CERT_HOME}"/*.*/; do for di in "${CERT_HOME}"/*.*/; do
if ! [ -d "$di" ]; then
_debug "Not directory, skip: $di"
continue
fi
d=$(basename "$di") d=$(basename "$di")
_debug d "$d" _debug d "$d"
( (
if _endswith "$d" "$ECC_SUFFIX"; then if _endswith "$d" "$ECC_SUFFIX"; then
_isEcc=$(echo "$d" | cut -d "$ECC_SEP" -f 2) _isEcc="ecc"
d=$(echo "$d" | cut -d "$ECC_SEP" -f 1) d=$(echo "$d" | cut -d "$ECC_SEP" -f 1)
fi fi
_initpath "$d" "$_isEcc" DOMAIN_CONF="$di/$d.conf"
if [ -f "$DOMAIN_CONF" ]; then if [ -f "$DOMAIN_CONF" ]; then
. "$DOMAIN_CONF" . "$DOMAIN_CONF"
printf "%s\n" "$Le_Domain${_sep}\"$Le_Keylength\"${_sep}$Le_Alt${_sep}$Le_CertCreateTimeStr${_sep}$Le_NextRenewTimeStr" printf "%s\n" "$Le_Domain${_sep}\"$Le_Keylength\"${_sep}$Le_Alt${_sep}$Le_CertCreateTimeStr${_sep}$Le_NextRenewTimeStr"
@ -5435,7 +5586,7 @@ _deactivate() {
return 1 return 1
fi fi
authzUri="$(echo "$responseHeaders" | grep "^Location:" | _head_n 1 | cut -d ' ' -f 2 | tr -d "\r\n")" authzUri="$(echo "$responseHeaders" | grep "^Location:" | _head_n 1 | cut -d ':' -f 2- | tr -d "\r\n")"
_debug "authzUri" "$authzUri" _debug "authzUri" "$authzUri"
if [ "$code" ] && [ ! "$code" = '201' ]; then if [ "$code" ] && [ ! "$code" = '201' ]; then
_err "new-authz error: $response" _err "new-authz error: $response"
@ -5932,8 +6083,12 @@ _send_notify() {
_send_err=0 _send_err=0
for _n_hook in $(echo "$_nhooks" | tr ',' " "); do for _n_hook in $(echo "$_nhooks" | tr ',' " "); do
_n_hook_file="$(_findHook "" $_SUB_FOLDER_NOTIFY "$_n_hook")" _n_hook_file="$(_findHook "" $_SUB_FOLDER_NOTIFY "$_n_hook")"
_info "Found $_n_hook_file" _info "Sending via: $_n_hook"
_debug "Found $_n_hook_file for $_n_hook"
if [ -z "$_n_hook_file" ]; then
_err "Can not find the hook file for $_n_hook"
continue
fi
if ! ( if ! (
if ! . "$_n_hook_file"; then if ! . "$_n_hook_file"; then
_err "Load file $_n_hook_file error. Please check your api file and try again." _err "Load file $_n_hook_file error. Please check your api file and try again."
@ -5967,8 +6122,8 @@ _send_notify() {
_set_notify_hook() { _set_notify_hook() {
_nhooks="$1" _nhooks="$1"
_test_subject="Hello, this is notification from $PROJECT_NAME" _test_subject="Hello, this is a notification from $PROJECT_NAME"
_test_content="If you receive this email, your notification works." _test_content="If you receive this message, your notification works."
_send_notify "$_test_subject" "$_test_content" "$_nhooks" 0 _send_notify "$_test_subject" "$_test_content" "$_nhooks" 0
@ -6061,7 +6216,7 @@ Parameters:
--force, -f Used to force to install or force to renew a cert immediately. --force, -f Used to force to install or force to renew a cert immediately.
--staging, --test Use staging server, just for test. --staging, --test Use staging server, just for test.
--debug Output debug info. --debug Output debug info.
--output-insecure Output all the sensitive messages. By default all the credentials/sensitive messages are hidden from the output/debug/log for secure. --output-insecure Output all the sensitive messages. By default all the credentials/sensitive messages are hidden from the output/debug/log for security.
--webroot, -w /path/to/webroot Specifies the web root folder for web root mode. --webroot, -w /path/to/webroot Specifies the web root folder for web root mode.
--standalone Use standalone mode. --standalone Use standalone mode.
--alpn Use standalone alpn mode. --alpn Use standalone alpn mode.
@ -6070,8 +6225,8 @@ Parameters:
--dns [dns_cf|dns_dp|dns_cx|/path/to/api/file] Use dns mode or dns api. --dns [dns_cf|dns_dp|dns_cx|/path/to/api/file] Use dns mode or dns api.
--dnssleep [$DEFAULT_DNS_SLEEP] The time in seconds to wait for all the txt records to take effect in dns api mode. Default $DEFAULT_DNS_SLEEP seconds. --dnssleep [$DEFAULT_DNS_SLEEP] The time in seconds to wait for all the txt records to take effect in dns api mode. Default $DEFAULT_DNS_SLEEP seconds.
--keylength, -k [2048] Specifies the domain key length: 2048, 3072, 4096, 8192 or ec-256, ec-384. --keylength, -k [2048] Specifies the domain key length: 2048, 3072, 4096, 8192 or ec-256, ec-384, ec-521.
--accountkeylength, -ak [2048] Specifies the account key length. --accountkeylength, -ak [2048] Specifies the account key length: 2048, 3072, 4096
--log [/path/to/logfile] Specifies the log file. The default is: \"$DEFAULT_LOG_FILE\" if you don't give a file path here. --log [/path/to/logfile] Specifies the log file. The default is: \"$DEFAULT_LOG_FILE\" if you don't give a file path here.
--log-level 1|2 Specifies the log level, default is 1. --log-level 1|2 Specifies the log level, default is 1.
--syslog [0|3|6|7] Syslog level, 0: disable syslog, 3: error, 6: info, 7: debug. --syslog [0|3|6|7] Syslog level, 0: disable syslog, 3: error, 6: info, 7: debug.
@ -6085,7 +6240,7 @@ Parameters:
--reloadcmd \"service nginx reload\" After issue/renew, it's used to reload the server. --reloadcmd \"service nginx reload\" After issue/renew, it's used to reload the server.
--server SERVER ACME Directory Resource URI. (default: https://acme-v01.api.letsencrypt.org/directory) --server SERVER ACME Directory Resource URI. (default: $DEFAULT_CA)
--accountconf Specifies a customized account config file. --accountconf Specifies a customized account config file.
--home Specifies the home dir for $PROJECT_NAME. --home Specifies the home dir for $PROJECT_NAME.
--cert-home Specifies the home dir to save all the certs, only valid for '--install' command. --cert-home Specifies the home dir to save all the certs, only valid for '--install' command.
@ -6123,7 +6278,7 @@ Parameters:
--branch, -b Only valid for '--upgrade' command, specifies the branch name to upgrade to. --branch, -b Only valid for '--upgrade' command, specifies the branch name to upgrade to.
--notify-level 0|1|2|3 Set the notification level: Default value is $NOTIFY_LEVEL_DEFAULT. --notify-level 0|1|2|3 Set the notification level: Default value is $NOTIFY_LEVEL_DEFAULT.
0: disabled, no notification will be sent. 0: disabled, no notification will be sent.
1: send notifications only when there is an error. 1: send notifications only when there is an error.
2: send notifications when a cert is successfully renewed, or there is an error. 2: send notifications when a cert is successfully renewed, or there is an error.
3: send notifications when a cert is skipped, renewed, or error. 3: send notifications when a cert is skipped, renewed, or error.
@ -6162,6 +6317,8 @@ _installOnline() {
chmod +x $PROJECT_ENTRY chmod +x $PROJECT_ENTRY
if ./$PROJECT_ENTRY install "$_nocron" "" "$_noprofile"; then if ./$PROJECT_ENTRY install "$_nocron" "" "$_noprofile"; then
_info "Install success!" _info "Install success!"
_initpath
_saveaccountconf "UPGRADE_HASH" "$(_getMasterHash)"
fi fi
cd .. cd ..
@ -6171,9 +6328,19 @@ _installOnline() {
) )
} }
_getMasterHash() {
_b="$BRANCH"
if [ -z "$_b" ]; then
_b="master"
fi
_hash_url="https://api.github.com/repos/acmesh-official/$PROJECT_NAME/git/refs/heads/$_b"
_get $_hash_url | tr -d "\r\n" | tr '{},' '\n' | grep '"sha":' | cut -d '"' -f 4
}
upgrade() { upgrade() {
if ( if (
_initpath _initpath
[ -z "$FORCE" ] && [ "$(_getMasterHash)" = "$(_readaccountconf "UPGRADE_HASH")" ] && _info "Already uptodate!" && exit 0
export LE_WORKING_DIR export LE_WORKING_DIR
cd "$LE_WORKING_DIR" cd "$LE_WORKING_DIR"
_installOnline "nocron" "noprofile" _installOnline "nocron" "noprofile"
@ -6219,6 +6386,23 @@ _processAccountConf() {
} }
_checkSudo() {
if [ "$SUDO_GID" ] && [ "$SUDO_COMMAND" ] && [ "$SUDO_USER" ] && [ "$SUDO_UID" ]; then
if [ "$SUDO_USER" = "root" ] && [ "$SUDO_UID" = "0" ]; then
#it's root using sudo, no matter it's using sudo or not, just fine
return 0
fi
if [ "$SUDO_COMMAND" = "/bin/su" ] || [ "$SUDO_COMMAND" = "/bin/bash" ]; then
#it's a normal user doing "sudo su", or `sudo -i` or `sudo -s`
#fine
return 0
fi
#otherwise
return 1
fi
return 0
}
_process() { _process() {
_CMD="" _CMD=""
_domain="" _domain=""
@ -6470,6 +6654,10 @@ _process() {
;; ;;
--nginx) --nginx)
wvalue="$NGINX" wvalue="$NGINX"
if [ "$2" ] && ! _startswith "$2" "-"; then
wvalue="$NGINX$2"
shift
fi
if [ -z "$_webroot" ]; then if [ -z "$_webroot" ]; then
_webroot="$wvalue" _webroot="$wvalue"
else else
@ -6747,6 +6935,14 @@ _process() {
done done
if [ "${_CMD}" != "install" ]; then if [ "${_CMD}" != "install" ]; then
if [ "$__INTERACTIVE" ] && ! _checkSudo; then
if [ -z "$FORCE" ]; then
#Use "echo" here, instead of _info. it's too early
echo "It seems that you are using sudo, please read this link first:"
echo "$_SUDO_WIKI"
return 1
fi
fi
__initHome __initHome
if [ "$_log" ]; then if [ "$_log" ]; then
if [ -z "$_logfile" ]; then if [ -z "$_logfile" ]; then

View File

@ -2,5 +2,5 @@
deploy hook usage: deploy hook usage:
https://github.com/Neilpang/acme.sh/wiki/deployhooks https://github.com/acmesh-official/acme.sh/wiki/deployhooks

View File

@ -8,7 +8,7 @@
#DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE="/path/to/fullchain.pem" #DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE="/path/to/fullchain.pem"
#DEPLOY_DOCKER_CONTAINER_RELOAD_CMD="service nginx force-reload" #DEPLOY_DOCKER_CONTAINER_RELOAD_CMD="service nginx force-reload"
_DEPLOY_DOCKER_WIKI="https://github.com/Neilpang/acme.sh/wiki/deploy-to-docker-containers" _DEPLOY_DOCKER_WIKI="https://github.com/acmesh-official/acme.sh/wiki/deploy-to-docker-containers"
_DOCKER_HOST_DEFAULT="/var/run/docker.sock" _DOCKER_HOST_DEFAULT="/var/run/docker.sock"

View File

@ -1,7 +1,6 @@
#!/usr/bin/env sh #!/usr/bin/env sh
# Here is the script to deploy the cert to G-Core CDN service (https://gcorelabs.com/ru/) using the G-Core Labs API (https://docs.gcorelabs.com/cdn/). # Here is the script to deploy the cert to G-Core CDN service (https://gcorelabs.com/ru/) using the G-Core Labs API (https://docs.gcorelabs.com/cdn/).
# Uses command line curl for send requests and jq for parse responses.
# Returns 0 when success. # Returns 0 when success.
# #
# Written by temoffey <temofffey@gmail.com> # Written by temoffey <temofffey@gmail.com>
@ -78,15 +77,15 @@ gcore_cdn_deploy() {
_debug _regex "$_regex" _debug _regex "$_regex"
_resource=$(echo "$_response" | sed 's/},{/},\n{/g' | _egrep_o "$_regex") _resource=$(echo "$_response" | sed 's/},{/},\n{/g' | _egrep_o "$_regex")
_debug _resource "$_resource" _debug _resource "$_resource"
_regex=".*\"id\":\([0-9]*\),.*$" _regex=".*\"id\":\([0-9]*\).*\"rules\".*$"
_debug _regex "$_regex" _debug _regex "$_regex"
_resourceId=$(echo "$_resource" | sed -n "s/$_regex/\1/p") _resourceId=$(echo "$_resource" | sed -n "s/$_regex/\1/p")
_debug _resourceId "$_resourceId" _debug _resourceId "$_resourceId"
_regex=".*\"sslData\":\([0-9]*\)}.*$" _regex=".*\"sslData\":\([0-9]*\).*$"
_debug _regex "$_regex" _debug _regex "$_regex"
_sslDataOld=$(echo "$_resource" | sed -n "s/$_regex/\1/p") _sslDataOld=$(echo "$_resource" | sed -n "s/$_regex/\1/p")
_debug _sslDataOld "$_sslDataOld" _debug _sslDataOld "$_sslDataOld"
_regex=".*\"originGroup\":\([0-9]*\),.*$" _regex=".*\"originGroup\":\([0-9]*\).*$"
_debug _regex "$_regex" _debug _regex "$_regex"
_originGroup=$(echo "$_resource" | sed -n "s/$_regex/\1/p") _originGroup=$(echo "$_resource" | sed -n "s/$_regex/\1/p")
_debug _originGroup "$_originGroup" _debug _originGroup "$_originGroup"
@ -102,7 +101,7 @@ gcore_cdn_deploy() {
_debug _request "$_request" _debug _request "$_request"
_response=$(_post "$_request" "https://api.gcdn.co/sslData") _response=$(_post "$_request" "https://api.gcdn.co/sslData")
_debug _response "$_response" _debug _response "$_response"
_regex=".*\"id\":\([0-9]*\),.*$" _regex=".*\"id\":\([0-9]*\).*$"
_debug _regex "$_regex" _debug _regex "$_regex"
_sslDataAdd=$(echo "$_response" | sed -n "s/$_regex/\1/p") _sslDataAdd=$(echo "$_response" | sed -n "s/$_regex/\1/p")
_debug _sslDataAdd "$_sslDataAdd" _debug _sslDataAdd "$_sslDataAdd"
@ -117,7 +116,7 @@ gcore_cdn_deploy() {
_debug _request "$_request" _debug _request "$_request"
_response=$(_post "$_request" "https://api.gcdn.co/resources/$_resourceId" '' "PUT") _response=$(_post "$_request" "https://api.gcdn.co/resources/$_resourceId" '' "PUT")
_debug _response "$_response" _debug _response "$_response"
_regex=".*\"sslData\":\([0-9]*\)}.*$" _regex=".*\"sslData\":\([0-9]*\).*$"
_debug _regex "$_regex" _debug _regex "$_regex"
_sslDataNew=$(echo "$_response" | sed -n "s/$_regex/\1/p") _sslDataNew=$(echo "$_response" | sed -n "s/$_regex/\1/p")
_debug _sslDataNew "$_sslDataNew" _debug _sslDataNew "$_sslDataNew"

View File

@ -208,33 +208,37 @@ haproxy_deploy() {
_issuerdn=$(openssl x509 -in "${_issuer}" -issuer -noout | cut -d'/' -f2,3,4,5,6,7,8,9,10) _issuerdn=$(openssl x509 -in "${_issuer}" -issuer -noout | cut -d'/' -f2,3,4,5,6,7,8,9,10)
_debug _issuerdn "${_issuerdn}" _debug _issuerdn "${_issuerdn}"
_info "Requesting OCSP response" _info "Requesting OCSP response"
# Request the OCSP response from the issuer and store it # If the issuer is a CA cert then our command line has "-CAfile" added
if [ "${_subjectdn}" = "${_issuerdn}" ]; then if [ "${_subjectdn}" = "${_issuerdn}" ]; then
# If the issuer is a CA cert then our command line has "-CAfile" added _cafile_argument="-CAfile \"${_issuer}\""
openssl ocsp \
-issuer "${_issuer}" \
-cert "${_pem}" \
-url "${_ocsp_url}" \
-header Host "${_ocsp_host}" \
-respout "${_ocsp}" \
-verify_other "${_issuer}" \
-no_nonce \
-CAfile "${_issuer}" \
| grep -q "${_pem}: good"
_ret=$?
else else
# Issuer is not a root CA so no "-CAfile" option _cafile_argument=""
openssl ocsp \
-issuer "${_issuer}" \
-cert "${_pem}" \
-url "${_ocsp_url}" \
-header Host "${_ocsp_host}" \
-respout "${_ocsp}" \
-verify_other "${_issuer}" \
-no_nonce \
| grep -q "${_pem}: good"
_ret=$?
fi fi
_debug _cafile_argument "${_cafile_argument}"
# if OpenSSL/LibreSSL is v1.1 or above, the format for the -header option has changed
_openssl_version=$(openssl version | cut -d' ' -f2)
_debug _openssl_version "${_openssl_version}"
_openssl_major=$(echo "${_openssl_version}" | cut -d '.' -f1)
_openssl_minor=$(echo "${_openssl_version}" | cut -d '.' -f2)
if [ "${_openssl_major}" -eq "1" ] && [ "${_openssl_minor}" -ge "1" ] || [ "${_openssl_major}" -ge "2" ]; then
_header_sep="="
else
_header_sep=" "
fi
# Request the OCSP response from the issuer and store it
_openssl_ocsp_cmd="openssl ocsp \
-issuer \"${_issuer}\" \
-cert \"${_pem}\" \
-url \"${_ocsp_url}\" \
-header Host${_header_sep}\"${_ocsp_host}\" \
-respout \"${_ocsp}\" \
-verify_other \"${_issuer}\" \
-no_nonce \
${_cafile_argument} \
| grep -q \"${_pem}: good\""
_debug _openssl_ocsp_cmd "${_openssl_ocsp_cmd}"
eval "${_openssl_ocsp_cmd}"
_ret=$?
else else
# Non fatal: No issuer file was present so no OCSP stapling file created # Non fatal: No issuer file was present so no OCSP stapling file created
_err "OCSP stapling in use but no .issuer file was present" _err "OCSP stapling in use but no .issuer file was present"

139
deploy/panos.sh Normal file
View File

@ -0,0 +1,139 @@
#!/usr/bin/env sh
# Script to deploy certificates to Palo Alto Networks PANOS via API
# Note PANOS API KEY and IP address needs to be set prior to running.
# The following variables exported from environment will be used.
# If not set then values previously saved in domain.conf file are used.
#
# Firewall admin with superuser and IP address is required.
#
# export PANOS_USER="" # required
# export PANOS_PASS="" # required
# export PANOS_HOST="" # required
# This function is to parse the XML
parse_response() {
type=$2
if [ "$type" = 'keygen' ]; then
status=$(echo "$1" | sed 's/^.*\(['\'']\)\([a-z]*\)'\''.*/\2/g')
if [ "$status" = "success" ]; then
panos_key=$(echo "$1" | sed 's/^.*\(<key>\)\(.*\)<\/key>.*/\2/g')
_panos_key=$panos_key
else
message="PAN-OS Key could not be set."
fi
else
status=$(echo "$1" | sed 's/^.*"\([a-z]*\)".*/\1/g')
message=$(echo "$1" | sed 's/^.*<result>\(.*\)<\/result.*/\1/g')
fi
return 0
}
deployer() {
content=""
type=$1 # Types are keygen, cert, key, commit
_debug "**** Deploying $type *****"
panos_url="https://$_panos_host/api/"
if [ "$type" = 'keygen' ]; then
_H1="Content-Type: application/x-www-form-urlencoded"
content="type=keygen&user=$_panos_user&password=$_panos_pass"
# content="$content${nl}--$delim${nl}Content-Disposition: form-data; type=\"keygen\"; user=\"$_panos_user\"; password=\"$_panos_pass\"${nl}Content-Type: application/octet-stream${nl}${nl}"
fi
if [ "$type" = 'cert' ] || [ "$type" = 'key' ]; then
#Generate DEIM
delim="-----MultipartDelimiter$(date "+%s%N")"
nl="\015\012"
#Set Header
export _H1="Content-Type: multipart/form-data; boundary=$delim"
if [ "$type" = 'cert' ]; then
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"type\"\r\n\r\n\r\nimport"
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"category\"\r\n\r\n\r\ncertificate"
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"certificate-name\"\r\n\r\n\r\n$_cdomain"
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"key\"\r\n\r\n\r\n$_panos_key"
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"format\"\r\n\r\n\r\npem"
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"file\"; filename=\"$(basename "$_cfullchain")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_cfullchain")"
fi
if [ "$type" = 'key' ]; then
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"type\"\r\n\r\n\r\nimport"
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"category\"\r\n\r\n\r\nprivate-key"
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"certificate-name\"\r\n\r\n\r\n$_cdomain"
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"key\"\r\n\r\n\r\n$_panos_key"
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"format\"\r\n\r\n\r\npem"
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"passphrase\"\r\n\r\n\r\n123456"
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"file\"; filename=\"$(basename "$_ckey")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_ckey")"
fi
#Close multipart
content="$content${nl}--$delim--${nl}"
#Convert CRLF
content=$(printf %b "$content")
fi
if [ "$type" = 'commit' ]; then
export _H1="Content-Type: application/x-www-form-urlencoded"
cmd=$(printf "%s" "<commit><partial><$_panos_user></$_panos_user></partial></commit>" | _url_encode)
content="type=commit&key=$_panos_key&cmd=$cmd"
fi
response=$(_post "$content" "$panos_url" "" "POST")
parse_response "$response" "$type"
# Saving response to variables
response_status=$status
#DEBUG
_debug response_status "$response_status"
if [ "$response_status" = "success" ]; then
_debug "Successfully deployed $type"
return 0
else
_err "Deploy of type $type failed. Try deploying with --debug to troubleshoot."
_debug "$message"
return 1
fi
}
# This is the main function that will call the other functions to deploy everything.
panos_deploy() {
_cdomain="$1"
_ckey="$2"
_cfullchain="$5"
# PANOS ENV VAR check
if [ -z "$PANOS_USER" ] || [ -z "$PANOS_PASS" ] || [ -z "$PANOS_HOST" ]; then
_debug "No ENV variables found lets check for saved variables"
_getdeployconf PANOS_USER
_getdeployconf PANOS_PASS
_getdeployconf PANOS_HOST
_panos_user=$PANOS_USER
_panos_pass=$PANOS_PASS
_panos_host=$PANOS_HOST
if [ -z "$_panos_user" ] && [ -z "$_panos_pass" ] && [ -z "$_panos_host" ]; then
_err "No host, user and pass found.. If this is the first time deploying please set PANOS_HOST, PANOS_USER and PANOS_PASS in environment variables. Delete them after you have succesfully deployed certs."
return 1
else
_debug "Using saved env variables."
fi
else
_debug "Detected ENV variables to be saved to the deploy conf."
# Encrypt and save user
_savedeployconf PANOS_USER "$PANOS_USER" 1
_savedeployconf PANOS_PASS "$PANOS_PASS" 1
_savedeployconf PANOS_HOST "$PANOS_HOST" 1
_panos_user="$PANOS_USER"
_panos_pass="$PANOS_PASS"
_panos_host="$PANOS_HOST"
fi
_debug "Let's use username and pass to generate token."
if [ -z "$_panos_user" ] || [ -z "$_panos_pass" ] || [ -z "$_panos_host" ]; then
_err "Please pass username and password and host as env variables PANOS_USER, PANOS_PASS and PANOS_HOST"
return 1
else
_debug "Getting PANOS KEY"
deployer keygen
if [ -z "$_panos_key" ]; then
_err "Missing apikey."
return 1
else
deployer cert
deployer key
deployer commit
fi
fi
}

View File

@ -1,6 +1,6 @@
#!/usr/bin/env sh #!/usr/bin/env sh
# Script to create certificate to qiniu.com # Script to create certificate to qiniu.com
# #
# This deployment required following variables # This deployment required following variables
# export QINIU_AK="QINIUACCESSKEY" # export QINIU_AK="QINIUACCESSKEY"

View File

@ -85,19 +85,19 @@ routeros_deploy() {
scp "$_ckey" "$ROUTER_OS_USERNAME@$ROUTER_OS_HOST:$_cdomain.key" scp "$_ckey" "$ROUTER_OS_USERNAME@$ROUTER_OS_HOST:$_cdomain.key"
_info "Trying to push cert '$_cfullchain' to router" _info "Trying to push cert '$_cfullchain' to router"
scp "$_cfullchain" "$ROUTER_OS_USERNAME@$ROUTER_OS_HOST:$_cdomain.cer" scp "$_cfullchain" "$ROUTER_OS_USERNAME@$ROUTER_OS_HOST:$_cdomain.cer"
DEPLOY_SCRIPT_CMD="/system script add name=\"LE Cert Deploy - $_cdomain\" owner=admin policy=ftp,read,write,password,sensitive DEPLOY_SCRIPT_CMD="/system script add name=\"LE Cert Deploy - $_cdomain\" owner=admin policy=ftp,read,write,password,sensitive \
source=\"## generated by routeros deploy script in acme.sh source=\"## generated by routeros deploy script in acme.sh;\
\n/certificate remove [ find name=$_cdomain.cer_0 ] \n/certificate remove [ find name=$_cdomain.cer_0 ];\
\n/certificate remove [ find name=$_cdomain.cer_1 ] \n/certificate remove [ find name=$_cdomain.cer_1 ];\
\ndelay 1 \ndelay 1;\
\n/certificate import file-name=$_cdomain.cer passphrase=\\\"\\\" \n/certificate import file-name=$_cdomain.cer passphrase=\\\"\\\";\
\n/certificate import file-name=$_cdomain.key passphrase=\\\"\\\" \n/certificate import file-name=$_cdomain.key passphrase=\\\"\\\";\
\ndelay 1 \ndelay 1;\
\n/file remove $_cdomain.cer \n/file remove $_cdomain.cer;\
\n/file remove $_cdomain.key \n/file remove $_cdomain.key;\
\ndelay 2 \ndelay 2;\
\n/ip service set www-ssl certificate=$_cdomain.cer_0 \n/ip service set www-ssl certificate=$_cdomain.cer_0;\
\n$ROUTER_OS_ADDITIONAL_SERVICES \n$ROUTER_OS_ADDITIONAL_SERVICES;\
\n\" \n\"
" "
# shellcheck disable=SC2029 # shellcheck disable=SC2029

152
deploy/synology_dsm.sh Normal file
View File

@ -0,0 +1,152 @@
#!/usr/bin/env sh
# Here is a script to deploy cert to Synology DSM
#
# it requires the jq and curl are in the $PATH and the following
# environment variables must be set:
#
# SYNO_Username - Synology Username to login (must be an administrator)
# SYNO_Password - Synology Password to login
# SYNO_Certificate - Certificate description to target for replacement
#
# The following environmental variables may be set if you don't like their
# default values:
#
# SYNO_Scheme - defaults to http
# SYNO_Hostname - defaults to localhost
# SYNO_Port - defaults to 5000
#
#returns 0 means success, otherwise error.
######## Public functions #####################
_syno_get_cookie_data() {
grep "\W$1=" "$HTTP_HEADER" | grep "^Set-Cookie:" | _tail_n 1 | _egrep_o "$1=[^;]*;" | tr -d ';'
}
#domain keyfile certfile cafile fullchain
synology_dsm_deploy() {
_cdomain="$1"
_ckey="$2"
_ccert="$3"
_cca="$4"
_debug _cdomain "$_cdomain"
# Get Username and Password, but don't save until we successfully authenticate
_getdeployconf SYNO_Username
_getdeployconf SYNO_Password
_getdeployconf SYNO_Create
if [ -z "$SYNO_Username" ] || [ -z "$SYNO_Password" ]; then
SYNO_Username=""
SYNO_Password=""
_err "SYNO_Username & SYNO_Password must be set"
return 1
fi
_debug2 SYNO_Username "$SYNO_Username"
_secure_debug2 SYNO_Password "$SYNO_Password"
# Optional scheme, hostname, and port for Synology DSM
_getdeployconf SYNO_Scheme
_getdeployconf SYNO_Hostname
_getdeployconf SYNO_Port
# default vaules for scheme, hostname, and port
# defaulting to localhost and http because it's localhost...
[ -n "${SYNO_Scheme}" ] || SYNO_Scheme="http"
[ -n "${SYNO_Hostname}" ] || SYNO_Hostname="localhost"
[ -n "${SYNO_Port}" ] || SYNO_Port="5000"
_savedeployconf SYNO_Scheme "$SYNO_Scheme"
_savedeployconf SYNO_Hostname "$SYNO_Hostname"
_savedeployconf SYNO_Port "$SYNO_Port"
_debug2 SYNO_Scheme "$SYNO_Scheme"
_debug2 SYNO_Hostname "$SYNO_Hostname"
_debug2 SYNO_Port "$SYNO_Port"
# Get the certificate description, but don't save it until we verfiy it's real
_getdeployconf SYNO_Certificate
if [ -z "${SYNO_Certificate:?}" ]; then
_err "SYNO_Certificate needs to be defined (with the Certificate description name)"
return 1
fi
_debug SYNO_Certificate "$SYNO_Certificate"
_base_url="$SYNO_Scheme://$SYNO_Hostname:$SYNO_Port"
_debug _base_url "$_base_url"
# Login, get the token from JSON and session id from cookie
_info "Logging into $SYNO_Hostname:$SYNO_Port"
response=$(_get "$_base_url/webman/login.cgi?username=$SYNO_Username&passwd=$SYNO_Password&enable_syno_token=yes")
token=$(echo "$response" | grep "SynoToken" | sed -n 's/.*"SynoToken" *: *"\([^"]*\).*/\1/p')
_debug3 response "$response"
if [ -z "$token" ]; then
_err "Unable to authenticate to $SYNO_Hostname:$SYNO_Port using $SYNO_Scheme."
_err "Check your username and password."
return 1
fi
_H1="Cookie: $(_syno_get_cookie_data "id"); $(_syno_get_cookie_data "smid")"
_H2="X-SYNO-TOKEN: $token"
export _H1
export _H2
_debug2 H1 "${_H1}"
_debug2 H2 "${_H2}"
# Now that we know the username and password are good, save them
_savedeployconf SYNO_Username "$SYNO_Username"
_savedeployconf SYNO_Password "$SYNO_Password"
_debug token "$token"
_info "Getting certificates in Synology DSM"
response=$(_post "api=SYNO.Core.Certificate.CRT&method=list&version=1" "$_base_url/webapi/entry.cgi")
_debug3 response "$response"
id=$(echo "$response" | sed -n "s/.*\"desc\":\"$SYNO_Certificate\",\"id\":\"\([^\"]*\).*/\1/p")
_debug2 id "$id"
if [ -z "$id" ] && [ -z "${SYNO_Create:?}" ]; then
_err "Unable to find certificate: $SYNO_Certificate and \$SYNO_Create is not set"
return 1
fi
# we've verified this certificate description is a thing, so save it
_savedeployconf SYNO_Certificate "$SYNO_Certificate"
default=false
if echo "$response" | sed -n "s/.*\"desc\":\"$SYNO_Certificate\",\([^{]*\).*/\1/p" | grep -- 'is_default":true' >/dev/null; then
default=true
fi
_debug2 default "$default"
_info "Generate form POST request"
nl="\015\012"
delim="--------------------------$(_utc_date | tr -d -- '-: ')"
content="--$delim${nl}Content-Disposition: form-data; name=\"key\"; filename=\"$(basename "$_ckey")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_ckey")\012"
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"cert\"; filename=\"$(basename "$_ccert")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_ccert")\012"
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"inter_cert\"; filename=\"$(basename "$_cca")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_cca")\012"
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"id\"${nl}${nl}$id"
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"desc\"${nl}${nl}${SYNO_Certificate}"
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"as_default\"${nl}${nl}${default}"
content="$content${nl}--$delim--${nl}"
content="$(printf "%b_" "$content")"
content="${content%_}" # protect trailing \n
_info "Upload certificate to the Synology DSM"
response=$(_post "$content" "$_base_url/webapi/entry.cgi?api=SYNO.Core.Certificate&method=import&version=1&SynoToken=$token" "" "POST" "multipart/form-data; boundary=${delim}")
_debug3 response "$response"
if ! echo "$response" | grep '"error":' >/dev/null; then
if echo "$response" | grep '"restart_httpd":true' >/dev/null; then
_info "http services were restarted"
else
_info "http services were NOT restarted"
fi
return 0
else
_err "Unable to update certificate, error code $response"
return 1
fi
}

View File

@ -2,10 +2,10 @@
# Here is a script to deploy cert to hashicorp vault # Here is a script to deploy cert to hashicorp vault
# (https://www.vaultproject.io/) # (https://www.vaultproject.io/)
# #
# it requires the vault binary to be available in PATH, and the following # it requires the vault binary to be available in PATH, and the following
# environment variables: # environment variables:
# #
# VAULT_PREFIX - this contains the prefix path in vault # VAULT_PREFIX - this contains the prefix path in vault
# VAULT_ADDR - vault requires this to find your vault server # VAULT_ADDR - vault requires this to find your vault server
# #

View File

@ -2,5 +2,5 @@
DNS api usage: DNS api usage:
https://github.com/Neilpang/acme.sh/wiki/dnsapi https://github.com/acmesh-official/acme.sh/wiki/dnsapi

View File

@ -185,7 +185,7 @@ _clean() {
return 1 return 1
fi fi
record_id="$(echo "$response" | tr '{' "\n" | grep "$_sub_domain" | grep "$txtvalue" | tr "," "\n" | grep RecordId | cut -d '"' -f 4)" record_id="$(echo "$response" | tr '{' "\n" | grep "$_sub_domain" | grep -- "$txtvalue" | tr "," "\n" | grep RecordId | cut -d '"' -f 4)"
_debug2 record_id "$record_id" _debug2 record_id "$record_id"
if [ -z "$record_id" ]; then if [ -z "$record_id" ]; then

View File

@ -6,11 +6,13 @@
#AWS_SECRET_ACCESS_KEY="xxxxxxx" #AWS_SECRET_ACCESS_KEY="xxxxxxx"
#This is the Amazon Route53 api wrapper for acme.sh #This is the Amazon Route53 api wrapper for acme.sh
#All `_sleep` commands are included to avoid Route53 throttling, see
#https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/DNSLimitations.html#limits-api-requests
AWS_HOST="route53.amazonaws.com" AWS_HOST="route53.amazonaws.com"
AWS_URL="https://$AWS_HOST" AWS_URL="https://$AWS_HOST"
AWS_WIKI="https://github.com/Neilpang/acme.sh/wiki/How-to-use-Amazon-Route53-API" AWS_WIKI="https://github.com/acmesh-official/acme.sh/wiki/How-to-use-Amazon-Route53-API"
######## Public functions ##################### ######## Public functions #####################
@ -43,6 +45,7 @@ dns_aws_add() {
_debug "First detect the root zone" _debug "First detect the root zone"
if ! _get_root "$fulldomain"; then if ! _get_root "$fulldomain"; then
_err "invalid domain" _err "invalid domain"
_sleep 1
return 1 return 1
fi fi
_debug _domain_id "$_domain_id" _debug _domain_id "$_domain_id"
@ -51,6 +54,7 @@ dns_aws_add() {
_info "Getting existing records for $fulldomain" _info "Getting existing records for $fulldomain"
if ! aws_rest GET "2013-04-01$_domain_id/rrset" "name=$fulldomain&type=TXT"; then if ! aws_rest GET "2013-04-01$_domain_id/rrset" "name=$fulldomain&type=TXT"; then
_sleep 1
return 1 return 1
fi fi
@ -63,6 +67,7 @@ dns_aws_add() {
if [ "$_resource_record" ] && _contains "$response" "$txtvalue"; then if [ "$_resource_record" ] && _contains "$response" "$txtvalue"; then
_info "The TXT record already exists. Skipping." _info "The TXT record already exists. Skipping."
_sleep 1
return 0 return 0
fi fi
@ -72,9 +77,10 @@ dns_aws_add() {
if aws_rest POST "2013-04-01$_domain_id/rrset/" "" "$_aws_tmpl_xml" && _contains "$response" "ChangeResourceRecordSetsResponse"; then if aws_rest POST "2013-04-01$_domain_id/rrset/" "" "$_aws_tmpl_xml" && _contains "$response" "ChangeResourceRecordSetsResponse"; then
_info "TXT record updated successfully." _info "TXT record updated successfully."
_sleep 1
return 0 return 0
fi fi
_sleep 1
return 1 return 1
} }
@ -93,6 +99,7 @@ dns_aws_rm() {
_debug "First detect the root zone" _debug "First detect the root zone"
if ! _get_root "$fulldomain"; then if ! _get_root "$fulldomain"; then
_err "invalid domain" _err "invalid domain"
_sleep 1
return 1 return 1
fi fi
_debug _domain_id "$_domain_id" _debug _domain_id "$_domain_id"
@ -101,6 +108,7 @@ dns_aws_rm() {
_info "Getting existing records for $fulldomain" _info "Getting existing records for $fulldomain"
if ! aws_rest GET "2013-04-01$_domain_id/rrset" "name=$fulldomain&type=TXT"; then if ! aws_rest GET "2013-04-01$_domain_id/rrset" "name=$fulldomain&type=TXT"; then
_sleep 1
return 1 return 1
fi fi
@ -109,6 +117,7 @@ dns_aws_rm() {
_debug "_resource_record" "$_resource_record" _debug "_resource_record" "$_resource_record"
else else
_debug "no records exist, skip" _debug "no records exist, skip"
_sleep 1
return 0 return 0
fi fi
@ -116,9 +125,10 @@ dns_aws_rm() {
if aws_rest POST "2013-04-01$_domain_id/rrset/" "" "$_aws_tmpl_xml" && _contains "$response" "ChangeResourceRecordSetsResponse"; then if aws_rest POST "2013-04-01$_domain_id/rrset/" "" "$_aws_tmpl_xml" && _contains "$response" "ChangeResourceRecordSetsResponse"; then
_info "TXT record deleted successfully." _info "TXT record deleted successfully."
_sleep 1
return 0 return 0
fi fi
_sleep 1
return 1 return 1
} }

View File

@ -1,6 +1,6 @@
#!/usr/bin/env sh #!/usr/bin/env sh
WIKI="https://github.com/Neilpang/acme.sh/wiki/How-to-use-Azure-DNS" WIKI="https://github.com/acmesh-official/acme.sh/wiki/How-to-use-Azure-DNS"
######## Public functions ##################### ######## Public functions #####################

View File

@ -7,6 +7,7 @@
#CF_Token="xxxx" #CF_Token="xxxx"
#CF_Account_ID="xxxx" #CF_Account_ID="xxxx"
#CF_Zone_ID="xxxx"
CF_Api="https://api.cloudflare.com/client/v4" CF_Api="https://api.cloudflare.com/client/v4"
@ -19,12 +20,14 @@ dns_cf_add() {
CF_Token="${CF_Token:-$(_readaccountconf_mutable CF_Token)}" CF_Token="${CF_Token:-$(_readaccountconf_mutable CF_Token)}"
CF_Account_ID="${CF_Account_ID:-$(_readaccountconf_mutable CF_Account_ID)}" CF_Account_ID="${CF_Account_ID:-$(_readaccountconf_mutable CF_Account_ID)}"
CF_Zone_ID="${CF_Zone_ID:-$(_readaccountconf_mutable CF_Zone_ID)}"
CF_Key="${CF_Key:-$(_readaccountconf_mutable CF_Key)}" CF_Key="${CF_Key:-$(_readaccountconf_mutable CF_Key)}"
CF_Email="${CF_Email:-$(_readaccountconf_mutable CF_Email)}" CF_Email="${CF_Email:-$(_readaccountconf_mutable CF_Email)}"
if [ "$CF_Token" ]; then if [ "$CF_Token" ]; then
_saveaccountconf_mutable CF_Token "$CF_Token" _saveaccountconf_mutable CF_Token "$CF_Token"
_saveaccountconf_mutable CF_Account_ID "$CF_Account_ID" _saveaccountconf_mutable CF_Account_ID "$CF_Account_ID"
_saveaccountconf_mutable CF_Zone_ID "$CF_Zone_ID"
else else
if [ -z "$CF_Key" ] || [ -z "$CF_Email" ]; then if [ -z "$CF_Key" ] || [ -z "$CF_Email" ]; then
CF_Key="" CF_Key=""
@ -141,6 +144,28 @@ _get_root() {
domain=$1 domain=$1
i=1 i=1
p=1 p=1
# Use Zone ID directly if provided
if [ "$CF_Zone_ID" ]; then
if ! _cf_rest GET "zones/$CF_Zone_ID"; then
return 1
else
if _contains "$response" '"success":true'; then
_domain=$(printf "%s\n" "$response" | _egrep_o "\"name\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | head -n 1)
if [ "$_domain" ]; then
_cutlength=$((${#domain} - ${#_domain} - 1))
_sub_domain=$(printf "%s" "$domain" | cut -c "1-$_cutlength")
_domain_id=$CF_Zone_ID
return 0
else
return 1
fi
else
return 1
fi
fi
fi
while true; do while true; do
h=$(printf "%s" "$domain" | cut -d . -f $i-100) h=$(printf "%s" "$domain" | cut -d . -f $i-100)
_debug h "$h" _debug h "$h"

197
dnsapi/dns_clouddns.sh Executable file
View File

@ -0,0 +1,197 @@
#!/usr/bin/env sh
# Author: Radek Sprta <sprta@vshosting.cz>
#CLOUDDNS_EMAIL=XXXXX
#CLOUDDNS_PASSWORD="YYYYYYYYY"
#CLOUDDNS_CLIENT_ID=XXXXX
CLOUDDNS_API='https://admin.vshosting.cloud/clouddns'
CLOUDDNS_LOGIN_API='https://admin.vshosting.cloud/api/public/auth/login'
######## Public functions #####################
# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_clouddns_add() {
fulldomain=$1
txtvalue=$2
_debug "fulldomain" "$fulldomain"
CLOUDDNS_CLIENT_ID="${CLOUDDNS_CLIENT_ID:-$(_readaccountconf_mutable CLOUDDNS_CLIENT_ID)}"
CLOUDDNS_EMAIL="${CLOUDDNS_EMAIL:-$(_readaccountconf_mutable CLOUDDNS_EMAIL)}"
CLOUDDNS_PASSWORD="${CLOUDDNS_PASSWORD:-$(_readaccountconf_mutable CLOUDDNS_PASSWORD)}"
if [ -z "$CLOUDDNS_PASSWORD" ] || [ -z "$CLOUDDNS_EMAIL" ] || [ -z "$CLOUDDNS_CLIENT_ID" ]; then
CLOUDDNS_CLIENT_ID=""
CLOUDDNS_EMAIL=""
CLOUDDNS_PASSWORD=""
_err "You didn't specify a CloudDNS password, email and client ID yet."
return 1
fi
if ! _contains "$CLOUDDNS_EMAIL" "@"; then
_err "It seems that the CLOUDDNS_EMAIL=$CLOUDDNS_EMAIL is not a valid email address."
_err "Please check and retry."
return 1
fi
# Save CloudDNS client id, email and password to config file
_saveaccountconf_mutable CLOUDDNS_CLIENT_ID "$CLOUDDNS_CLIENT_ID"
_saveaccountconf_mutable CLOUDDNS_EMAIL "$CLOUDDNS_EMAIL"
_saveaccountconf_mutable CLOUDDNS_PASSWORD "$CLOUDDNS_PASSWORD"
_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"
# Add TXT record
data="{\"type\":\"TXT\",\"name\":\"$fulldomain.\",\"value\":\"$txtvalue\",\"domainId\":\"$_domain_id\"}"
if _clouddns_api POST "record-txt" "$data"; then
if _contains "$response" "$txtvalue"; then
_info "Added, OK"
elif _contains "$response" '"code":4136'; then
_info "Already exists, OK"
else
_err "Add TXT record error."
return 1
fi
fi
_debug "Publishing record changes"
_clouddns_api PUT "domain/$_domain_id/publish" "{\"soaTtl\":300}"
}
# Usage: rm _acme-challenge.www.domain.com
dns_clouddns_rm() {
fulldomain=$1
_debug "fulldomain" "$fulldomain"
CLOUDDNS_CLIENT_ID="${CLOUDDNS_CLIENT_ID:-$(_readaccountconf_mutable CLOUDDNS_CLIENT_ID)}"
CLOUDDNS_EMAIL="${CLOUDDNS_EMAIL:-$(_readaccountconf_mutable CLOUDDNS_EMAIL)}"
CLOUDDNS_PASSWORD="${CLOUDDNS_PASSWORD:-$(_readaccountconf_mutable CLOUDDNS_PASSWORD)}"
_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"
# Get record ID
_clouddns_api GET "domain/$_domain_id"
if _contains "$response" "lastDomainRecordList"; then
re="\"lastDomainRecordList\".*\"id\":\"([^\"}]*)\"[^}]*\"name\":\"$fulldomain.\","
_last_domains=$(echo "$response" | _egrep_o "$re")
re2="\"id\":\"([^\"}]*)\"[^}]*\"name\":\"$fulldomain.\","
_record_id=$(echo "$_last_domains" | _egrep_o "$re2" | _head_n 1 | cut -d : -f 2 | cut -d , -f 1 | tr -d "\"")
_debug _record_id "$_record_id"
else
_err "Could not retrieve record ID"
return 1
fi
_info "Removing record"
if _clouddns_api DELETE "record/$_record_id"; then
if _contains "$response" "\"error\":"; then
_err "Could not remove record"
return 1
fi
fi
_debug "Publishing record changes"
_clouddns_api PUT "domain/$_domain_id/publish" "{\"soaTtl\":300}"
}
#################### Private functions below ##################################
# Usage: _get_root _acme-challenge.www.domain.com
# Returns:
# _sub_domain=_acme-challenge.www
# _domain=domain.com
# _domain_id=sdjkglgdfewsdfg
_get_root() {
domain=$1
# Get domain root
data="{\"search\": [{\"name\": \"clientId\", \"operator\": \"eq\", \"value\": \"$CLOUDDNS_CLIENT_ID\"}]}"
_clouddns_api "POST" "domain/search" "$data"
domain_slice="$domain"
while [ -z "$domain_root" ]; do
if _contains "$response" "\"domainName\":\"$domain_slice\.\""; then
domain_root="$domain_slice"
_debug domain_root "$domain_root"
fi
domain_slice="$(echo "$domain_slice" | cut -d . -f 2-)"
done
# Get domain id
data="{\"search\": [{\"name\": \"clientId\", \"operator\": \"eq\", \"value\": \"$CLOUDDNS_CLIENT_ID\"}, \
{\"name\": \"domainName\", \"operator\": \"eq\", \"value\": \"$domain_root.\"}]}"
_clouddns_api "POST" "domain/search" "$data"
if _contains "$response" "\"id\":\""; then
re='domainType\":\"[^\"]*\",\"id\":\"([^\"]*)\",' # Match domain id
_domain_id=$(echo "$response" | _egrep_o "$re" | _head_n 1 | cut -d : -f 3 | tr -d "\",")
if [ "$_domain_id" ]; then
_sub_domain=$(printf "%s" "$domain" | sed "s/.$domain_root//")
_domain="$domain_root"
return 0
fi
_err 'Domain name not found on your CloudDNS account'
return 1
fi
return 1
}
# Usage: _clouddns_api GET domain/search '{"data": "value"}'
# Returns:
# response='{"message": "api response"}'
_clouddns_api() {
method=$1
endpoint="$2"
data="$3"
_debug endpoint "$endpoint"
if [ -z "$CLOUDDNS_TOKEN" ]; then
_clouddns_login
fi
_debug CLOUDDNS_TOKEN "$CLOUDDNS_TOKEN"
export _H1="Content-Type: application/json"
export _H2="Authorization: Bearer $CLOUDDNS_TOKEN"
if [ "$method" != "GET" ]; then
_debug data "$data"
response="$(_post "$data" "$CLOUDDNS_API/$endpoint" "" "$method" | tr -d '\t\r\n ')"
else
response="$(_get "$CLOUDDNS_API/$endpoint" | tr -d '\t\r\n ')"
fi
# shellcheck disable=SC2181
if [ "$?" != "0" ]; then
_err "Error $endpoint"
return 1
fi
_debug2 response "$response"
return 0
}
# Returns:
# CLOUDDNS_TOKEN=dslfje2rj23l
_clouddns_login() {
login_data="{\"email\": \"$CLOUDDNS_EMAIL\", \"password\": \"$CLOUDDNS_PASSWORD\"}"
response="$(_post "$login_data" "$CLOUDDNS_LOGIN_API" "" "POST" "Content-Type: application/json")"
if _contains "$response" "\"accessToken\":\""; then
CLOUDDNS_TOKEN=$(echo "$response" | _egrep_o "\"accessToken\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")
export CLOUDDNS_TOKEN
else
echo 'Could not get CloudDNS access token; check your credentials'
return 1
fi
return 0
}

141
dnsapi/dns_constellix.sh Normal file
View File

@ -0,0 +1,141 @@
#!/usr/bin/env sh
# Author: Wout Decre <wout@canodus.be>
CONSTELLIX_Api="https://api.dns.constellix.com/v1"
#CONSTELLIX_Key="XXX"
#CONSTELLIX_Secret="XXX"
######## Public functions #####################
# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
# Used to add txt record
dns_constellix_add() {
fulldomain=$1
txtvalue=$2
CONSTELLIX_Key="${CONSTELLIX_Key:-$(_readaccountconf_mutable CONSTELLIX_Key)}"
CONSTELLIX_Secret="${CONSTELLIX_Secret:-$(_readaccountconf_mutable CONSTELLIX_Secret)}"
if [ -z "$CONSTELLIX_Key" ] || [ -z "$CONSTELLIX_Secret" ]; then
_err "You did not specify the Contellix API key and secret yet."
return 1
fi
_saveaccountconf_mutable CONSTELLIX_Key "$CONSTELLIX_Key"
_saveaccountconf_mutable CONSTELLIX_Secret "$CONSTELLIX_Secret"
if ! _get_root "$fulldomain"; then
_err "Invalid domain"
return 1
fi
_info "Adding TXT record"
if _constellix_rest POST "domains/${_domain_id}/records" "[{\"type\":\"txt\",\"add\":true,\"set\":{\"name\":\"${_sub_domain}\",\"ttl\":120,\"roundRobin\":[{\"value\":\"${txtvalue}\"}]}}]"; then
if printf -- "%s" "$response" | grep "{\"success\":\"1 record(s) added, 0 record(s) updated, 0 record(s) deleted\"}" >/dev/null; then
_info "Added"
return 0
else
_err "Error adding TXT record"
return 1
fi
fi
}
# Usage: fulldomain txtvalue
# Used to remove the txt record after validation
dns_constellix_rm() {
fulldomain=$1
txtvalue=$2
CONSTELLIX_Key="${CONSTELLIX_Key:-$(_readaccountconf_mutable CONSTELLIX_Key)}"
CONSTELLIX_Secret="${CONSTELLIX_Secret:-$(_readaccountconf_mutable CONSTELLIX_Secret)}"
if [ -z "$CONSTELLIX_Key" ] || [ -z "$CONSTELLIX_Secret" ]; then
_err "You did not specify the Contellix API key and secret yet."
return 1
fi
if ! _get_root "$fulldomain"; then
_err "Invalid domain"
return 1
fi
_info "Removing TXT record"
if _constellix_rest POST "domains/${_domain_id}/records" "[{\"type\":\"txt\",\"delete\":true,\"filter\":{\"field\":\"name\",\"op\":\"eq\",\"value\":\"${_sub_domain}\"}}]"; then
if printf -- "%s" "$response" | grep "{\"success\":\"0 record(s) added, 0 record(s) updated, 1 record(s) deleted\"}" >/dev/null; then
_info "Removed"
return 0
else
_err "Error removing TXT record"
return 1
fi
fi
}
#################### Private functions below ##################################
_get_root() {
domain=$1
i=2
p=1
_debug "Detecting root zone"
while true; do
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
if [ -z "$h" ]; then
return 1
fi
if ! _constellix_rest GET "domains"; then
return 1
fi
if _contains "$response" "\"name\":\"$h\""; 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"
_debug _domain_id "$_domain_id"
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
return 0
fi
return 1
fi
p=$i
i=$(_math "$i" + 1)
done
return 1
}
_constellix_rest() {
m=$1
ep="$2"
data="$3"
_debug "$ep"
rdate=$(date +"%s")"000"
hmac=$(printf "%s" "$rdate" | _hmac sha1 "$(printf "%s" "$CONSTELLIX_Secret" | _hex_dump | tr -d ' ')" | _base64)
export _H1="x-cnsdns-apiKey: $CONSTELLIX_Key"
export _H2="x-cnsdns-requestDate: $rdate"
export _H3="x-cnsdns-hmac: $hmac"
export _H4="Accept: application/json"
export _H5="Content-Type: application/json"
if [ "$m" != "GET" ]; then
_debug data "$data"
response="$(_post "$data" "$CONSTELLIX_Api/$ep" "" "$m")"
else
response="$(_get "$CONSTELLIX_Api/$ep")"
fi
if [ "$?" != "0" ]; then
_err "Error $ep"
return 1
fi
_debug response "$response"
return 0
}

View File

@ -1,7 +1,7 @@
#!/usr/bin/env sh #!/usr/bin/env sh
######## ########
# Custom cyon.ch DNS API for use with [acme.sh](https://github.com/Neilpang/acme.sh) # Custom cyon.ch DNS API for use with [acme.sh](https://github.com/acmesh-official/acme.sh)
# #
# Usage: acme.sh --issue --dns dns_cyon -d www.domain.com # Usage: acme.sh --issue --dns dns_cyon -d www.domain.com
# #

View File

@ -9,7 +9,7 @@
# #
# User must provide login data and URL to DirectAdmin incl. port. # User must provide login data and URL to DirectAdmin incl. port.
# You can create login key, by using the Login Keys function # You can create login key, by using the Login Keys function
# ( https://da.example.com:8443/CMD_LOGIN_KEYS ), which only has access to # ( https://da.example.com:8443/CMD_LOGIN_KEYS ), which only has access to
# - CMD_API_DNS_CONTROL # - CMD_API_DNS_CONTROL
# - CMD_API_SHOW_DOMAINS # - CMD_API_SHOW_DOMAINS
# #

View File

@ -12,7 +12,7 @@
# -- # --
# #
DDNSS_DNS_API="https://ddnss.de/upd.php" DDNSS_DNS_API="https://ip4.ddnss.de/upd.php"
######## Public functions ##################### ######## Public functions #####################

View File

@ -25,8 +25,8 @@ dns_desec_add() {
if [ -z "$DEDYN_TOKEN" ] || [ -z "$DEDYN_NAME" ]; then if [ -z "$DEDYN_TOKEN" ] || [ -z "$DEDYN_NAME" ]; then
DEDYN_TOKEN="" DEDYN_TOKEN=""
DEDYN_NAME="" DEDYN_NAME=""
_err "You don't specify DEDYN_TOKEN and DEDYN_NAME yet." _err "You did not specify DEDYN_TOKEN and DEDYN_NAME yet."
_err "Please create you key and try again." _err "Please create your key and try again."
_err "e.g." _err "e.g."
_err "export DEDYN_TOKEN=d41d8cd98f00b204e9800998ecf8427e" _err "export DEDYN_TOKEN=d41d8cd98f00b204e9800998ecf8427e"
_err "export DEDYN_NAME=foobar.dedyn.io" _err "export DEDYN_NAME=foobar.dedyn.io"
@ -92,8 +92,8 @@ dns_desec_rm() {
if [ -z "$DEDYN_TOKEN" ] || [ -z "$DEDYN_NAME" ]; then if [ -z "$DEDYN_TOKEN" ] || [ -z "$DEDYN_NAME" ]; then
DEDYN_TOKEN="" DEDYN_TOKEN=""
DEDYN_NAME="" DEDYN_NAME=""
_err "You don't specify DEDYN_TOKEN and DEDYN_NAME yet." _err "You did not specify DEDYN_TOKEN and DEDYN_NAME yet."
_err "Please create you key and try again." _err "Please create your key and try again."
_err "e.g." _err "e.g."
_err "export DEDYN_TOKEN=d41d8cd98f00b204e9800998ecf8427e" _err "export DEDYN_TOKEN=d41d8cd98f00b204e9800998ecf8427e"
_err "export DEDYN_NAME=foobar.dedyn.io" _err "export DEDYN_NAME=foobar.dedyn.io"

View File

@ -1,11 +1,11 @@
#!/usr/bin/env sh #!/usr/bin/env sh
# Official Let's Encrypt API for do.de / Domain-Offensive # Official Let's Encrypt API for do.de / Domain-Offensive
# #
# This is different from the dns_do adapter, because dns_do is only usable for enterprise customers # This is different from the dns_do adapter, because dns_do is only usable for enterprise customers
# This API is also available to private customers/individuals # This API is also available to private customers/individuals
# #
# Provide the required LetsEncrypt token like this: # Provide the required LetsEncrypt token like this:
# DO_LETOKEN="FmD408PdqT1E269gUK57" # DO_LETOKEN="FmD408PdqT1E269gUK57"
DO_API="https://www.do.de/api/letsencrypt" DO_API="https://www.do.de/api/letsencrypt"

155
dnsapi/dns_domeneshop.sh Normal file
View File

@ -0,0 +1,155 @@
#!/usr/bin/env sh
DOMENESHOP_Api_Endpoint="https://api.domeneshop.no/v0"
##################### Public functions #####################
# Usage: dns_domeneshop_add <full domain> <txt record>
# Example: dns_domeneshop_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_domeneshop_add() {
fulldomain=$1
txtvalue=$2
# Get token and secret
DOMENESHOP_Token="${DOMENESHOP_Token:-$(_readaccountconf_mutable DOMENESHOP_Token)}"
DOMENESHOP_Secret="${DOMENESHOP_Secret:-$(_readaccountconf_mutable DOMENESHOP_Secret)}"
if [ -z "$DOMENESHOP_Token" ] || [ -z "$DOMENESHOP_Secret" ]; then
DOMENESHOP_Token=""
DOMENESHOP_Secret=""
_err "You need to spesify a Domeneshop/Domainnameshop API Token and Secret."
return 1
fi
# Save the api token and secret.
_saveaccountconf_mutable DOMENESHOP_Token "$DOMENESHOP_Token"
_saveaccountconf_mutable DOMENESHOP_Secret "$DOMENESHOP_Secret"
# Get the domain name id
if ! _get_domainid "$fulldomain"; then
_err "Did not find domainname"
return 1
fi
# Create record
_domeneshop_rest POST "domains/$_domainid/dns" "{\"type\":\"TXT\",\"host\":\"$_sub_domain\",\"data\":\"$txtvalue\",\"ttl\":120}"
}
# Usage: dns_domeneshop_rm <full domain> <txt record>
# Example: dns_domeneshop_rm _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_domeneshop_rm() {
fulldomain=$1
txtvalue=$2
# Get token and secret
DOMENESHOP_Token="${DOMENESHOP_Token:-$(_readaccountconf_mutable DOMENESHOP_Token)}"
DOMENESHOP_Secret="${DOMENESHOP_Secret:-$(_readaccountconf_mutable DOMENESHOP_Secret)}"
if [ -z "$DOMENESHOP_Token" ] || [ -z "$DOMENESHOP_Secret" ]; then
DOMENESHOP_Token=""
DOMENESHOP_Secret=""
_err "You need to spesify a Domeneshop/Domainnameshop API Token and Secret."
return 1
fi
# Get the domain name id
if ! _get_domainid "$fulldomain"; then
_err "Did not find domainname"
return 1
fi
# Find record
if ! _get_recordid "$_domainid" "$_sub_domain" "$txtvalue"; then
_err "Did not find dns record"
return 1
fi
# Remove record
_domeneshop_rest DELETE "domains/$_domainid/dns/$_recordid"
}
##################### Private functions #####################
_get_domainid() {
domain=$1
# Get domains
_domeneshop_rest GET "domains"
if ! _contains "$response" "\"id\":"; then
_err "failed to get domain names"
return 1
fi
i=2
p=1
while true; do
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
_debug "h" "$h"
if [ -z "$h" ]; then
#not valid
return 1
fi
if _contains "$response" "\"$h\"" >/dev/null; then
# We have found the domain name.
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_domain=$h
_domainid=$(printf "%s" "$response" | _egrep_o "[^{]*\"domain\":\"$_domain\"[^}]*" | _egrep_o "\"id\":[0-9]+" | cut -d : -f 2)
return 0
fi
p=$i
i=$(_math "$i" + 1)
done
return 1
}
_get_recordid() {
domainid=$1
subdomain=$2
txtvalue=$3
# Get all dns records for the domainname
_domeneshop_rest GET "domains/$domainid/dns"
if ! _contains "$response" "\"id\":"; then
_debug "No records in dns"
return 1
fi
if ! _contains "$response" "\"host\":\"$subdomain\""; then
_debug "Record does not exist"
return 1
fi
# Get the id of the record in question
_recordid=$(printf "%s" "$response" | _egrep_o "[^{]*\"host\":\"$subdomain\"[^}]*" | _egrep_o "[^{]*\"data\":\"$txtvalue\"[^}]*" | _egrep_o "\"id\":[0-9]+" | cut -d : -f 2)
if [ -z "$_recordid" ]; then
return 1
fi
return 0
}
_domeneshop_rest() {
method=$1
endpoint=$2
data=$3
credentials=$(printf "%b" "$DOMENESHOP_Token:$DOMENESHOP_Secret" | _base64)
export _H1="Authorization: Basic $credentials"
export _H2="Content-Type: application/json"
if [ "$method" != "GET" ]; then
response="$(_post "$data" "$DOMENESHOP_Api_Endpoint/$endpoint" "" "$method")"
else
response="$(_get "$DOMENESHOP_Api_Endpoint/$endpoint")"
fi
if [ "$?" != "0" ]; then
_err "error $endpoint"
return 1
fi
return 0
}

View File

@ -63,7 +63,7 @@ dns_dp_rm() {
return 0 return 0
fi fi
record_id=$(echo "$response" | tr "{" "\n" | grep "$txtvalue" | grep '^"id"' | cut -d : -f 2 | cut -d '"' -f 2) record_id=$(echo "$response" | tr "{" "\n" | grep -- "$txtvalue" | grep '^"id"' | cut -d : -f 2 | cut -d '"' -f 2)
_debug record_id "$record_id" _debug record_id "$record_id"
if [ -z "$record_id" ]; then if [ -z "$record_id" ]; then
_err "Can not get record id." _err "Can not get record id."

View File

@ -147,11 +147,11 @@ _dd_soap() {
# build SOAP XML # build SOAP XML
_xml='<?xml version="1.0" encoding="utf-8"?> _xml='<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:tns="urn:'$_urn'" xmlns:tns="urn:'$_urn'"
xmlns:types="urn:'$_urn'/encodedTypes" xmlns:types="urn:'$_urn'/encodedTypes"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"> xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">'"$body"'</soap:Body> <soap:Body soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">'"$body"'</soap:Body>
</soap:Envelope>' </soap:Envelope>'

121
dnsapi/dns_dynv6.sh Normal file
View File

@ -0,0 +1,121 @@
#!/usr/bin/env sh
#Author StefanAbl
#Usage specify a private keyfile to use with dynv6 'export KEY="path/to/keyfile"'
#if no keyfile is specified, you will be asked if you want to create one in /home/$USER/.ssh/dynv6 and /home/$USER/.ssh/dynv6.pub
######## Public functions #####################
# Please Read this guide first: https://github.com/Neilpang/acme.sh/wiki/DNS-API-Dev-Guide
#Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_dynv6_add() {
fulldomain=$1
txtvalue=$2
_info "Using dynv6 api"
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
_get_keyfile
_info "using keyfile $dynv6_keyfile"
_get_domain "$fulldomain"
_your_hosts="$(ssh -i "$dynv6_keyfile" api@dynv6.com hosts)"
if ! _contains "$_your_hosts" "$_host"; then
_debug "The host is $_host and the record $_record"
_debug "Dynv6 returned $_your_hosts"
_err "The host $_host does not exists on your dynv6 account"
return 1
fi
_debug "found host on your account"
returnval="$(ssh -i "$dynv6_keyfile" api@dynv6.com hosts \""$_host"\" records set \""$_record"\" txt data \""$txtvalue"\")"
_debug "Dynv6 returend this after record was added: $returnval"
if _contains "$returnval" "created"; then
return 0
elif _contains "$returnval" "updated"; then
return 0
else
_err "Something went wrong! it does not seem like the record was added succesfully"
return 1
fi
return 1
}
#Usage: fulldomain txtvalue
#Remove the txt record after validation.
dns_dynv6_rm() {
fulldomain=$1
txtvalue=$2
_info "Using dynv6 api"
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
_get_keyfile
_info "using keyfile $dynv6_keyfile"
_get_domain "$fulldomain"
_your_hosts="$(ssh -i "$dynv6_keyfile" api@dynv6.com hosts)"
if ! _contains "$_your_hosts" "$_host"; then
_debug "The host is $_host and the record $_record"
_debug "Dynv6 returned $_your_hosts"
_err "The host $_host does not exists on your dynv6 account"
return 1
fi
_debug "found host on your account"
_info "$(ssh -i "$dynv6_keyfile" api@dynv6.com hosts "\"$_host\"" records del "\"$_record\"" txt)"
return 0
}
#################### Private functions below ##################################
#Usage: No Input required
#returns
#dynv6_keyfile the path to the new keyfile that has been generated
_generate_new_key() {
dynv6_keyfile="$(eval echo ~"$USER")/.ssh/dynv6"
_info "Path to key file used: $dynv6_keyfile"
if [ ! -f "$dynv6_keyfile" ] && [ ! -f "$dynv6_keyfile.pub" ]; then
_debug "generating key in $dynv6_keyfile and $dynv6_keyfile.pub"
ssh-keygen -f "$dynv6_keyfile" -t ssh-ed25519 -N ''
else
_err "There is already a file in $dynv6_keyfile or $dynv6_keyfile.pub"
return 1
fi
}
#Usage: _acme-challenge.www.example.dynv6.net
#returns
#_host= example.dynv6.net
#_record=_acme-challenge.www
#aborts if not a valid domain
_get_domain() {
_full_domain="$1"
_debug "getting domain for $_full_domain"
if ! _contains "$_full_domain" 'dynv6.net' && ! _contains "$_full_domain" 'dns.army' && ! _contains "$_full_domain" 'dns.navy'; then
_err "The hosts does not seem to be a dynv6 host"
return 1
fi
_record="${_full_domain%.*}"
_record="${_record%.*}"
_record="${_record%.*}"
_debug "The record we are ging to use is $_record"
_host="$_full_domain"
while [ "$(echo "$_host" | grep -o '\.' | wc -l)" != "2" ]; do
_host="${_host#*.}"
done
_debug "And the host is $_host"
return 0
}
# Usage: No input required
#returns
#dynv6_keyfile path to the key that will be used
_get_keyfile() {
_debug "get keyfile method called"
dynv6_keyfile="${dynv6_keyfile:-$(_readaccountconf_mutable dynv6_keyfile)}"
_debug Your key is "$dynv6_keyfile"
if [ -z "$dynv6_keyfile" ]; then
if [ -z "$KEY" ]; then
_err "You did not specify a key to use with dynv6"
_info "Creating new dynv6 api key to add to dynv6.com"
_generate_new_key
_info "Please add this key to dynv6.com $(cat "$dynv6_keyfile.pub")"
_info "Hit Enter to contiue"
read -r _
#save the credentials to the account conf file.
else
dynv6_keyfile="$KEY"
fi
_saveaccountconf_mutable dynv6_keyfile "$dynv6_keyfile"
fi
}

172
dnsapi/dns_easydns.sh Normal file
View File

@ -0,0 +1,172 @@
#!/usr/bin/env sh
#######################################################
#
# easyDNS REST API for acme.sh by Neilpang based on dns_cf.sh
#
# Please note: # API is currently beta and subject to constant change
# http://sandbox.rest.easydns.net:3000/
#
# Author: wurzelpanzer [wurzelpanzer@maximolider.net]
# Report Bugs here: https://github.com/acmesh-official/acme.sh/issues/2647
#
#################### Public functions #################
#EASYDNS_Key="xxxxxxxxxxxxxxxxxxxxxxxx"
#EASYDNS_Token="xxxxxxxxxxxxxxxxxxxxxxxx"
EASYDNS_Api="https://rest.easydns.net"
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_easydns_add() {
fulldomain=$1
txtvalue=$2
EASYDNS_Token="${EASYDNS_Token:-$(_readaccountconf_mutable EASYDNS_Token)}"
EASYDNS_Key="${EASYDNS_Key:-$(_readaccountconf_mutable EASYDNS_Key)}"
if [ -z "$EASYDNS_Token" ] || [ -z "$EASYDNS_Key" ]; then
_err "You didn't specify an easydns.net token or api key. Please sign up at http://docs.sandbox.rest.easydns.net/beta_signup.php"
return 1
else
_saveaccountconf_mutable EASYDNS_Token "$EASYDNS_Token"
_saveaccountconf_mutable EASYDNS_Key "$EASYDNS_Key"
fi
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
_debug "Getting txt records"
_EASYDNS_rest GET "zones/records/all/${_domain}/search/${_sub_domain}"
if ! printf "%s" "$response" | grep \"status\":200 >/dev/null; then
_err "Error"
return 1
fi
_info "Adding record"
if _EASYDNS_rest PUT "zones/records/add/$_domain/TXT" "{\"host\":\"$_sub_domain\",\"rdata\":\"$txtvalue\"}"; then
if _contains "$response" "\"status\":201"; then
_info "Added, OK"
return 0
elif _contains "$response" "Record already exists"; then
_info "Already exists, OK"
return 0
else
_err "Add txt record error."
return 1
fi
fi
_err "Add txt record error."
return 1
}
dns_easydns_rm() {
fulldomain=$1
txtvalue=$2
EASYDNS_Token="${EASYDNS_Token:-$(_readaccountconf_mutable EASYDNS_Token)}"
EASYDNS_Key="${EASYDNS_Key:-$(_readaccountconf_mutable EASYDNS_Key)}"
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
_debug "Getting txt records"
_EASYDNS_rest GET "zones/records/all/${_domain}/search/${_sub_domain}"
if ! printf "%s" "$response" | grep \"status\":200 >/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 ! _EASYDNS_rest DELETE "zones/records/$_domain/$record_id"; then
_err "Delete record error."
return 1
fi
_contains "$response" "\"status\":200"
fi
}
#################### Private functions below ##################################
#_acme-challenge.www.domain.com
#returns
# _sub_domain=_acme-challenge.www
# _domain=domain.com
_get_root() {
domain=$1
i=1
p=1
while true; do
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
_debug h "$h"
if [ -z "$h" ]; then
#not valid
return 1
fi
if ! _EASYDNS_rest GET "zones/records/all/$h"; then
return 1
fi
if _contains "$response" "\"status\":200"; then
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_domain=$h
return 0
fi
p=$i
i=$(_math "$i" + 1)
done
return 1
}
_EASYDNS_rest() {
m=$1
ep="$2"
data="$3"
_debug "$ep"
basicauth=$(printf "%s" "$EASYDNS_Token":"$EASYDNS_Key" | _base64)
export _H1="accept: application/json"
if [ "$basicauth" ]; then
export _H2="Authorization: Basic $basicauth"
fi
if [ "$m" != "GET" ]; then
export _H3="Content-Type: application/json"
_debug data "$data"
response="$(_post "$data" "$EASYDNS_Api/$ep" "" "$m")"
else
response="$(_get "$EASYDNS_Api/$ep")"
fi
if [ "$?" != "0" ]; then
_err "error $ep"
return 1
fi
_debug2 response "$response"
return 0
}

View File

@ -127,7 +127,7 @@ dns_euserv_rm() {
else else
# find XML block where txtvalue is in. The record_id is allways prior this line! # find XML block where txtvalue is in. The record_id is allways prior this line!
_endLine=$(echo "$response" | grep -n '>dns_record_content<.*>'"$txtvalue"'<' | cut -d ':' -f 1) _endLine=$(echo "$response" | grep -n '>dns_record_content<.*>'"$txtvalue"'<' | cut -d ':' -f 1)
# record_id is the last <name> Tag with a number before the row _endLine, identified by </name><value><struct> # record_id is the last <name> Tag with a number before the row _endLine, identified by </name><value><struct>
_record_id=$(echo "$response" | sed -n '1,'"$_endLine"'p' | grep '</name><value><struct>' | _tail_n 1 | sed 's/.*<name>\([0-9]*\)<\/name>.*/\1/') _record_id=$(echo "$response" | sed -n '1,'"$_endLine"'p' | grep '</name><value><struct>' | _tail_n 1 | sed 's/.*<name>\([0-9]*\)<\/name>.*/\1/')
_info "Deleting record" _info "Deleting record"
_euserv_delete_record "$_record_id" _euserv_delete_record "$_record_id"

View File

@ -7,7 +7,7 @@
# #
#Author: David Kerr #Author: David Kerr
#Report Bugs here: https://github.com/dkerr64/acme.sh #Report Bugs here: https://github.com/dkerr64/acme.sh
#or here... https://github.com/Neilpang/acme.sh/issues/2305 #or here... https://github.com/acmesh-official/acme.sh/issues/2305
# #
######## Public functions ##################### ######## Public functions #####################
@ -303,9 +303,9 @@ _freedns_domain_id() {
return 1 return 1
fi fi
domain_id="$(echo "$htmlpage" | tr -d "[:space:]" | sed 's/<tr>/@<tr>/g' | tr '@' '\n' \ domain_id="$(echo "$htmlpage" | tr -d " \t\r\n\v\f" | sed 's/<tr>/@<tr>/g' | tr '@' '\n' \
| grep "<td>$search_domain</td>\|<td>$search_domain(.*)</td>" \ | grep "<td>$search_domain</td>\|<td>$search_domain(.*)</td>" \
| _egrep_o "edit\.php\?edit_domain_id=[0-9a-zA-Z]+" \ | sed -n 's/.*\(edit\.php?edit_domain_id=[0-9a-zA-Z]*\).*/\1/p' \
| cut -d = -f 2)" | cut -d = -f 2)"
# The above beauty extracts domain ID from the html page... # The above beauty extracts domain ID from the html page...
# strip out all blank space and new lines. Then insert newlines # strip out all blank space and new lines. Then insert newlines
@ -349,17 +349,17 @@ _freedns_data_id() {
return 1 return 1
fi fi
data_id="$(echo "$htmlpage" | tr -d "[:space:]" | sed 's/<tr>/@<tr>/g' | tr '@' '\n' \ data_id="$(echo "$htmlpage" | tr -d " \t\r\n\v\f" | sed 's/<tr>/@<tr>/g' | tr '@' '\n' \
| grep "<td[a-zA-Z=#]*>$record_type</td>" \ | grep "<td[a-zA-Z=#]*>$record_type</td>" \
| grep "<ahref.*>$search_domain</a>" \ | grep "<ahref.*>$search_domain</a>" \
| _egrep_o "edit\.php\?data_id=[0-9a-zA-Z]+" \ | sed -n 's/.*\(edit\.php?data_id=[0-9a-zA-Z]*\).*/\1/p' \
| cut -d = -f 2)" | cut -d = -f 2)"
# The above beauty extracts data ID from the html page... # The above beauty extracts data ID from the html page...
# strip out all blank space and new lines. Then insert newlines # strip out all blank space and new lines. Then insert newlines
# before each table row <tr> # before each table row <tr>
# search for the record type withing each row (e.g. TXT) # search for the record type withing each row (e.g. TXT)
# search for the domain within each row (which is within a <a..> # search for the domain within each row (which is within a <a..>
# </a> anchor. And finally extract the domain ID. # </a> anchor. And finally extract the domain ID.
if [ -n "$data_id" ]; then if [ -n "$data_id" ]; then
printf "%s" "$data_id" printf "%s" "$data_id"
return 0 return 0

View File

@ -131,7 +131,7 @@ _dns_gcloud_find_zone() {
filter="$filter$part. " filter="$filter$part. "
part="$(echo "$part" | sed 's/[^.]*\.*//')" part="$(echo "$part" | sed 's/[^.]*\.*//')"
done done
filter="$filter)" filter="$filter) AND visibility=public"
_debug filter "$filter" _debug filter "$filter"
# List domains and find the zone with the deepest sub-domain (in case of some levels of delegation) # List domains and find the zone with the deepest sub-domain (in case of some levels of delegation)

View File

@ -134,9 +134,9 @@ _find_zone() {
_zone_ids=$(echo "$_matches" | _egrep_o "hosted_dns_zoneid=[0-9]*&" | cut -d = -f 2 | tr -d '&') _zone_ids=$(echo "$_matches" | _egrep_o "hosted_dns_zoneid=[0-9]*&" | cut -d = -f 2 | tr -d '&')
_zone_names=$(echo "$_matches" | _egrep_o "name=.*onclick" | cut -d '"' -f 2) _zone_names=$(echo "$_matches" | _egrep_o "name=.*onclick" | cut -d '"' -f 2)
_debug2 "These are the zones on this HE account:" _debug2 "These are the zones on this HE account:"
_debug2 "$_zone_names" _debug2 "_zone_names" "$_zone_names"
_debug2 "And these are their respective IDs:" _debug2 "And these are their respective IDs:"
_debug2 "$_zone_ids" _debug2 "_zone_ids" "$_zone_ids"
if [ -z "$_zone_names" ] || [ -z "$_zone_ids" ]; then if [ -z "$_zone_names" ] || [ -z "$_zone_ids" ]; then
_err "Can not get zone names." _err "Can not get zone names."
return 1 return 1
@ -154,10 +154,14 @@ _find_zone() {
_debug "Looking for zone \"${_attempted_zone}\"" _debug "Looking for zone \"${_attempted_zone}\""
line_num="$(echo "$_zone_names" | grep -n "^$_attempted_zone" | cut -d : -f 1)" line_num="$(echo "$_zone_names" | grep -n "^$_attempted_zone\$" | _head_n 1 | cut -d : -f 1)"
_debug2 line_num "$line_num"
if [ "$line_num" ]; then if [ "$line_num" ]; then
_zone_id=$(echo "$_zone_ids" | sed -n "${line_num}p") _zone_id=$(echo "$_zone_ids" | sed -n "${line_num}p")
if [ -z "$_zone_id" ]; then
_err "Can not find zone id."
return 1
fi
_debug "Found relevant zone \"$_attempted_zone\" with id \"$_zone_id\" - will be used for domain \"$_domain\"." _debug "Found relevant zone \"$_attempted_zone\" with id \"$_zone_id\" - will be used for domain \"$_domain\"."
return 0 return 0
fi fi

168
dnsapi/dns_kas.sh Executable file
View File

@ -0,0 +1,168 @@
#!/usr/bin/env sh
########################################################################
# All-inkl Kasserver hook script for acme.sh
#
# Environment variables:
#
# - $KAS_Login (Kasserver API login name)
# - $KAS_Authtype (Kasserver API auth type. Default: sha1)
# - $KAS_Authdata (Kasserver API auth data.)
#
# Author: Martin Kammerlander, Phlegx Systems OG <martin.kammerlander@phlegx.com>
# Updated by: Marc-Oliver Lange <git@die-lang.es>
# Credits: Inspired by dns_he.sh. Thanks a lot man!
# Git repo: https://github.com/phlegx/acme.sh
# TODO: Better Error handling
########################################################################
KAS_Api="https://kasapi.kasserver.com/dokumentation/formular.php"
######## Public functions #####################
dns_kas_add() {
_fulldomain=$1
_txtvalue=$2
_info "Using DNS-01 All-inkl/Kasserver hook"
_info "Adding $_fulldomain DNS TXT entry on All-inkl/Kasserver"
_info "Check and Save Props"
_check_and_save
_info "Checking Zone and Record_Name"
_get_zone_and_record_name "$_fulldomain"
_info "Getting Record ID"
_get_record_id
_info "Creating TXT DNS record"
params="?kas_login=$KAS_Login"
params="$params&kas_auth_type=$KAS_Authtype"
params="$params&kas_auth_data=$KAS_Authdata"
params="$params&var1=record_name"
params="$params&wert1=$_record_name"
params="$params&var2=record_type"
params="$params&wert2=TXT"
params="$params&var3=record_data"
params="$params&wert3=$_txtvalue"
params="$params&var4=record_aux"
params="$params&wert4=0"
params="$params&kas_action=add_dns_settings"
params="$params&var5=zone_host"
params="$params&wert5=$_zone"
_debug2 "Wait for 10 seconds by default before calling KAS API."
_sleep 10
response="$(_get "$KAS_Api$params")"
_debug2 "response" "$response"
if ! _contains "$response" "TRUE"; then
_err "An unkown error occurred, please check manually."
return 1
fi
return 0
}
dns_kas_rm() {
_fulldomain=$1
_txtvalue=$2
_info "Using DNS-01 All-inkl/Kasserver hook"
_info "Cleaning up after All-inkl/Kasserver hook"
_info "Removing $_fulldomain DNS TXT entry on All-inkl/Kasserver"
_info "Check and Save Props"
_check_and_save
_info "Checking Zone and Record_Name"
_get_zone_and_record_name "$_fulldomain"
_info "Getting Record ID"
_get_record_id
# If there is a record_id, delete the entry
if [ -n "$_record_id" ]; then
params="?kas_login=$KAS_Login"
params="$params&kas_auth_type=$KAS_Authtype"
params="$params&kas_auth_data=$KAS_Authdata"
params="$params&kas_action=delete_dns_settings"
for i in $_record_id; do
params2="$params&var1=record_id"
params2="$params2&wert1=$i"
_debug2 "Wait for 10 seconds by default before calling KAS API."
_sleep 10
response="$(_get "$KAS_Api$params2")"
_debug2 "response" "$response"
if ! _contains "$response" "TRUE"; then
_err "Either the txt record is not found or another error occurred, please check manually."
return 1
fi
done
else # Cannot delete or unkown error
_err "No record_id found that can be deleted. Please check manually."
return 1
fi
return 0
}
########################## PRIVATE FUNCTIONS ###########################
# Checks for the ENV variables and saves them
_check_and_save() {
KAS_Login="${KAS_Login:-$(_readaccountconf_mutable KAS_Login)}"
KAS_Authtype="${KAS_Authtype:-$(_readaccountconf_mutable KAS_Authtype)}"
KAS_Authdata="${KAS_Authdata:-$(_readaccountconf_mutable KAS_Authdata)}"
if [ -z "$KAS_Login" ] || [ -z "$KAS_Authtype" ] || [ -z "$KAS_Authdata" ]; then
KAS_Login=
KAS_Authtype=
KAS_Authdata=
_err "No auth details provided. Please set user credentials using the \$KAS_Login, \$KAS_Authtype, and \$KAS_Authdata environment variables."
return 1
fi
_saveaccountconf_mutable KAS_Login "$KAS_Login"
_saveaccountconf_mutable KAS_Authtype "$KAS_Authtype"
_saveaccountconf_mutable KAS_Authdata "$KAS_Authdata"
return 0
}
# Gets back the base domain/zone and record name.
# See: https://github.com/Neilpang/acme.sh/wiki/DNS-API-Dev-Guide
_get_zone_and_record_name() {
params="?kas_login=$KAS_Login"
params="?kas_login=$KAS_Login"
params="$params&kas_auth_type=$KAS_Authtype"
params="$params&kas_auth_data=$KAS_Authdata"
params="$params&kas_action=get_domains"
_debug2 "Wait for 10 seconds by default before calling KAS API."
_sleep 10
response="$(_get "$KAS_Api$params")"
_debug2 "response" "$response"
_zonen="$(echo "$response" | tr -d "\n\r" | tr -d " " | tr '[]' '<>' | sed "s/=>Array/\n=> Array/g" | tr ' ' '\n' | grep "domain_name" | tr '<' '\n' | grep "domain_name" | sed "s/domain_name>=>//g")"
_domain="$1"
_temp_domain="$(echo "$1" | sed 's/\.$//')"
_rootzone="$_domain"
for i in $_zonen; do
l1=${#_rootzone}
l2=${#i}
if _endswith "$_domain" "$i" && [ "$l1" -ge "$l2" ]; then
_rootzone="$i"
fi
done
_zone="${_rootzone}."
_temp_record_name="$(echo "$_temp_domain" | sed "s/$_rootzone//g")"
_record_name="$(echo "$_temp_record_name" | sed 's/\.$//')"
_debug2 "Zone:" "$_zone"
_debug2 "Domain:" "$_domain"
_debug2 "Record_Name:" "$_record_name"
return 0
}
# Retrieve the DNS record ID
_get_record_id() {
params="?kas_login=$KAS_Login"
params="$params&kas_auth_type=$KAS_Authtype"
params="$params&kas_auth_data=$KAS_Authdata"
params="$params&kas_action=get_dns_settings"
params="$params&var1=zone_host"
params="$params&wert1=$_zone"
_debug2 "Wait for 10 seconds by default before calling KAS API."
_sleep 10
response="$(_get "$KAS_Api$params")"
_debug2 "response" "$response"
_record_id="$(echo "$response" | tr -d "\n\r" | tr -d " " | tr '[]' '<>' | sed "s/=>Array/\n=> Array/g" | tr ' ' '\n' | grep "=>$_record_name<" | grep '>TXT<' | tr '<' '\n' | grep record_id | sed "s/record_id>=>//g")"
_debug2 _record_id "$_record_id"
return 0
}

149
dnsapi/dns_leaseweb.sh Normal file
View File

@ -0,0 +1,149 @@
#!/usr/bin/env sh
#Author: Rolph Haspers <r.haspers@global.leaseweb.com>
#Utilize leaseweb.com API to finish dns-01 verifications.
#Requires a Leaseweb API Key (export LSW_Key="Your Key")
#See http://developer.leaseweb.com for more information.
######## Public functions #####################
LSW_API="https://api.leaseweb.com/hosting/v2/domains/"
#Usage: dns_leaseweb_add _acme-challenge.www.domain.com
dns_leaseweb_add() {
fulldomain=$1
txtvalue=$2
LSW_Key="${LSW_Key:-$(_readaccountconf_mutable LSW_Key)}"
if [ -z "$LSW_Key" ]; then
LSW_Key=""
_err "You don't specify Leaseweb api key yet."
_err "Please create your key and try again."
return 1
fi
#save the api key to the account conf file.
_saveaccountconf_mutable LSW_Key "$LSW_Key"
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug _root_domain "$_domain"
_debug _domain "$fulldomain"
if _lsw_api "POST" "$_domain" "$fulldomain" "$txtvalue"; then
if [ "$_code" = "201" ]; then
_info "Added, OK"
return 0
else
_err "Add txt record error, invalid code. Code: $_code"
return 1
fi
fi
_err "Add txt record error."
return 1
}
#Usage: fulldomain txtvalue
#Remove the txt record after validation.
dns_leaseweb_rm() {
fulldomain=$1
txtvalue=$2
LSW_Key="${LSW_Key:-$(_readaccountconf_mutable LSW_Key)}"
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug _root_domain "$_domain"
_debug _domain "$fulldomain"
if _lsw_api "DELETE" "$_domain" "$fulldomain" "$txtvalue"; then
if [ "$_code" = "204" ]; then
_info "Deleted, OK"
return 0
else
_err "Delete txt record error."
return 1
fi
fi
_err "Delete txt record error."
return 1
}
#################### Private functions below ##################################
# _acme-challenge.www.domain.com
# returns
# _domain=domain.com
_get_root() {
rdomain=$1
i="$(echo "$rdomain" | tr '.' ' ' | wc -w)"
i=$(_math "$i" - 1)
while true; do
h=$(printf "%s" "$rdomain" | cut -d . -f "$i"-100)
_debug h "$h"
if [ -z "$h" ]; then
return 1 #not valid domain
fi
#Check API if domain exists
if _lsw_api "GET" "$h"; then
if [ "$_code" = "200" ]; then
_domain="$h"
return 0
fi
fi
i=$(_math "$i" - 1)
if [ "$i" -lt 2 ]; then
return 1 #not found, no need to check _acme-challenge.sub.domain in leaseweb api.
fi
done
return 1
}
_lsw_api() {
cmd=$1
d=$2
fd=$3
tvalue=$4
# Construct the HTTP Authorization header
export _H2="Content-Type: application/json"
export _H1="X-Lsw-Auth: ${LSW_Key}"
if [ "$cmd" = "GET" ]; then
response="$(_get "$LSW_API/$d")"
_code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")"
_debug "http response code $_code"
_debug response "$response"
return 0
fi
if [ "$cmd" = "POST" ]; then
data="{\"name\": \"$fd.\",\"type\": \"TXT\",\"content\": [\"$tvalue\"],\"ttl\": 60}"
response="$(_post "$data" "$LSW_API/$d/resourceRecordSets" "$data" "POST")"
_code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")"
_debug "http response code $_code"
_debug response "$response"
return 0
fi
if [ "$cmd" = "DELETE" ]; then
response="$(_post "" "$LSW_API/$d/resourceRecordSets/$fd/TXT" "" "DELETE")"
_code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")"
_debug "http response code $_code"
_debug response "$response"
return 0
fi
return 1
}

View File

@ -5,7 +5,7 @@
# https://github.com/AnalogJ/lexicon # https://github.com/AnalogJ/lexicon
lexicon_cmd="lexicon" lexicon_cmd="lexicon"
wiki="https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api" wiki="https://github.com/acmesh-official/acme.sh/wiki/How-to-use-lexicon-dns-api"
_lexicon_init() { _lexicon_init() {
if ! _exists "$lexicon_cmd"; then if ! _exists "$lexicon_cmd"; then
@ -63,6 +63,16 @@ _lexicon_init() {
_saveaccountconf_mutable "$Lx_domaintoken" "$Lx_domaintoken_v" _saveaccountconf_mutable "$Lx_domaintoken" "$Lx_domaintoken_v"
eval export "$Lx_domaintoken" eval export "$Lx_domaintoken"
fi fi
# shellcheck disable=SC2018,SC2019
Lx_api_key=$(echo LEXICON_"${PROVIDER}"_API_KEY | tr 'a-z' 'A-Z')
eval "$Lx_api_key=\${$Lx_api_key:-$(_readaccountconf_mutable "$Lx_api_key")}"
Lx_api_key_v=$(eval echo \$"$Lx_api_key")
_secure_debug "$Lx_api_key" "$Lx_api_key_v"
if [ "$Lx_api_key_v" ]; then
_saveaccountconf_mutable "$Lx_api_key" "$Lx_api_key_v"
eval export "$Lx_api_key"
fi
} }
######## Public functions ##################### ######## Public functions #####################

View File

@ -31,11 +31,12 @@ dns_linode_v4_add() {
_payload="{ _payload="{
\"type\": \"TXT\", \"type\": \"TXT\",
\"name\": \"$_sub_domain\", \"name\": \"$_sub_domain\",
\"target\": \"$txtvalue\" \"target\": \"$txtvalue\",
\"ttl_sec\": 300
}" }"
if _rest POST "/$_domain_id/records" "$_payload" && [ -n "$response" ]; then if _rest POST "/$_domain_id/records" "$_payload" && [ -n "$response" ]; then
_resource_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\s*[0-9]+" | cut -d : -f 2 | tr -d " " | _head_n 1) _resource_id=$(printf "%s\n" "$response" | _egrep_o "\"id\": *[0-9]+" | cut -d : -f 2 | tr -d " " | _head_n 1)
_debug _resource_id "$_resource_id" _debug _resource_id "$_resource_id"
if [ -z "$_resource_id" ]; then if [ -z "$_resource_id" ]; then
@ -73,9 +74,9 @@ dns_linode_v4_rm() {
if _rest GET "/$_domain_id/records" && [ -n "$response" ]; then if _rest GET "/$_domain_id/records" && [ -n "$response" ]; then
response="$(echo "$response" | tr -d "\n" | tr '{' "|" | sed 's/|/&{/g' | tr "|" "\n")" response="$(echo "$response" | tr -d "\n" | tr '{' "|" | sed 's/|/&{/g' | tr "|" "\n")"
resource="$(echo "$response" | _egrep_o "{.*\"name\":\s*\"$_sub_domain\".*}")" resource="$(echo "$response" | _egrep_o "\{.*\"name\": *\"$_sub_domain\".*}")"
if [ "$resource" ]; then if [ "$resource" ]; then
_resource_id=$(printf "%s\n" "$resource" | _egrep_o "\"id\":\s*[0-9]+" | _head_n 1 | cut -d : -f 2 | tr -d \ ) _resource_id=$(printf "%s\n" "$resource" | _egrep_o "\"id\": *[0-9]+" | _head_n 1 | cut -d : -f 2 | tr -d \ )
if [ "$_resource_id" ]; then if [ "$_resource_id" ]; then
_debug _resource_id "$_resource_id" _debug _resource_id "$_resource_id"
@ -138,9 +139,9 @@ _get_root() {
return 1 return 1
fi fi
hostedzone="$(echo "$response" | _egrep_o "{.*\"domain\":\s*\"$h\".*}")" hostedzone="$(echo "$response" | _egrep_o "\{.*\"domain\": *\"$h\".*}")"
if [ "$hostedzone" ]; then if [ "$hostedzone" ]; then
_domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o "\"id\":\s*[0-9]+" | _head_n 1 | cut -d : -f 2 | tr -d \ ) _domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o "\"id\": *[0-9]+" | _head_n 1 | cut -d : -f 2 | tr -d \ )
if [ "$_domain_id" ]; then if [ "$_domain_id" ]; then
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_domain=$h _domain=$h

View File

@ -2,7 +2,7 @@
# bug reports to dev@1e.ca # bug reports to dev@1e.ca
# ME_Key=qmlkdjflmkqdjf # ME_Key=qmlkdjflmkqdjf
# ME_Secret=qmsdlkqmlksdvnnpae # ME_Secret=qmsdlkqmlksdvnnpae
ME_Api=https://api.dnsmadeeasy.com/V2.0/dns/managed ME_Api=https://api.dnsmadeeasy.com/V2.0/dns/managed
@ -114,7 +114,7 @@ _get_root() {
fi fi
if _contains "$response" "\"name\":\"$h\""; then if _contains "$response" "\"name\":\"$h\""; then
_domain_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":[^,]*" | head -n 1 | cut -d : -f 2 | tr -d '}') _domain_id=$(printf "%s\n" "$response" | cut -c 2- | head -c -2 | sed 's/{.*}//' | sed -r 's/^.*"id":([0-9]+).*$/\1/')
if [ "$_domain_id" ]; then if [ "$_domain_id" ]; then
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_domain="$h" _domain="$h"

210
dnsapi/dns_miab.sh Normal file
View File

@ -0,0 +1,210 @@
#!/usr/bin/env sh
# Name: dns_miab.sh
#
# Authors:
# Darven Dissek 2018
# William Gertz 2019
#
# Thanks to Neil Pang and other developers here for code reused from acme.sh from DNS-01
# used to communicate with the MailinaBox Custom DNS API
# Report Bugs here:
# https://github.com/billgertz/MIAB_dns_api (for dns_miab.sh)
# https://github.com/acmesh-official/acme.sh (for acme.sh)
#
######## Public functions #####################
#Usage: dns_miab_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_miab_add() {
fulldomain=$1
txtvalue=$2
_info "Using miab challange add"
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
#retrieve MIAB environemt vars
if ! _retrieve_miab_env; then
return 1
fi
#check domain and seperate into doamin and host
if ! _get_root "$fulldomain"; then
_err "Cannot find any part of ${fulldomain} is hosted on ${MIAB_Server}"
return 1
fi
_debug2 _sub_domain "$_sub_domain"
_debug2 _domain "$_domain"
#add the challenge record
_api_path="custom/${fulldomain}/txt"
_miab_rest "$txtvalue" "$_api_path" "POST"
#check if result was good
if _contains "$response" "updated DNS"; then
_info "Successfully created the txt record"
return 0
else
_err "Error encountered during record add"
_err "$response"
return 1
fi
}
#Usage: dns_miab_rm _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_miab_rm() {
fulldomain=$1
txtvalue=$2
_info "Using miab challage delete"
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
#retrieve MIAB environemt vars
if ! _retrieve_miab_env; then
return 1
fi
#check domain and seperate into doamin and host
if ! _get_root "$fulldomain"; then
_err "Cannot find any part of ${fulldomain} is hosted on ${MIAB_Server}"
return 1
fi
_debug2 _sub_domain "$_sub_domain"
_debug2 _domain "$_domain"
#Remove the challenge record
_api_path="custom/${fulldomain}/txt"
_miab_rest "$txtvalue" "$_api_path" "DELETE"
#check if result was good
if _contains "$response" "updated DNS"; then
_info "Successfully removed the txt record"
return 0
else
_err "Error encountered during record remove"
_err "$response"
return 1
fi
}
#################### Private functions below ##################################
#
#Usage: _get_root _acme-challenge.www.domain.com
#Returns:
# _sub_domain=_acme-challenge.www
# _domain=domain.com
_get_root() {
_passed_domain=$1
_debug _passed_domain "$_passed_domain"
_i=2
_p=1
#get the zones hosed on MIAB server, must be a json stream
_miab_rest "" "zones" "GET"
if ! _is_json "$response"; then
_err "ERROR fetching domain list"
_err "$response"
return 1
fi
#cycle through the passed domain seperating out a test domain discarding
# the subdomain by marching thorugh the dots
while true; do
_test_domain=$(printf "%s" "$_passed_domain" | cut -d . -f ${_i}-100)
_debug _test_domain "$_test_domain"
if [ -z "$_test_domain" ]; then
return 1
fi
#report found if the test domain is in the json response and
# report the subdomain
if _contains "$response" "\"$_test_domain\""; then
_sub_domain=$(printf "%s" "$_passed_domain" | cut -d . -f 1-${_p})
_domain=${_test_domain}
return 0
fi
#cycle to the next dot in the passed domain
_p=${_i}
_i=$(_math "$_i" + 1)
done
return 1
}
#Usage: _retrieve_miab_env
#Returns (from store or environment variables):
# MIAB_Username
# MIAB_Password
# MIAB_Server
#retrieve MIAB environment variables, report errors and quit if problems
_retrieve_miab_env() {
MIAB_Username="${MIAB_Username:-$(_readaccountconf_mutable MIAB_Username)}"
MIAB_Password="${MIAB_Password:-$(_readaccountconf_mutable MIAB_Password)}"
MIAB_Server="${MIAB_Server:-$(_readaccountconf_mutable MIAB_Server)}"
#debug log the environmental variables
_debug MIAB_Username "$MIAB_Username"
_debug MIAB_Password "$MIAB_Password"
_debug MIAB_Server "$MIAB_Server"
#check if MIAB environemt vars set and quit if not
if [ -z "$MIAB_Username" ] || [ -z "$MIAB_Password" ] || [ -z "$MIAB_Server" ]; then
_err "You didn't specify one or more of MIAB_Username, MIAB_Password or MIAB_Server."
_err "Please check these environment variables and try again."
return 1
fi
#save the credentials to the account conf file.
_saveaccountconf_mutable MIAB_Username "$MIAB_Username"
_saveaccountconf_mutable MIAB_Password "$MIAB_Password"
_saveaccountconf_mutable MIAB_Server "$MIAB_Server"
}
#Useage: _miab_rest "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" "custom/_acme-challenge.www.domain.com/txt "POST"
#Returns: "updated DNS: domain.com"
#rest interface MIAB dns
_miab_rest() {
_data="$1"
_api_path="$2"
_httpmethod="$3"
#encode username and password for basic authentication
_credentials="$(printf "%s" "$MIAB_Username:$MIAB_Password" | _base64)"
export _H1="Authorization: Basic $_credentials"
_url="https://${MIAB_Server}/admin/dns/${_api_path}"
_debug2 _data "$_data"
_debug _api_path "$_api_path"
_debug2 _url "$_url"
_debug2 _credentails "$_credentials"
_debug _httpmethod "$_httpmethod"
if [ "$_httpmethod" = "GET" ]; then
response="$(_get "$_url")"
else
response="$(_post "$_data" "$_url" "" "$_httpmethod")"
fi
_retcode="$?"
if [ "$_retcode" != "0" ]; then
_err "MIAB REST authentication failed on $_httpmethod"
return 1
fi
_debug response "$response"
return 0
}
#Usage: _is_json "\[\n "mydomain.com"\n]"
#Reurns "\[\n "mydomain.com"\n]"
#returns the string if it begins and ends with square braces
_is_json() {
_str="$(echo "$1" | _normalizeJson)"
echo "$_str" | grep '^\[.*\]$' >/dev/null 2>&1
}

159
dnsapi/dns_misaka.sh Executable file
View File

@ -0,0 +1,159 @@
#!/usr/bin/env sh
# bug reports to support+acmesh@misaka.io
# based on dns_nsone.sh by dev@1e.ca
#
#Misaka_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
#
Misaka_Api="https://dnsapi.misaka.io/dns"
######## Public functions #####################
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_misaka_add() {
fulldomain=$1
txtvalue=$2
if [ -z "$Misaka_Key" ]; then
Misaka_Key=""
_err "You didn't specify misaka.io dns api key yet."
_err "Please create you key and try again."
return 1
fi
#save the api key and email to the account conf file.
_saveaccountconf Misaka_Key "$Misaka_Key"
_debug "checking root zone [$fulldomain]"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
_debug "Getting txt records"
_misaka_rest GET "zones/${_domain}/recordsets?search=${_sub_domain}"
if ! _contains "$response" "\"results\":"; then
_err "Error"
return 1
fi
count=$(printf "%s\n" "$response" | _egrep_o "\"name\":\"$_sub_domain\",[^{]*\"type\":\"TXT\"" | wc -l | tr -d " ")
_debug count "$count"
if [ "$count" = "0" ]; then
_info "Adding record"
if _misaka_rest PUT "zones/${_domain}/recordsets/${_sub_domain}/TXT" "{\"records\":[{\"value\":\"\\\"$txtvalue\\\"\"}],\"filters\":[],\"ttl\":1}"; then
_debug response "$response"
if _contains "$response" "$_sub_domain"; then
_info "Added"
return 0
else
_err "Add txt record error."
return 1
fi
fi
_err "Add txt record error."
else
_info "Updating record"
_misaka_rest POST "zones/${_domain}/recordsets/${_sub_domain}/TXT?append=true" "{\"records\": [{\"value\": \"\\\"$txtvalue\\\"\"}],\"ttl\":1}"
if [ "$?" = "0" ] && _contains "$response" "$_sub_domain"; then
_info "Updated!"
#todo: check if the record takes effect
return 0
fi
_err "Update error"
return 1
fi
}
#fulldomain
dns_misaka_rm() {
fulldomain=$1
txtvalue=$2
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
_debug "Getting txt records"
_misaka_rest GET "zones/${_domain}/recordsets?search=${_sub_domain}"
count=$(printf "%s\n" "$response" | _egrep_o "\"name\":\"$_sub_domain\",[^{]*\"type\":\"TXT\"" | wc -l | tr -d " ")
_debug count "$count"
if [ "$count" = "0" ]; then
_info "Don't need to remove."
else
if ! _misaka_rest DELETE "zones/${_domain}/recordsets/${_sub_domain}/TXT"; then
_err "Delete record error."
return 1
fi
_contains "$response" ""
fi
}
#################### Private functions below ##################################
#_acme-challenge.www.domain.com
#returns
# _sub_domain=_acme-challenge.www
# _domain=domain.com
# _domain_id=sdjkglgdfewsdfg
_get_root() {
domain=$1
i=2
p=1
if ! _misaka_rest GET "zones?limit=1000"; then
return 1
fi
while true; do
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
_debug h "$h"
if [ -z "$h" ]; then
#not valid
return 1
fi
if _contains "$response" "\"name\":\"$h\""; then
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_domain="$h"
return 0
fi
p=$i
i=$(_math "$i" + 1)
done
return 1
}
_misaka_rest() {
m=$1
ep="$2"
data="$3"
_debug "$ep"
export _H1="Content-Type: application/json"
export _H2="User-Agent: acme.sh/$VER misaka-dns-acmesh/20191213"
export _H3="Authorization: Token $Misaka_Key"
if [ "$m" != "GET" ]; then
_debug data "$data"
response="$(_post "$data" "$Misaka_Api/$ep" "" "$m")"
else
response="$(_get "$Misaka_Api/$ep")"
fi
if [ "$?" != "0" ]; then
_err "error $ep"
return 1
fi
_debug2 response "$response"
return 0
}

View File

@ -7,11 +7,11 @@
#returns 0 means success, otherwise error. #returns 0 means success, otherwise error.
# #
#Author: Neilpang #Author: Neilpang
#Report Bugs here: https://github.com/Neilpang/acme.sh #Report Bugs here: https://github.com/acmesh-official/acme.sh
# #
######## Public functions ##################### ######## Public functions #####################
# Please Read this guide first: https://github.com/Neilpang/acme.sh/wiki/DNS-API-Dev-Guide # Please Read this guide first: https://github.com/acmesh-official/acme.sh/wiki/DNS-API-Dev-Guide
#Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" #Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_myapi_add() { dns_myapi_add() {

View File

@ -3,10 +3,10 @@
# Namecheap API # Namecheap API
# https://www.namecheap.com/support/api/intro.aspx # https://www.namecheap.com/support/api/intro.aspx
# #
# Requires Namecheap API key set in # Requires Namecheap API key set in
#NAMECHEAP_API_KEY, #NAMECHEAP_API_KEY,
#NAMECHEAP_USERNAME, #NAMECHEAP_USERNAME,
#NAMECHEAP_SOURCEIP #NAMECHEAP_SOURCEIP
# Due to Namecheap's API limitation all the records of your domain will be read and re applied, make sure to have a backup of your records you could apply if any issue would arise. # Due to Namecheap's API limitation all the records of your domain will be read and re applied, make sure to have a backup of your records you could apply if any issue would arise.
######## Public functions ##################### ######## Public functions #####################

View File

@ -110,7 +110,7 @@ _get_root() {
return 1 return 1
fi fi
if _contains "$response" "$host"; then if _contains "$response" "<domain>$host"; then
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_domain="$host" _domain="$host"
return 0 return 0

205
dnsapi/dns_nic.sh Normal file
View File

@ -0,0 +1,205 @@
#!/usr/bin/env sh
#
#NIC_ClientID='0dc0xxxxxxxxxxxxxxxxxxxxxxxxce88'
#NIC_ClientSecret='3LTtxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxnuW8'
#NIC_Username="000000/NIC-D"
#NIC_Password="xxxxxxx"
NIC_Api="https://api.nic.ru"
dns_nic_add() {
fulldomain="${1}"
txtvalue="${2}"
if ! _nic_get_authtoken save; then
_err "get NIC auth token failed"
return 1
fi
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "Invalid domain"
return 1
fi
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
_debug _service "$_service"
_info "Adding record"
if ! _nic_rest PUT "services/$_service/zones/$_domain/records" "<?xml version=\"1.0\" encoding=\"UTF-8\" ?><request><rr-list><rr><name>$_sub_domain</name><type>TXT</type><txt><string>$txtvalue</string></txt></rr></rr-list></request>"; then
_err "Add TXT record error"
return 1
fi
if ! _nic_rest POST "services/$_service/zones/$_domain/commit" ""; then
return 1
fi
_info "Added, OK"
}
dns_nic_rm() {
fulldomain="${1}"
txtvalue="${2}"
if ! _nic_get_authtoken; then
_err "get NIC auth token failed"
return 1
fi
if ! _get_root "$fulldomain"; then
_err "Invalid domain"
return 1
fi
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
_debug _service "$_service"
if ! _nic_rest GET "services/$_service/zones/$_domain/records"; then
_err "Get records error"
return 1
fi
_domain_id=$(printf "%s" "$response" | grep "$_sub_domain" | grep -- "$txtvalue" | sed -r "s/.*<rr id=\"(.*)\".*/\1/g")
if ! _nic_rest DELETE "services/$_service/zones/$_domain/records/$_domain_id"; then
_err "Delete record error"
return 1
fi
if ! _nic_rest POST "services/$_service/zones/$_domain/commit" ""; then
return 1
fi
}
#################### Private functions below ##################################
#_nic_get_auth_elements [need2save]
_nic_get_auth_elements() {
_need2save=$1
NIC_ClientID="${NIC_ClientID:-$(_readaccountconf_mutable NIC_ClientID)}"
NIC_ClientSecret="${NIC_ClientSecret:-$(_readaccountconf_mutable NIC_ClientSecret)}"
NIC_Username="${NIC_Username:-$(_readaccountconf_mutable NIC_Username)}"
NIC_Password="${NIC_Password:-$(_readaccountconf_mutable NIC_Password)}"
## for backward compatibility
if [ -z "$NIC_ClientID" ] || [ -z "$NIC_ClientSecret" ]; then
NIC_Token="${NIC_Token:-$(_readaccountconf_mutable NIC_Token)}"
_debug NIC_Token "$NIC_Token"
if [ -n "$NIC_Token" ]; then
_two_values="$(echo "${NIC_Token}" | _dbase64)"
_debug _two_values "$_two_values"
NIC_ClientID=$(echo "$_two_values" | cut -d':' -f1)
NIC_ClientSecret=$(echo "$_two_values" | cut -d':' -f2-)
_debug restored_NIC_ClientID "$NIC_ClientID"
_debug restored_NIC_ClientSecret "$NIC_ClientSecret"
fi
fi
if [ -z "$NIC_ClientID" ] || [ -z "$NIC_ClientSecret" ] || [ -z "$NIC_Username" ] || [ -z "$NIC_Password" ]; then
NIC_ClientID=""
NIC_ClientSecret=""
NIC_Username=""
NIC_Password=""
_err "You must export variables: NIC_ClientID, NIC_ClientSecret, NIC_Username and NIC_Password"
return 1
fi
if [ "$_need2save" ]; then
_saveaccountconf_mutable NIC_ClientID "$NIC_ClientID"
_saveaccountconf_mutable NIC_ClientSecret "$NIC_ClientSecret"
_saveaccountconf_mutable NIC_Username "$NIC_Username"
_saveaccountconf_mutable NIC_Password "$NIC_Password"
fi
NIC_BasicAuth=$(printf "%s:%s" "${NIC_ClientID}" "${NIC_ClientSecret}" | _base64)
_debug NIC_BasicAuth "$NIC_BasicAuth"
}
#_nic_get_authtoken [need2save]
_nic_get_authtoken() {
_need2save=$1
if ! _nic_get_auth_elements "$_need2save"; then
return 1
fi
_info "Getting NIC auth token"
export _H1="Authorization: Basic ${NIC_BasicAuth}"
export _H2="Content-Type: application/x-www-form-urlencoded"
res=$(_post "grant_type=password&username=${NIC_Username}&password=${NIC_Password}&scope=%28GET%7CPUT%7CPOST%7CDELETE%29%3A%2Fdns-master%2F.%2B" "$NIC_Api/oauth/token" "" "POST")
if _contains "$res" "access_token"; then
_auth_token=$(printf "%s" "$res" | cut -d , -f2 | tr -d "\"" | sed "s/access_token://")
_info "Token received"
_debug _auth_token "$_auth_token"
return 0
fi
return 1
}
_get_root() {
domain="$1"
i=1
p=1
if ! _nic_rest GET "zones"; then
return 1
fi
_all_domains=$(printf "%s" "$response" | grep "idn-name" | sed -r "s/.*idn-name=\"(.*)\" name=.*/\1/g")
_debug2 _all_domains "$_all_domains"
while true; do
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
_debug h "$h"
if [ -z "$h" ]; then
return 1
fi
if _contains "$_all_domains" "^$h$"; then
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_domain=$h
_service=$(printf "%s" "$response" | grep "idn-name=\"$_domain\"" | sed -r "s/.*service=\"(.*)\".*$/\1/")
return 0
fi
p="$i"
i=$(_math "$i" + 1)
done
return 1
}
_nic_rest() {
m="$1"
ep="$2"
data="$3"
_debug "$ep"
export _H1="Content-Type: application/xml"
export _H2="Authorization: Bearer $_auth_token"
if [ "$m" != "GET" ]; then
_debug data "$data"
response=$(_post "$data" "$NIC_Api/dns-master/$ep" "" "$m")
else
response=$(_get "$NIC_Api/dns-master/$ep")
fi
if _contains "$response" "<errors>"; then
error=$(printf "%s" "$response" | grep "error code" | sed -r "s/.*<error code=.*>(.*)<\/error>/\1/g")
_err "Error: $error"
return 1
fi
if ! _contains "$response" "<status>success</status>"; then
return 1
fi
_debug2 response "$response"
return 0
}

View File

@ -27,7 +27,7 @@ dns_nsupdate_add() {
[ -n "$DEBUG" ] && [ "$DEBUG" -ge "$DEBUG_LEVEL_2" ] && nsdebug="-D" [ -n "$DEBUG" ] && [ "$DEBUG" -ge "$DEBUG_LEVEL_2" ] && nsdebug="-D"
if [ -z "${NSUPDATE_ZONE}" ]; then if [ -z "${NSUPDATE_ZONE}" ]; then
nsupdate -k "${NSUPDATE_KEY}" $nsdebug <<EOF nsupdate -k "${NSUPDATE_KEY}" $nsdebug <<EOF
server ${NSUPDATE_SERVER} ${NSUPDATE_SERVER_PORT} server ${NSUPDATE_SERVER} ${NSUPDATE_SERVER_PORT}
update add ${fulldomain}. 60 in txt "${txtvalue}" update add ${fulldomain}. 60 in txt "${txtvalue}"
send send
EOF EOF
@ -64,7 +64,7 @@ dns_nsupdate_rm() {
[ -n "$DEBUG" ] && [ "$DEBUG" -ge "$DEBUG_LEVEL_2" ] && nsdebug="-D" [ -n "$DEBUG" ] && [ "$DEBUG" -ge "$DEBUG_LEVEL_2" ] && nsdebug="-D"
if [ -z "${NSUPDATE_ZONE}" ]; then if [ -z "${NSUPDATE_ZONE}" ]; then
nsupdate -k "${NSUPDATE_KEY}" $nsdebug <<EOF nsupdate -k "${NSUPDATE_KEY}" $nsdebug <<EOF
server ${NSUPDATE_SERVER} ${NSUPDATE_SERVER_PORT} server ${NSUPDATE_SERVER} ${NSUPDATE_SERVER_PORT}
update delete ${fulldomain}. txt update delete ${fulldomain}. txt
send send
EOF EOF

View File

@ -3,7 +3,7 @@
# This is the OpenProvider API wrapper for acme.sh # This is the OpenProvider API wrapper for acme.sh
# #
# Author: Sylvia van Os # Author: Sylvia van Os
# Report Bugs here: https://github.com/Neilpang/acme.sh/issues/2104 # Report Bugs here: https://github.com/acmesh-official/acme.sh/issues/2104
# #
# export OPENPROVIDER_USER="username" # export OPENPROVIDER_USER="username"
# export OPENPROVIDER_PASSWORDHASH="hashed_password" # export OPENPROVIDER_PASSWORDHASH="hashed_password"

273
dnsapi/dns_opnsense.sh Executable file
View File

@ -0,0 +1,273 @@
#!/usr/bin/env sh
#OPNsense Bind API
#https://docs.opnsense.org/development/api.html
#
#OPNs_Host="opnsense.example.com"
#OPNs_Port="443"
# optional, defaults to 443 if unset
#OPNs_Key="qocfU9RSbt8vTIBcnW8bPqCrpfAHMDvj5OzadE7Str+rbjyCyk7u6yMrSCHtBXabgDDXx/dY0POUp7ZA"
#OPNs_Token="pZEQ+3ce8dDlfBBdg3N8EpqpF5I1MhFqdxX06le6Gl8YzyQvYCfCzNaFX9O9+IOSyAs7X71fwdRiZ+Lv"
#OPNs_Api_Insecure=0
# optional, defaults to 0 if unset
# 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: add _acme-challenge.www.domain.com "123456789ABCDEF0000000000000000000000000000000000000"
#fulldomain
#txtvalue
OPNs_DefaultPort=443
OPNs_DefaultApi_Insecure=0
dns_opnsense_add() {
fulldomain=$1
txtvalue=$2
_opns_check_auth || return 1
if ! set_record "$fulldomain" "$txtvalue"; then
return 1
fi
return 0
}
#fulldomain
dns_opnsense_rm() {
fulldomain=$1
txtvalue=$2
_opns_check_auth || return 1
if ! rm_record "$fulldomain" "$txtvalue"; then
return 1
fi
return 0
}
set_record() {
fulldomain=$1
new_challenge=$2
_info "Adding record $fulldomain with challenge: $new_challenge"
_debug "Detect root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug _domain "$_domain"
_debug _host "$_host"
_debug _domainid "$_domainid"
_return_str=""
_record_string=""
_build_record_string "$_domainid" "$_host" "$new_challenge"
_uuid=""
if _existingchallenge "$_domain" "$_host" "$new_challenge"; then
# Update
if _opns_rest "POST" "/record/setRecord/${_uuid}" "$_record_string"; then
_return_str="$response"
else
return 1
fi
else
#create
if _opns_rest "POST" "/record/addRecord" "$_record_string"; then
_return_str="$response"
else
return 1
fi
fi
if echo "$_return_str" | _egrep_o "\"result\":\"saved\"" >/dev/null; then
_opns_rest "POST" "/service/reconfigure" "{}"
_debug "Record created"
else
_err "Error creating record $_record_string"
return 1
fi
return 0
}
rm_record() {
fulldomain=$1
new_challenge="$2"
_info "Remove record $fulldomain with challenge: $new_challenge"
_debug "Detect root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug _domain "$_domain"
_debug _host "$_host"
_debug _domainid "$_domainid"
_uuid=""
if _existingchallenge "$_domain" "$_host" "$new_challenge"; then
# Delete
if _opns_rest "POST" "/record/delRecord/${_uuid}" "\{\}"; then
if echo "$_return_str" | _egrep_o "\"result\":\"deleted\"" >/dev/null; then
_opns_rest "POST" "/service/reconfigure" "{}"
_debug "Record deleted"
else
_err "Error deleting record $_host from domain $fulldomain"
return 1
fi
else
_err "Error deleting record $_host from domain $fulldomain"
return 1
fi
else
_info "Record not found, nothing to remove"
fi
return 0
}
#################### Private functions below ##################################
#_acme-challenge.www.domain.com
#returns
# _domainid=domid
#_domain=domain.com
_get_root() {
domain=$1
i=2
p=1
if _opns_rest "GET" "/domain/get"; then
_domain_response="$response"
else
return 1
fi
while true; do
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
if [ -z "$h" ]; then
#not valid
return 1
fi
_debug h "$h"
id=$(echo "$_domain_response" | _egrep_o "\"[^\"]*\":{\"enabled\":\"1\",\"type\":{\"master\":{\"value\":\"master\",\"selected\":1},\"slave\":{\"value\":\"slave\",\"selected\":0}},\"masterip\":\"[^\"]*\",\"domainname\":\"${h}\"" | cut -d ':' -f 1 | cut -d '"' -f 2)
if [ -n "$id" ]; then
_debug id "$id"
_host=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_domain="${h}"
_domainid="${id}"
return 0
fi
p=$i
i=$(_math $i + 1)
done
_debug "$domain not found"
return 1
}
_opns_rest() {
method=$1
ep=$2
data=$3
#Percent encode user and token
key=$(echo "$OPNs_Key" | tr -d "\n\r" | _url_encode)
token=$(echo "$OPNs_Token" | tr -d "\n\r" | _url_encode)
opnsense_url="https://${key}:${token}@${OPNs_Host}:${OPNs_Port:-$OPNs_DefaultPort}/api/bind${ep}"
export _H1="Content-Type: application/json"
_debug2 "Try to call api: https://${OPNs_Host}:${OPNs_Port:-$OPNs_DefaultPort}/api/bind${ep}"
if [ ! "$method" = "GET" ]; then
_debug data "$data"
export _H1="Content-Type: application/json"
response="$(_post "$data" "$opnsense_url" "" "$method")"
else
export _H1=""
response="$(_get "$opnsense_url")"
fi
if [ "$?" != "0" ]; then
_err "error $ep"
return 1
fi
_debug2 response "$response"
return 0
}
_build_record_string() {
_record_string="{\"record\":{\"enabled\":\"1\",\"domain\":\"$1\",\"name\":\"$2\",\"type\":\"TXT\",\"value\":\"$3\"}}"
}
_existingchallenge() {
if _opns_rest "GET" "/record/searchRecord"; then
_record_response="$response"
else
return 1
fi
_uuid=""
_uuid=$(echo "$_record_response" | _egrep_o "\"uuid\":\"[^\"]*\",\"enabled\":\"[01]\",\"domain\":\"$1\",\"name\":\"$2\",\"type\":\"TXT\",\"value\":\"$3\"" | cut -d ':' -f 2 | cut -d '"' -f 2)
if [ -n "$_uuid" ]; then
_debug uuid "$_uuid"
return 0
fi
_debug "${2}.$1{1} record not found"
return 1
}
_opns_check_auth() {
OPNs_Host="${OPNs_Host:-$(_readaccountconf_mutable OPNs_Host)}"
OPNs_Port="${OPNs_Port:-$(_readaccountconf_mutable OPNs_Port)}"
OPNs_Key="${OPNs_Key:-$(_readaccountconf_mutable OPNs_Key)}"
OPNs_Token="${OPNs_Token:-$(_readaccountconf_mutable OPNs_Token)}"
OPNs_Api_Insecure="${OPNs_Api_Insecure:-$(_readaccountconf_mutable OPNs_Api_Insecure)}"
if [ -z "$OPNs_Host" ]; then
_err "You don't specify OPNsense address."
return 1
else
_saveaccountconf_mutable OPNs_Host "$OPNs_Host"
fi
if ! printf '%s' "$OPNs_Port" | grep '^[0-9]*$' >/dev/null; then
_err 'OPNs_Port specified but not numeric value'
return 1
elif [ -z "$OPNs_Port" ]; then
_info "OPNSense port not specified. Defaulting to using port $OPNs_DefaultPort"
else
_saveaccountconf_mutable OPNs_Port "$OPNs_Port"
fi
if ! printf '%s' "$OPNs_Api_Insecure" | grep '^[01]$' >/dev/null; then
_err 'OPNs_Api_Insecure specified but not 0/1 value'
return 1
elif [ -n "$OPNs_Api_Insecure" ]; then
_saveaccountconf_mutable OPNs_Api_Insecure "$OPNs_Api_Insecure"
fi
export HTTPS_INSECURE="${OPNs_Api_Insecure:-$OPNs_DefaultApi_Insecure}"
if [ -z "$OPNs_Key" ]; then
_err "you have not specified your OPNsense api key id."
_err "Please set OPNs_Key and try again."
return 1
else
_saveaccountconf_mutable OPNs_Key "$OPNs_Key"
fi
if [ -z "$OPNs_Token" ]; then
_err "you have not specified your OPNsense token."
_err "Please create OPNs_Token and try again."
return 1
else
_saveaccountconf_mutable OPNs_Token "$OPNs_Token"
fi
if ! _opns_rest "GET" "/general/get"; then
_err "Call to OPNsense API interface failed. Unable to access OPNsense API."
return 1
fi
return 0
}

View File

@ -32,9 +32,9 @@ SYS_CA='https://ca.api.soyoustart.com/1.0'
#'runabove-ca' #'runabove-ca'
RAV_CA='https://api.runabove.com/1.0' RAV_CA='https://api.runabove.com/1.0'
wiki="https://github.com/Neilpang/acme.sh/wiki/How-to-use-OVH-domain-api" wiki="https://github.com/acmesh-official/acme.sh/wiki/How-to-use-OVH-domain-api"
ovh_success="https://github.com/Neilpang/acme.sh/wiki/OVH-Success" ovh_success="https://github.com/acmesh-official/acme.sh/wiki/OVH-Success"
_ovh_get_api() { _ovh_get_api() {
_ogaep="$1" _ogaep="$1"

414
dnsapi/dns_pleskxml.sh Normal file
View File

@ -0,0 +1,414 @@
#!/usr/bin/env sh
## Name: dns_pleskxml.sh
## Created by Stilez.
## Also uses some code from PR#1832 by @romanlum (https://github.com/acmesh-official/acme.sh/pull/1832/files)
## This DNS-01 method uses the Plesk XML API described at:
## https://docs.plesk.com/en-US/12.5/api-rpc/about-xml-api.28709
## and more specifically: https://docs.plesk.com/en-US/12.5/api-rpc/reference.28784
## Note: a DNS ID with host = empty string is OK for this API, see
## https://docs.plesk.com/en-US/obsidian/api-rpc/about-xml-api/reference/managing-dns/managing-dns-records/adding-dns-record.34798
## For example, to add a TXT record to DNS alias domain "acme-alias.com" would be a valid Plesk action.
## So this API module can handle such a request, if needed.
## For ACME v2 purposes, new TXT records are appended when added, and removing one TXT record will not affect any other TXT records.
## The user credentials (username+password) and URL/URI for the Plesk XML API must be set by the user
## before this module is called (case sensitive):
##
## ```
## export pleskxml_uri="https://address-of-my-plesk-server.net:8443/enterprise/control/agent.php"
## (or probably something similar)
## export pleskxml_user="my plesk username"
## export pleskxml_pass="my plesk password"
## ```
## Ok, let's issue a cert now:
## ```
## acme.sh --issue --dns dns_pleskxml -d example.com -d www.example.com
## ```
##
## The `pleskxml_uri`, `pleskxml_user` and `pleskxml_pass` will be saved in `~/.acme.sh/account.conf` and reused when needed.
#################### INTERNAL VARIABLES + NEWLINE + API TEMPLATES ##################################
pleskxml_init_checks_done=0
# Variable containing bare newline - not a style issue
# shellcheck disable=SC1004
NEWLINE='\
'
pleskxml_tplt_get_domains="<packet><customer><get-domain-list><filter/></get-domain-list></customer></packet>"
# Get a list of domains that PLESK can manage, so we can check root domain + host for acme.sh
# Also used to test credentials and URI.
# No params.
pleskxml_tplt_get_dns_records="<packet><dns><get_rec><filter><site-id>%s</site-id></filter></get_rec></dns></packet>"
# Get all DNS records for a Plesk domain ID.
# PARAM = Plesk domain id to query
pleskxml_tplt_add_txt_record="<packet><dns><add_rec><site-id>%s</site-id><type>TXT</type><host>%s</host><value>%s</value></add_rec></dns></packet>"
# Add a TXT record to a domain.
# PARAMS = (1) Plesk internal domain ID, (2) "hostname" for the new record, eg '_acme_challenge', (3) TXT record value
pleskxml_tplt_rmv_dns_record="<packet><dns><del_rec><filter><id>%s</id></filter></del_rec></dns></packet>"
# Delete a specific TXT record from a domain.
# PARAM = the Plesk internal ID for the DNS record to be deleted
#################### Public functions ##################################
#Usage: dns_pleskxml_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_pleskxml_add() {
fulldomain=$1
txtvalue=$2
_info "Entering dns_pleskxml_add() to add TXT record '$txtvalue' to domain '$fulldomain'..."
# Get credentials if not already checked, and confirm we can log in to Plesk XML API
if ! _credential_check; then
return 1
fi
# Get root and subdomain details, and Plesk domain ID
if ! _pleskxml_get_root_domain "$fulldomain"; then
return 1
fi
_debug 'Credentials OK, and domain identified. Calling Plesk XML API to add TXT record'
# printf using template in a variable - not a style issue
# shellcheck disable=SC2059
request="$(printf "$pleskxml_tplt_add_txt_record" "$root_domain_id" "$sub_domain_name" "$txtvalue")"
if ! _call_api "$request"; then
return 1
fi
# OK, we should have added a TXT record. Let's check and return success if so.
# All that should be left in the result, is one section, containing <result><status>ok</status><id>NEW_DNS_RECORD_ID</id></result>
results="$(_api_response_split "$pleskxml_prettyprint_result" 'result' '<status>')"
if ! _value "$results" | grep '<status>ok</status>' | grep '<id>[0-9]\{1,\}</id>' >/dev/null; then
# Error - doesn't contain expected string. Something's wrong.
_err 'Error when calling Plesk XML API.'
_err 'The result did not contain the expected <id>XXXXX</id> section, or contained other values as well.'
_err 'This is unexpected: something has gone wrong.'
_err 'The full response was:'
_err "$pleskxml_prettyprint_result"
return 1
fi
recid="$(_value "$results" | grep '<id>[0-9]\{1,\}</id>' | sed 's/^.*<id>\([0-9]\{1,\}\)<\/id>.*$/\1/')"
_info "Success. TXT record appears to be correctly added (Plesk record ID=$recid). Exiting dns_pleskxml_add()."
return 0
}
#Usage: dns_pleskxml_rm _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_pleskxml_rm() {
fulldomain=$1
txtvalue=$2
_info "Entering dns_pleskxml_rm() to remove TXT record '$txtvalue' from domain '$fulldomain'..."
# Get credentials if not already checked, and confirm we can log in to Plesk XML API
if ! _credential_check; then
return 1
fi
# Get root and subdomain details, and Plesk domain ID
if ! _pleskxml_get_root_domain "$fulldomain"; then
return 1
fi
_debug 'Credentials OK, and domain identified. Calling Plesk XML API to get list of TXT records and their IDs'
# printf using template in a variable - not a style issue
# shellcheck disable=SC2059
request="$(printf "$pleskxml_tplt_get_dns_records" "$root_domain_id")"
if ! _call_api "$request"; then
return 1
fi
# Reduce output to one line per DNS record, filtered for TXT records with a record ID only (which they should all have)
# Also strip out spaces between tags, redundant <data> and </data> group tags and any <self-closing/> tags
reclist="$(_api_response_split "$pleskxml_prettyprint_result" 'result' '<status>ok</status>' \
| sed 's# \{1,\}<\([a-zA-Z]\)#<\1#g;s#</\{0,1\}data>##g;s#<[a-z][^/<>]*/>##g' \
| grep "<site-id>${root_domain_id}</site-id>" \
| grep '<id>[0-9]\{1,\}</id>' \
| grep '<type>TXT</type>'
)"
if [ -z "$reclist" ]; then
_err "No TXT records found for root domain ${root_domain_name} (Plesk domain ID ${root_domain_id}). Exiting."
return 1
fi
_debug "Got list of DNS TXT records for root domain '$root_domain_name':"
_debug "$reclist"
recid="$(_value "$reclist" \
| grep "<host>${fulldomain}.</host>" \
| grep "<value>${txtvalue}</value>" \
| sed 's/^.*<id>\([0-9]\{1,\}\)<\/id>.*$/\1/'
)"
if ! _value "$recid" | grep '^[0-9]\{1,\}$' >/dev/null; then
_err "DNS records for root domain '${root_domain_name}' (Plesk ID ${root_domain_id}) + host '${sub_domain_name}' do not contain the TXT record '${txtvalue}'"
_err "Cannot delete TXT record. Exiting."
return 1
fi
_debug "Found Plesk record ID for target text string '${txtvalue}': ID=${recid}"
_debug 'Calling Plesk XML API to remove TXT record'
# printf using template in a variable - not a style issue
# shellcheck disable=SC2059
request="$(printf "$pleskxml_tplt_rmv_dns_record" "$recid")"
if ! _call_api "$request"; then
return 1
fi
# OK, we should have removed a TXT record. Let's check and return success if so.
# All that should be left in the result, is one section, containing <result><status>ok</status><id>PLESK_DELETED_DNS_RECORD_ID</id></result>
results="$(_api_response_split "$pleskxml_prettyprint_result" 'result' '<status>')"
if ! _value "$results" | grep '<status>ok</status>' | grep '<id>[0-9]\{1,\}</id>' >/dev/null; then
# Error - doesn't contain expected string. Something's wrong.
_err 'Error when calling Plesk XML API.'
_err 'The result did not contain the expected <id>XXXXX</id> section, or contained other values as well.'
_err 'This is unexpected: something has gone wrong.'
_err 'The full response was:'
_err "$pleskxml_prettyprint_result"
return 1
fi
_info "Success. TXT record appears to be correctly removed. Exiting dns_pleskxml_rm()."
return 0
}
#################### Private functions below (utility functions) ##################################
# Outputs value of a variable without additional newlines etc
_value() {
printf '%s' "$1"
}
# Outputs value of a variable (FQDN) and cuts it at 2 specified '.' delimiters, returning the text in between
# $1, $2 = where to cut
# $3 = FQDN
_valuecut() {
printf '%s' "$3" | cut -d . -f "${1}-${2}"
}
# Counts '.' present in a domain name or other string
# $1 = domain name
_countdots() {
_value "$1" | tr -dc '.' | wc -c | sed 's/ //g'
}
# Cleans up an API response, splits it "one line per item in the response" and greps for a string that in the context, identifies "useful" lines
# $1 - result string from API
# $2 - plain text tag to resplit on (usually "result" or "domain"). NOT REGEX
# $3 - basic regex to recognise useful return lines
# note: $3 matches via basic NOT extended regex (BRE), as extended regex capabilities not needed at the moment.
# Last line could change to <sed -n '/.../p'> instead, with suitable escaping of ['"/$],
# if future Plesk XML API changes ever require extended regex
_api_response_split() {
printf '%s' "$1" \
| sed 's/^ +//;s/ +$//' \
| tr -d '\n\r' \
| sed "s/<\/\{0,1\}$2>/${NEWLINE}/g" \
| grep "$3"
}
#################### Private functions below (DNS functions) ##################################
# Calls Plesk XML API, and checks results for obvious issues
_call_api() {
request="$1"
errtext=''
_debug 'Entered _call_api(). Calling Plesk XML API with request:'
_debug "'$request'"
export _H1="HTTP_AUTH_LOGIN: $pleskxml_user"
export _H2="HTTP_AUTH_PASSWD: $pleskxml_pass"
export _H3="content-Type: text/xml"
export _H4="HTTP_PRETTY_PRINT: true"
pleskxml_prettyprint_result="$(_post "${request}" "$pleskxml_uri" "" "POST")"
pleskxml_retcode="$?"
_debug 'The responses from the Plesk XML server were:'
_debug "retcode=$pleskxml_retcode. Literal response:"
_debug "'$pleskxml_prettyprint_result'"
# Detect any <status> that isn't "ok". None of the used calls should fail if the API is working correctly.
# Also detect if there simply aren't any status lines (null result?) and report that, as well.
statuslines_count_total="$(echo "$pleskxml_prettyprint_result" | grep -c '^ *<status>[^<]*</status> *$')"
statuslines_count_okay="$(echo "$pleskxml_prettyprint_result" | grep -c '^ *<status>ok</status> *$')"
if [ -z "$statuslines_count_total" ]; then
# We have no status lines at all. Results are empty
errtext='The Plesk XML API unexpectedly returned an empty set of results for this call.'
elif [ "$statuslines_count_okay" -ne "$statuslines_count_total" ]; then
# We have some status lines that aren't "ok". Any available details are in API response fields "status" "errcode" and "errtext"
# Workaround for basic regex:
# - filter output to keep only lines like this: "SPACES<TAG>text</TAG>SPACES" (shouldn't be necessary with prettyprint but guarantees subsequent code is ok)
# - then edit the 3 "useful" error tokens individually and remove closing tags on all lines
# - then filter again to remove all lines not edited (which will be the lines not starting A-Z)
errtext="$(_value "$pleskxml_prettyprint_result" \
| grep '^ *<[a-z]\{1,\}>[^<]*<\/[a-z]\{1,\}> *$' \
| sed 's/^ *<status>/Status: /;s/^ *<errcode>/Error code: /;s/^ *<errtext>/Error text: /;s/<\/.*$//' \
| grep '^[A-Z]'
)"
fi
if [ "$pleskxml_retcode" -ne 0 ] || [ "$errtext" != "" ]; then
# Call failed, for reasons either in the retcode or the response text...
if [ "$pleskxml_retcode" -eq 0 ]; then
_err "The POST request was successfully sent to the Plesk server."
else
_err "The return code for the POST request was $pleskxml_retcode (non-zero = failure in submitting request to server)."
fi
if [ "$errtext" != "" ]; then
_err 'The error responses received from the Plesk server were:'
_err "$errtext"
else
_err "No additional error messages were received back from the Plesk server"
fi
_err "The Plesk XML API call failed."
return 1
fi
_debug "Leaving _call_api(). Successful call."
return 0
}
# Startup checks (credentials, URI)
_credential_check() {
_debug "Checking Plesk XML API login credentials and URI..."
if [ "$pleskxml_init_checks_done" -eq 1 ]; then
_debug "Initial checks already done, no need to repeat. Skipped."
return 0
fi
pleskxml_user="${pleskxml_user:-$(_readaccountconf_mutable pleskxml_user)}"
pleskxml_pass="${pleskxml_pass:-$(_readaccountconf_mutable pleskxml_pass)}"
pleskxml_uri="${pleskxml_uri:-$(_readaccountconf_mutable pleskxml_uri)}"
if [ -z "$pleskxml_user" ] || [ -z "$pleskxml_pass" ] || [ -z "$pleskxml_uri" ]; then
pleskxml_user=""
pleskxml_pass=""
pleskxml_uri=""
_err "You didn't specify one or more of the Plesk XML API username, password, or URI."
_err "Please create these and try again."
_err "Instructions are in the 'dns_pleskxml' plugin source code or in the acme.sh documentation."
return 1
fi
# Test the API is usable, by trying to read the list of managed domains...
_call_api "$pleskxml_tplt_get_domains"
if [ "$pleskxml_retcode" -ne 0 ]; then
_err 'Failed to access Plesk XML API.'
_err "Please check your login credentials and Plesk URI, and that the URI is reachable, and try again."
return 1
fi
_saveaccountconf_mutable pleskxml_uri "$pleskxml_uri"
_saveaccountconf_mutable pleskxml_user "$pleskxml_user"
_saveaccountconf_mutable pleskxml_pass "$pleskxml_pass"
_debug "Test login to Plesk XML API successful. Login credentials and URI successfully saved to the acme.sh configuration file for future use."
pleskxml_init_checks_done=1
return 0
}
# For a FQDN, identify the root domain managed by Plesk, its domain ID in Plesk, and the host if any.
# IMPORTANT NOTE: a result with host = empty string is OK for this API, see
# https://docs.plesk.com/en-US/obsidian/api-rpc/about-xml-api/reference/managing-dns/managing-dns-records/adding-dns-record.34798
# See notes at top of this file
_pleskxml_get_root_domain() {
original_full_domain_name="$1"
_debug "Identifying DNS root domain for '$original_full_domain_name' that is managed by the Plesk account."
# test if the domain as provided is valid for splitting.
if [ "$(_countdots "$original_full_domain_name")" -eq 0 ]; then
_err "Invalid domain. The ACME domain must contain at least two parts (aa.bb) to identify a domain and tld for the TXT record."
return 1
fi
_debug "Querying Plesk server for list of managed domains..."
_call_api "$pleskxml_tplt_get_domains"
if [ "$pleskxml_retcode" -ne 0 ]; then
return 1
fi
# Generate a crude list of domains known to this Plesk account.
# We convert <ascii-name> tags to <name> so it'll flag on a hit with either <name> or <ascii-name> fields,
# for non-Western character sets.
# Output will be one line per known domain, containing 2 <name> tages and a single <id> tag
# We don't actually need to check for type, name, *and* id, but it guarantees only usable lines are returned.
output="$(_api_response_split "$pleskxml_prettyprint_result" 'domain' '<type>domain</type>' | sed 's/<ascii-name>/<name>/g;s/<\/ascii-name>/<\/name>/g' | grep '<name>' | grep '<id>')"
_debug 'Domains managed by Plesk server are (ignore the hacked output):'
_debug "$output"
# loop and test if domain, or any parent domain, is managed by Plesk
# Loop until we don't have any '.' in the string we're testing as a candidate Plesk-managed domain
root_domain_name="$original_full_domain_name"
while true; do
_debug "Checking if '$root_domain_name' is managed by the Plesk server..."
root_domain_id="$(_value "$output" | grep "<name>$root_domain_name</name>" | _head_n 1 | sed 's/^.*<id>\([0-9]\{1,\}\)<\/id>.*$/\1/')"
if [ -n "$root_domain_id" ]; then
# Found a match
# SEE IMPORTANT NOTE ABOVE - THIS FUNCTION CAN RETURN HOST='', AND THAT'S OK FOR PLESK XML API WHICH ALLOWS IT.
# SO WE HANDLE IT AND DON'T PREVENT IT
sub_domain_name="$(_value "$original_full_domain_name" | sed "s/\.\{0,1\}${root_domain_name}"'$//')"
_info "Success. Matched host '$original_full_domain_name' to: DOMAIN '${root_domain_name}' (Plesk ID '${root_domain_id}'), HOST '${sub_domain_name}'. Returning."
return 0
fi
# No match, try next parent up (if any)...
root_domain_name="$(_valuecut 2 1000 "$root_domain_name")"
if [ "$(_countdots "$root_domain_name")" -eq 0 ]; then
_debug "No match, and next parent would be a TLD..."
_err "Cannot find '$original_full_domain_name' or any parent domain of it, in Plesk."
_err "Are you sure that this domain is managed by this Plesk server?"
return 1
fi
_debug "No match, trying next parent up..."
done
}

View File

@ -9,7 +9,7 @@ RACKSPACE_Endpoint="https://dns.api.rackspacecloud.com/v1.0"
# 20190213 - The name & id fields swapped in the API response; fix sed # 20190213 - The name & id fields swapped in the API response; fix sed
# 20190101 - Duplicating file for new pull request to dev branch # 20190101 - Duplicating file for new pull request to dev branch
# Original - tcocca:rackspace_dnsapi https://github.com/Neilpang/acme.sh/pull/1297 # Original - tcocca:rackspace_dnsapi https://github.com/acmesh-official/acme.sh/pull/1297
######## Public functions ##################### ######## Public functions #####################
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" #Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"

224
dnsapi/dns_rcode0.sh Executable file
View File

@ -0,0 +1,224 @@
#!/usr/bin/env sh
#Rcode0 API Integration
#https://my.rcodezero.at/api-doc
#
# log into https://my.rcodezero.at/enableapi and get your ACME API Token (the ACME API token has limited
# access to the REST calls needed for acme.sh only)
#
#RCODE0_URL="https://my.rcodezero.at"
#RCODE0_API_TOKEN="0123456789ABCDEF"
#RCODE0_TTL=60
DEFAULT_RCODE0_URL="https://my.rcodezero.at"
DEFAULT_RCODE0_TTL=60
######## Public functions #####################
#Usage: add _acme-challenge.www.domain.com "123456789ABCDEF0000000000000000000000000000000000000"
#fulldomain
#txtvalue
dns_rcode0_add() {
fulldomain=$1
txtvalue=$2
RCODE0_API_TOKEN="${RCODE0_API_TOKEN:-$(_readaccountconf_mutable RCODE0_API_TOKEN)}"
RCODE0_URL="${RCODE0_URL:-$(_readaccountconf_mutable RCODE0_URL)}"
RCODE0_TTL="${RCODE0_TTL:-$(_readaccountconf_mutable RCODE0_TTL)}"
if [ -z "$RCODE0_URL" ]; then
RCODE0_URL="$DEFAULT_RCODE0_URL"
fi
if [ -z "$RCODE0_API_TOKEN" ]; then
RCODE0_API_TOKEN=""
_err "Missing Rcode0 ACME API Token."
_err "Please login and create your token at httsp://my.rcodezero.at/enableapi and try again."
return 1
fi
if [ -z "$RCODE0_TTL" ]; then
RCODE0_TTL="$DEFAULT_RCODE0_TTL"
fi
#save the token to the account conf file.
_saveaccountconf_mutable RCODE0_API_TOKEN "$RCODE0_API_TOKEN"
if [ "$RCODE0_URL" != "$DEFAULT_RCODE0_URL" ]; then
_saveaccountconf_mutable RCODE0_URL "$RCODE0_URL"
fi
if [ "$RCODE0_TTL" != "$DEFAULT_RCODE0_TTL" ]; then
_saveaccountconf_mutable RCODE0_TTL "$RCODE0_TTL"
fi
_debug "Detect root zone"
if ! _get_root "$fulldomain"; then
_err "No 'MASTER' zone for $fulldomain found at RcodeZero Anycast."
return 1
fi
_debug _domain "$_domain"
_debug "Adding record"
_record_string=""
_build_record_string "$txtvalue"
_list_existingchallenges
for oldchallenge in $_existing_challenges; do
_build_record_string "$oldchallenge"
done
_debug "Challenges: $_existing_challenges"
if [ -z "$_existing_challenges" ]; then
if ! _rcode0_rest "PATCH" "/api/v1/acme/zones/$_domain/rrsets" "[{\"changetype\": \"add\", \"name\": \"$fulldomain.\", \"type\": \"TXT\", \"ttl\": $RCODE0_TTL, \"records\": [$_record_string]}]"; then
_err "Add txt record error."
return 1
fi
else
# try update in case a records exists (need for wildcard certs)
if ! _rcode0_rest "PATCH" "/api/v1/acme/zones/$_domain/rrsets" "[{\"changetype\": \"update\", \"name\": \"$fulldomain.\", \"type\": \"TXT\", \"ttl\": $RCODE0_TTL, \"records\": [$_record_string]}]"; then
_err "Set txt record error."
return 1
fi
fi
return 0
}
#fulldomain txtvalue
dns_rcode0_rm() {
fulldomain=$1
txtvalue=$2
RCODE0_API_TOKEN="${RCODE0_API_TOKEN:-$(_readaccountconf_mutable RCODE0_API_TOKEN)}"
RCODE0_URL="${RCODE0_URL:-$(_readaccountconf_mutable RCODE0_URL)}"
RCODE0_TTL="${RCODE0_TTL:-$(_readaccountconf_mutable RCODE0_TTL)}"
if [ -z "$RCODE0_URL" ]; then
RCODE0_URL="$DEFAULT_RCODE0_URL"
fi
if [ -z "$RCODE0_API_TOKEN" ]; then
RCODE0_API_TOKEN=""
_err "Missing Rcode0 API Token."
_err "Please login and create your token at httsp://my.rcodezero.at/enableapi and try again."
return 1
fi
#save the api addr and key to the account conf file.
_saveaccountconf_mutable RCODE0_URL "$RCODE0_URL"
_saveaccountconf_mutable RCODE0_API_TOKEN "$RCODE0_API_TOKEN"
if [ "$RCODE0_TTL" != "$DEFAULT_RCODE0_TTL" ]; then
_saveaccountconf_mutable RCODE0_TTL "$RCODE0_TTL"
fi
if [ -z "$RCODE0_TTL" ]; then
RCODE0_TTL="$DEFAULT_RCODE0_TTL"
fi
_debug "Detect root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug "Remove record"
#Enumerate existing acme challenges
_list_existingchallenges
if _contains "$_existing_challenges" "$txtvalue"; then
#Delete all challenges (PowerDNS API does not allow to delete content)
if ! _rcode0_rest "PATCH" "/api/v1/acme/zones/$_domain/rrsets" "[{\"changetype\": \"delete\", \"name\": \"$fulldomain.\", \"type\": \"TXT\"}]"; then
_err "Delete txt record error."
return 1
fi
_record_string=""
#If the only existing challenge was the challenge to delete: nothing to do
if ! [ "$_existing_challenges" = "$txtvalue" ]; then
for oldchallenge in $_existing_challenges; do
#Build up the challenges to re-add, ommitting the one what should be deleted
if ! [ "$oldchallenge" = "$txtvalue" ]; then
_build_record_string "$oldchallenge"
fi
done
#Recreate the existing challenges
if ! _rcode0_rest "PATCH" "/api/v1/acme/zones/$_domain/rrsets" "[{\"changetype\": \"update\", \"name\": \"$fulldomain.\", \"type\": \"TXT\", \"ttl\": $RCODE0_TTL, \"records\": [$_record_string]}]"; then
_err "Set txt record error."
return 1
fi
fi
else
_info "Record not found, nothing to remove"
fi
return 0
}
#################### Private functions below ##################################
#_acme-challenge.www.domain.com
#returns
# _domain=domain.com
_get_root() {
domain=$1
i=1
while true; do
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
_debug "try to find: $h"
if _rcode0_rest "GET" "/api/v1/acme/zones/$h"; then
if [ "$response" = "[\"found\"]" ]; then
_domain="$h"
if [ -z "$h" ]; then
_domain="=2E"
fi
return 0
elif [ "$response" = "[\"not a master domain\"]" ]; then
return 1
fi
fi
if [ -z "$h" ]; then
return 1
fi
i=$(_math $i + 1)
done
_debug "no matching domain for $domain found"
return 1
}
_rcode0_rest() {
method=$1
ep=$2
data=$3
export _H1="Authorization: Bearer $RCODE0_API_TOKEN"
if [ ! "$method" = "GET" ]; then
_debug data "$data"
response="$(_post "$data" "$RCODE0_URL$ep" "" "$method")"
else
response="$(_get "$RCODE0_URL$ep")"
fi
if [ "$?" != "0" ]; then
_err "error $ep"
return 1
fi
_debug2 response "$response"
return 0
}
_build_record_string() {
_record_string="${_record_string:+${_record_string}, }{\"content\": \"\\\"${1}\\\"\", \"disabled\": false}"
}
_list_existingchallenges() {
_rcode0_rest "GET" "/api/v1/acme/zones/$_domain/rrsets"
_existing_challenges=$(echo "$response" | _normalizeJson | _egrep_o "\"name\":\"${fulldomain}[^]]*}" | _egrep_o 'content\":\"\\"[^\\]*' | sed -n 's/^content":"\\"//p')
_debug2 "$_existing_challenges"
}

View File

@ -1,7 +1,7 @@
#!/usr/bin/env sh #!/usr/bin/env sh
########## ##########
# Custom servercow.de DNS API v1 for use with [acme.sh](https://github.com/Neilpang/acme.sh) # Custom servercow.de DNS API v1 for use with [acme.sh](https://github.com/acmesh-official/acme.sh)
# #
# Usage: # Usage:
# export SERVERCOW_API_Username=username # export SERVERCOW_API_Username=username

View File

@ -52,7 +52,7 @@ dns_unoeuro_add() {
fi fi
_info "Adding record" _info "Adding record"
if _uno_rest POST "my/products/$h/dns/records" "{\"name\":\"$fulldomain\",\"type\":\"TXT\",\"data\":\"$txtvalue\",\"ttl\":120}"; then if _uno_rest POST "my/products/$h/dns/records" "{\"name\":\"$fulldomain\",\"type\":\"TXT\",\"data\":\"$txtvalue\",\"ttl\":120,\"priority\":0}"; then
if _contains "$response" "\"status\": 200" >/dev/null; then if _contains "$response" "\"status\": 200" >/dev/null; then
_info "Added, OK" _info "Added, OK"
return 0 return 0

147
dnsapi/dns_variomedia.sh Normal file
View File

@ -0,0 +1,147 @@
#!/usr/bin/env sh
#
#VARIOMEDIA_API_TOKEN=000011112222333344445555666677778888
VARIOMEDIA_API="https://api.variomedia.de"
######## Public functions #####################
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_variomedia_add() {
fulldomain=$1
txtvalue=$2
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
VARIOMEDIA_API_TOKEN="${VARIOMEDIA_API_TOKEN:-$(_readaccountconf_mutable VARIOMEDIA_API_TOKEN)}"
if test -z "$VARIOMEDIA_API_TOKEN"; then
VARIOMEDIA_API_TOKEN=""
_err 'VARIOMEDIA_API_TOKEN was not exported'
return 1
fi
_saveaccountconf_mutable VARIOMEDIA_API_TOKEN "$VARIOMEDIA_API_TOKEN"
_debug 'First detect the root zone'
if ! _get_root "$fulldomain"; then
return 1
fi
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
if ! _variomedia_rest POST "dns-records" "{\"data\": {\"type\": \"dns-record\", \"attributes\": {\"record_type\": \"TXT\", \"name\": \"$_sub_domain\", \"domain\": \"$_domain\", \"data\": \"$txtvalue\", \"ttl\":300}}}"; then
_err "$response"
return 1
fi
_debug2 _response "$response"
return 0
}
#fulldomain txtvalue
dns_variomedia_rm() {
fulldomain=$1
txtvalue=$2
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
VARIOMEDIA_API_TOKEN="${VARIOMEDIA_API_TOKEN:-$(_readaccountconf_mutable VARIOMEDIA_API_TOKEN)}"
if test -z "$VARIOMEDIA_API_TOKEN"; then
VARIOMEDIA_API_TOKEN=""
_err 'VARIOMEDIA_API_TOKEN was not exported'
return 1
fi
_saveaccountconf_mutable VARIOMEDIA_API_TOKEN "$VARIOMEDIA_API_TOKEN"
_debug 'First detect the root zone'
if ! _get_root "$fulldomain"; then
return 1
fi
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
_debug 'Getting txt records'
if ! _variomedia_rest GET "dns-records?filter[domain]=$_domain"; then
_err 'Error'
return 1
fi
_record_id="$(echo "$response" | cut -d '[' -f2 | cut -d']' -f1 | sed 's/},[ \t]*{/\},§\{/g' | tr § '\n' | grep "$_sub_domain" | grep "$txtvalue" | sed 's/^{//;s/}[,]?$//' | tr , '\n' | tr -d '\"' | grep ^id | cut -d : -f2 | tr -d ' ')"
_debug _record_id "$_record_id"
if [ "$_record_id" ]; then
_info "Successfully retrieved the record id for ACME challenge."
else
_info "Empty record id, it seems no such record."
return 0
fi
if ! _variomedia_rest DELETE "/dns-records/$_record_id"; then
_err "$response"
return 1
fi
_debug2 _response "$response"
return 0
}
#################### Private functions below ##################################
#_acme-challenge.www.domain.com
#returns
# _sub_domain=_acme-challenge.www
# _domain=domain.com
_get_root() {
fulldomain=$1
i=1
while true; do
h=$(printf "%s" "$fulldomain" | cut -d . -f $i-100)
_debug h "$h"
if [ -z "$h" ]; then
return 1
fi
if ! _variomedia_rest GET "domains/$h"; then
return 1
fi
if _startswith "$response" "\{\"data\":"; then
if _contains "$response" "\"id\": \"$h\""; then
_sub_domain="$(echo "$fulldomain" | sed "s/\\.$h\$//")"
_domain=$h
return 0
fi
fi
i=$(_math "$i" + 1)
done
_debug "root domain not found"
return 1
}
_variomedia_rest() {
m=$1
ep="$2"
data="$3"
_debug "$ep"
export _H1="Authorization: token $VARIOMEDIA_API_TOKEN"
export _H2="Content-Type: application/vnd.api+json"
export _H3="Accept: application/vnd.variomedia.v1+json"
if [ "$m" != "GET" ]; then
_debug data "$data"
response="$(_post "$data" "$VARIOMEDIA_API/$ep" "" "$m")"
else
response="$(_get "$VARIOMEDIA_API/$ep")"
fi
if [ "$?" != "0" ]; then
_err "Error $ep"
return 1
fi
_debug2 response "$response"
return 0
}

View File

@ -102,7 +102,7 @@ _get_root() {
return 1 return 1
fi fi
hostedzone="$(echo "$response" | _egrep_o "{.*\"name\":\s*\"$h\".*}")" hostedzone="$(echo "$response" | tr "{" "\n" | _egrep_o "\"name\":\s*\"$h\".*}")"
if [ "$hostedzone" ]; then if [ "$hostedzone" ]; then
_domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o "\"id\":\s*[0-9]+" | _head_n 1 | cut -d : -f 2 | tr -d \ ) _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 if [ "$_domain_id" ]; then

View File

@ -106,9 +106,9 @@ _get_root() {
domain=$1 domain=$1
i=1 i=1
while true; do while true; do
h=$(printf "%s" "$domain" | cut -d . -f $i-100) _domain=$(printf "%s" "$domain" | cut -d . -f $i-100)
_debug h "$h" _debug h "$_domain"
if [ -z "$h" ]; then if [ -z "$_domain" ]; then
return 1 return 1
fi fi
@ -119,11 +119,9 @@ _get_root() {
if printf "%s\n" "$response" | grep '^\[.*\]' >/dev/null; then if printf "%s\n" "$response" | grep '^\[.*\]' >/dev/null; then
if _contains "$response" "\"domain\":\"$_domain\""; then if _contains "$response" "\"domain\":\"$_domain\""; then
_sub_domain="$(echo "$fulldomain" | sed "s/\\.$_domain\$//")" _sub_domain="$(echo "$fulldomain" | sed "s/\\.$_domain\$//")"
_domain=$_domain
return 0 return 0
else else
_err 'Invalid domain' _debug "Go to next level of $_domain"
return 1
fi fi
else else
_err "$response" _err "$response"

View File

@ -6,6 +6,9 @@
# Values to export: # Values to export:
# export PDD_Token="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" # export PDD_Token="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
# Sometimes cloudflare / google doesn't pick new dns records fast enough.
# You can add --dnssleep XX to params as workaround.
######## Public functions ##################### ######## Public functions #####################
#Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" #Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
@ -13,97 +16,106 @@ dns_yandex_add() {
fulldomain="${1}" fulldomain="${1}"
txtvalue="${2}" txtvalue="${2}"
_debug "Calling: dns_yandex_add() '${fulldomain}' '${txtvalue}'" _debug "Calling: dns_yandex_add() '${fulldomain}' '${txtvalue}'"
_PDD_credentials || return 1
export _H1="PddToken: $PDD_Token"
_PDD_get_domain "$fulldomain" || return 1 _PDD_credentials || return 1
_debug "Found suitable domain in pdd: $curDomain"
curData="domain=${curDomain}&type=TXT&subdomain=${curSubdomain}&ttl=360&content=${txtvalue}" _PDD_get_domain || return 1
curUri="https://pddimp.yandex.ru/api2/admin/dns/add" _debug "Found suitable domain: $domain"
curResult="$(_post "${curData}" "${curUri}")"
_debug "Result: $curResult" _PDD_get_record_ids || return 1
_debug "Record_ids: $record_ids"
if [ ! -z "$record_ids" ]; then
_info "All existing $subdomain records from $domain will be removed at the very end."
fi
data="domain=${domain}&type=TXT&subdomain=${subdomain}&ttl=300&content=${txtvalue}"
uri="https://pddimp.yandex.ru/api2/admin/dns/add"
result="$(_post "${data}" "${uri}" | _normalizeJson)"
_debug "Result: $result"
if ! _contains "$result" '"success":"ok"'; then
if _contains "$result" '"success":"error"' && _contains "$result" '"error":"record_exists"'; then
_info "Record already exists."
else
_err "Can't add $subdomain to $domain."
return 1
fi
fi
} }
#Usage: dns_myapi_rm _acme-challenge.www.domain.com #Usage: dns_myapi_rm _acme-challenge.www.domain.com
dns_yandex_rm() { dns_yandex_rm() {
fulldomain="${1}" fulldomain="${1}"
_debug "Calling: dns_yandex_rm() '${fulldomain}'" _debug "Calling: dns_yandex_rm() '${fulldomain}'"
_PDD_credentials || return 1 _PDD_credentials || return 1
export _H1="PddToken: $PDD_Token"
_PDD_get_domain "$fulldomain" || return 1 _PDD_get_domain "$fulldomain" || return 1
_debug "Found suitable domain in pdd: $curDomain" _debug "Found suitable domain: $domain"
record_id=$(pdd_get_record_id "${fulldomain}") _PDD_get_record_ids "${domain}" "${subdomain}" || return 1
_debug "Result: $record_id" _debug "Record_ids: $record_ids"
for rec_i in $record_id; do for record_id in $record_ids; do
curUri="https://pddimp.yandex.ru/api2/admin/dns/del" data="domain=${domain}&record_id=${record_id}"
curData="domain=${curDomain}&record_id=${rec_i}" uri="https://pddimp.yandex.ru/api2/admin/dns/del"
curResult="$(_post "${curData}" "${curUri}")" result="$(_post "${data}" "${uri}" | _normalizeJson)"
_debug "Result: $curResult" _debug "Result: $result"
if ! _contains "$result" '"success":"ok"'; then
_info "Can't remove $subdomain from $domain."
fi
done done
} }
#################### Private functions below ################################## #################### Private functions below ##################################
_PDD_get_domain() { _PDD_get_domain() {
fulldomain="${1}" subdomain_start=1
__page=1 while true; do
__last=0 domain_start=$(_math $subdomain_start + 1)
while [ $__last -eq 0 ]; do domain=$(echo "$fulldomain" | cut -d . -f "$domain_start"-)
uri1="https://pddimp.yandex.ru/api2/admin/domain/domains?page=${__page}&on_page=20" subdomain=$(echo "$fulldomain" | cut -d . -f -"$subdomain_start")
res1="$(_get "$uri1" | _normalizeJson)"
_debug2 "res1" "$res1" _debug "Checking domain $domain"
__found="$(echo "$res1" | sed -n -e 's#.* "found": \([^,]*\),.*#\1#p')" if [ -z "$domain" ]; then
_debug "found: $__found results on page" return 1
if [ "0$__found" -lt 20 ]; then
_debug "last page: $__page"
__last=1
fi fi
__all_domains="$__all_domains $(echo "$res1" | tr "," "\n" | grep '"name"' | cut -d: -f2 | sed -e 's@"@@g')" uri="https://pddimp.yandex.ru/api2/admin/dns/list?domain=$domain"
result="$(_get "${uri}" | _normalizeJson)"
_debug "Result: $result"
__page=$(_math $__page + 1) if _contains "$result" '"success":"ok"'; then
return 0
fi
subdomain_start=$(_math $subdomain_start + 1)
done done
k=2
while [ $k -lt 10 ]; do
__t=$(echo "$fulldomain" | cut -d . -f $k-100)
_debug "finding zone for domain $__t"
for d in $__all_domains; do
if [ "$d" = "$__t" ]; then
p=$(_math $k - 1)
curSubdomain="$(echo "$fulldomain" | cut -d . -f "1-$p")"
curDomain="$__t"
return 0
fi
done
k=$(_math $k + 1)
done
_err "No suitable domain found in your account"
return 1
} }
_PDD_credentials() { _PDD_credentials() {
if [ -z "${PDD_Token}" ]; then if [ -z "${PDD_Token}" ]; then
PDD_Token="" PDD_Token=""
_err "You need to export PDD_Token=xxxxxxxxxxxxxxxxx" _err "You need to export PDD_Token=xxxxxxxxxxxxxxxxx."
_err "You can get it at https://pddimp.yandex.ru/api2/admin/get_token" _err "You can get it at https://pddimp.yandex.ru/api2/admin/get_token."
return 1 return 1
else else
_saveaccountconf PDD_Token "${PDD_Token}" _saveaccountconf PDD_Token "${PDD_Token}"
fi fi
export _H1="PddToken: $PDD_Token"
} }
pdd_get_record_id() { _PDD_get_record_ids() {
fulldomain="${1}" _debug "Check existing records for $subdomain"
_PDD_get_domain "$fulldomain" uri="https://pddimp.yandex.ru/api2/admin/dns/list?domain=${domain}"
_debug "Found suitable domain in pdd: $curDomain" result="$(_get "${uri}" | _normalizeJson)"
_debug "Result: $result"
curUri="https://pddimp.yandex.ru/api2/admin/dns/list?domain=${curDomain}" if ! _contains "$result" '"success":"ok"'; then
curResult="$(_get "${curUri}" | _normalizeJson)" return 1
_debug "Result: $curResult" fi
echo "$curResult" | _egrep_o "{[^{]*\"content\":[^{]*\"subdomain\":\"${curSubdomain}\"" | sed -n -e 's#.* "record_id": \(.*\),[^,]*#\1#p'
record_ids=$(echo "$result" | _egrep_o "{[^{]*\"subdomain\":\"${subdomain}\"[^}]*}" | sed -n -e 's#.*"record_id": \([0-9]*\).*#\1#p')
} }

View File

@ -136,10 +136,10 @@ _get_root() {
if [ -z "$h" ]; then if [ -z "$h" ]; then
return 1 return 1
fi fi
if ! _zone_rest GET "dns/$h/a"; then if ! _zone_rest GET "dns/$h"; then
return 1 return 1
fi fi
if _contains "$response" "\"name\":\"$h\"" >/dev/null; then if _contains "$response" "\"identificator\":\"$h\"" >/dev/null; then
_domain=$h _domain=$h
return 0 return 0
fi fi

64
notify/cqhttp.sh Normal file
View File

@ -0,0 +1,64 @@
#!/usr/bin/env sh
#Support for CQHTTP api. Push notification on CoolQ
#CQHTTP_TOKEN="" Recommended to be not empty, QQ application token
#CQHTTP_USER="" Required, QQ receiver ID
#CQHTTP_APIROOT="" Required, CQHTTP Server URL (without slash suffix)
#CQHTTP_CUSTOM_MSGHEAD="" Optional, custom message header
CQHTTP_APIPATH="/send_private_msg"
cqhttp_send() {
_subject="$1"
_content="$2"
_statusCode="$3" #0: success, 1: error 2($RENEW_SKIP): skipped
_debug "_statusCode" "$_statusCode"
CQHTTP_TOKEN="${CQHTTP_TOKEN:-$(_readaccountconf_mutable CQHTTP_TOKEN)}"
if [ -z "$CQHTTP_TOKEN" ]; then
CQHTTP_TOKEN=""
_info "You didn't specify a CQHTTP application token yet, which is unsafe. Assuming it to be empty."
else
_saveaccountconf_mutable CQHTTP_TOKEN "$CQHTTP_TOKEN"
fi
CQHTTP_USER="${CQHTTP_USER:-$(_readaccountconf_mutable CQHTTP_USER)}"
if [ -z "$CQHTTP_USER" ]; then
CQHTTP_USER=""
_err "You didn't specify a QQ user yet."
return 1
fi
_saveaccountconf_mutable CQHTTP_USER "$CQHTTP_USER"
CQHTTP_APIROOT="${CQHTTP_APIROOT:-$(_readaccountconf_mutable CQHTTP_APIROOT)}"
if [ -z "$CQHTTP_APIROOT" ]; then
CQHTTP_APIROOT=""
_err "You didn't specify the API root yet."
return 1
fi
_saveaccountconf_mutable CQHTTP_APIROOT "$CQHTTP_APIROOT"
CQHTTP_CUSTOM_MSGHEAD="${CQHTTP_CUSTOM_MSGHEAD:-$(_readaccountconf_mutable CQHTTP_CUSTOM_MSGHEAD)}"
if [ -z "$CQHTTP_CUSTOM_MSGHEAD" ]; then
CQHTTP_CUSTOM_MSGHEAD="A message from acme.sh:"
else
_saveaccountconf_mutable CQHTTP_CUSTOM_MSGHEAD "$CQHTTP_CUSTOM_MSGHEAD"
fi
_access_token="$(printf "%s" "$CQHTTP_TOKEN" | _url_encode)"
_user_id="$(printf "%s" "$CQHTTP_USER" | _url_encode)"
_message="$(printf "$CQHTTP_CUSTOM_MSGHEAD %s\\n%s" "$_subject" "$_content" | _url_encode)"
_finalUrl="$CQHTTP_APIROOT$CQHTTP_APIPATH?access_token=$_access_token&user_id=$_user_id&message=$_message"
response="$(_get "$_finalUrl")"
if [ "$?" = "0" ] && _contains "$response" "\"retcode\":0,\"status\":\"ok\""; then
_info "QQ send success."
return 0
fi
_err "QQ send error."
_debug "URL" "$_finalUrl"
_debug "Response" "$response"
return 1
}

68
notify/dingtalk.sh Normal file
View File

@ -0,0 +1,68 @@
#!/usr/bin/env sh
#Support dingtalk webhooks api
#DINGTALK_WEBHOOK="xxxx"
#optional
#DINGTALK_KEYWORD="yyyy"
#DINGTALK_SIGNING_KEY="SEC08ffdbd403cbc3fc8a65xxxxxxxxxxxxxxxxxxxx"
# subject content statusCode
dingtalk_send() {
_subject="$1"
_content="$2"
_statusCode="$3" #0: success, 1: error 2($RENEW_SKIP): skipped
_debug "_subject" "$_subject"
_debug "_content" "$_content"
_debug "_statusCode" "$_statusCode"
DINGTALK_WEBHOOK="${DINGTALK_WEBHOOK:-$(_readaccountconf_mutable DINGTALK_WEBHOOK)}"
if [ -z "$DINGTALK_WEBHOOK" ]; then
DINGTALK_WEBHOOK=""
_err "You didn't specify a dingtalk webhooks DINGTALK_WEBHOOK yet."
_err "You can get yours from https://dingtalk.com"
return 1
fi
_saveaccountconf_mutable DINGTALK_WEBHOOK "$DINGTALK_WEBHOOK"
DINGTALK_KEYWORD="${DINGTALK_KEYWORD:-$(_readaccountconf_mutable DINGTALK_KEYWORD)}"
if [ "$DINGTALK_KEYWORD" ]; then
_saveaccountconf_mutable DINGTALK_KEYWORD "$DINGTALK_KEYWORD"
fi
# DINGTALK_SIGNING_KEY="${DINGTALK_SIGNING_KEY:-$(_readaccountconf_mutable DINGTALK_SIGNING_KEY)}"
# if [ -z "$DINGTALK_SIGNING_KEY" ]; then
# DINGTALK_SIGNING_KEY="value1"
# _info "The DINGTALK_SIGNING_KEY is not set, so use the default value1 as key."
# elif ! _hasfield "$_IFTTT_AVAIL_MSG_KEYS" "$DINGTALK_SIGNING_KEY"; then
# _err "The DINGTALK_SIGNING_KEY \"$DINGTALK_SIGNING_KEY\" is not available, should be one of $_IFTTT_AVAIL_MSG_KEYS"
# DINGTALK_SIGNING_KEY=""
# return 1
# else
# _saveaccountconf_mutable DINGTALK_SIGNING_KEY "$DINGTALK_SIGNING_KEY"
# fi
# if [ "$DINGTALK_SIGNING_KEY" = "$IFTTT_CONTENT_KEY" ]; then
# DINGTALK_SIGNING_KEY=""
# IFTTT_CONTENT_KEY=""
# _err "The DINGTALK_SIGNING_KEY must not be same as IFTTT_CONTENT_KEY."
# return 1
# fi
_content=$(echo "$_content" | _json_encode)
_subject=$(echo "$_subject" | _json_encode)
_data="{\"msgtype\": \"text\", \"text\": {\"content\": \"[$DINGTALK_KEYWORD]\n$_subject\n$_content\"}}"
response="$(_post "$_data" "$DINGTALK_WEBHOOK" "" "POST" "application/json")"
if [ "$?" = "0" ] && _contains "$response" "errmsg\":\"ok"; then
_info "dingtalk webhooks event fired success."
return 0
fi
_err "dingtalk webhooks event fired error."
_err "$response"
return 1
}

86
notify/ifttt.sh Normal file
View File

@ -0,0 +1,86 @@
#!/usr/bin/env sh
#Support ifttt.com webhooks api
#IFTTT_API_KEY="xxxx"
#IFTTT_EVENT_NAME="yyyy"
#IFTTT_SUBJECT_KEY="value1|value2|value3" #optional, use "value1" as default
#IFTTT_CONTENT_KEY="value1|value2|value3" #optional, use "value2" as default
_IFTTT_AVAIL_MSG_KEYS="value1,value2,value3"
# subject content statusCode
ifttt_send() {
_subject="$1"
_content="$2"
_statusCode="$3" #0: success, 1: error 2($RENEW_SKIP): skipped
_debug "_subject" "$_subject"
_debug "_content" "$_content"
_debug "_statusCode" "$_statusCode"
IFTTT_API_KEY="${IFTTT_API_KEY:-$(_readaccountconf_mutable IFTTT_API_KEY)}"
if [ -z "$IFTTT_API_KEY" ]; then
IFTTT_API_KEY=""
_err "You didn't specify a ifttt webhooks api key IFTTT_API_KEY yet."
_err "You can get yours from https://ifttt.com"
return 1
fi
_saveaccountconf_mutable IFTTT_API_KEY "$IFTTT_API_KEY"
IFTTT_EVENT_NAME="${IFTTT_EVENT_NAME:-$(_readaccountconf_mutable IFTTT_EVENT_NAME)}"
if [ -z "$IFTTT_EVENT_NAME" ]; then
IFTTT_EVENT_NAME=""
_err "You didn't specify a ifttt webhooks event name IFTTT_EVENT_NAME yet."
return 1
fi
_saveaccountconf_mutable IFTTT_EVENT_NAME "$IFTTT_EVENT_NAME"
IFTTT_SUBJECT_KEY="${IFTTT_SUBJECT_KEY:-$(_readaccountconf_mutable IFTTT_SUBJECT_KEY)}"
if [ -z "$IFTTT_SUBJECT_KEY" ]; then
IFTTT_SUBJECT_KEY="value1"
_info "The IFTTT_SUBJECT_KEY is not set, so use the default value1 as key."
elif ! _hasfield "$_IFTTT_AVAIL_MSG_KEYS" "$IFTTT_SUBJECT_KEY"; then
_err "The IFTTT_SUBJECT_KEY \"$IFTTT_SUBJECT_KEY\" is not available, should be one of $_IFTTT_AVAIL_MSG_KEYS"
IFTTT_SUBJECT_KEY=""
return 1
else
_saveaccountconf_mutable IFTTT_SUBJECT_KEY "$IFTTT_SUBJECT_KEY"
fi
IFTTT_CONTENT_KEY="${IFTTT_CONTENT_KEY:-$(_readaccountconf_mutable IFTTT_CONTENT_KEY)}"
if [ -z "$IFTTT_CONTENT_KEY" ]; then
IFTTT_CONTENT_KEY="value2"
_info "The IFTTT_CONTENT_KEY is not set, so use the default value2 as key."
elif ! _hasfield "$_IFTTT_AVAIL_MSG_KEYS" "$IFTTT_CONTENT_KEY"; then
_err "The IFTTT_CONTENT_KEY \"$IFTTT_CONTENT_KEY\" is not available, should be one of $_IFTTT_AVAIL_MSG_KEYS"
IFTTT_CONTENT_KEY=""
return 1
else
_saveaccountconf_mutable IFTTT_CONTENT_KEY "$IFTTT_CONTENT_KEY"
fi
if [ "$IFTTT_SUBJECT_KEY" = "$IFTTT_CONTENT_KEY" ]; then
IFTTT_SUBJECT_KEY=""
IFTTT_CONTENT_KEY=""
_err "The IFTTT_SUBJECT_KEY must not be same as IFTTT_CONTENT_KEY."
return 1
fi
IFTTT_API_URL="https://maker.ifttt.com/trigger/$IFTTT_EVENT_NAME/with/key/$IFTTT_API_KEY"
_content=$(echo "$_content" | _json_encode)
_subject=$(echo "$_subject" | _json_encode)
_data="{\"$IFTTT_SUBJECT_KEY\": \"$_subject\", \"$IFTTT_CONTENT_KEY\": \"$_content\"}"
response="$(_post "$_data" "$IFTTT_API_URL" "" "POST" "application/json")"
if [ "$?" = "0" ] && _contains "$response" "Congratulations"; then
_info "IFTTT webhooks event fired success."
return 0
fi
_err "IFTTT webhooks event fired error."
_err "$response"
return 1
}

View File

@ -7,7 +7,7 @@
#MAILGUN_REGION="us|eu" #optional, use "us" as default #MAILGUN_REGION="us|eu" #optional, use "us" as default
#MAILGUN_API_DOMAIN="xxxxxx.com" #optional, use the default sandbox domain #MAILGUN_API_DOMAIN="xxxxxx.com" #optional, use the default sandbox domain
#MAILGUN_FROM="xxx@xxxxx.com" #optional, use the default sendbox account #MAILGUN_FROM="xxx@xxxxx.com" #optional, use the default sandbox account
_MAILGUN_BASE_US="https://api.mailgun.net/v3" _MAILGUN_BASE_US="https://api.mailgun.net/v3"
_MAILGUN_BASE_EU="https://api.eu.mailgun.net/v3" _MAILGUN_BASE_EU="https://api.eu.mailgun.net/v3"

View File

@ -50,13 +50,13 @@ pushover_send() {
_subject="$(printf "*%s*\n" "$_subject" | _json_encode)" _subject="$(printf "*%s*\n" "$_subject" | _json_encode)"
_data="{\"token\": \"$PUSHOVER_TOKEN\",\"user\": \"$PUSHOVER_USER\",\"title\": \"$_subject\",\"message\": \"$_content\",\"sound\": \"$PUSHOVER_SOUND\", \"device\": \"$PUSHOVER_DEVICE\", \"priority\": \"$PUSHOVER_PRIORITY\"}" _data="{\"token\": \"$PUSHOVER_TOKEN\",\"user\": \"$PUSHOVER_USER\",\"title\": \"$_subject\",\"message\": \"$_content\",\"sound\": \"$PUSHOVER_SOUND\", \"device\": \"$PUSHOVER_DEVICE\", \"priority\": \"$PUSHOVER_PRIORITY\"}"
response="" #just make shellcheck happy response="$(_post "$_data" "$PUSHOVER_URI")"
if _post "$_data" "$PUSHOVER_URI"; then
if _contains "$response" "{\"status\":1"; then if [ "$?" = "0" ] && _contains "$response" "{\"status\":1"; then
_info "PUSHOVER send success." _info "PUSHOVER send success."
return 0 return 0
fi
fi fi
_err "PUSHOVER send error." _err "PUSHOVER send error."
_err "$response" _err "$response"
return 1 return 1

View File

@ -42,13 +42,13 @@ sendgrid_send() {
_content="$(echo "$_content" | _json_encode)" _content="$(echo "$_content" | _json_encode)"
_data="{\"personalizations\": [{\"to\": [{\"email\": \"$SENDGRID_TO\"}]}],\"from\": {\"email\": \"$SENDGRID_FROM\"},\"subject\": \"$_subject\",\"content\": [{\"type\": \"text/plain\", \"value\": \"$_content\"}]}" _data="{\"personalizations\": [{\"to\": [{\"email\": \"$SENDGRID_TO\"}]}],\"from\": {\"email\": \"$SENDGRID_FROM\"},\"subject\": \"$_subject\",\"content\": [{\"type\": \"text/plain\", \"value\": \"$_content\"}]}"
response="" #just make shellcheck happy response="$(_post "$_data" "https://api.sendgrid.com/v3/mail/send")"
if _post "$_data" "https://api.sendgrid.com/v3/mail/send"; then
if [ -z "$response" ]; then if [ "$?" = "0" ] && [ -z "$response" ]; then
_info "sendgrid send sccess." _info "sendgrid send sccess."
return 0 return 0
fi
fi fi
_err "sendgrid send error." _err "sendgrid send error."
_err "$response" _err "$response"
return 1 return 1

90
notify/xmpp.sh Normal file
View File

@ -0,0 +1,90 @@
#!/usr/bin/env sh
#Support xmpp via sendxmpp
#XMPP_BIN="/usr/bin/sendxmpp"
#XMPP_BIN_ARGS="-n -t --tls-ca-path=/etc/ssl/certs"
#XMPP_TO="zzzz@example.com"
xmpp_send() {
_subject="$1"
_content="$2"
_statusCode="$3" #0: success, 1: error 2($RENEW_SKIP): skipped
_debug "_subject" "$_subject"
_debug "_content" "$_content"
_debug "_statusCode" "$_statusCode"
XMPP_BIN="${XMPP_BIN:-$(_readaccountconf_mutable XMPP_BIN)}"
if [ -n "$XMPP_BIN" ] && ! _exists "$XMPP_BIN"; then
_err "It seems that the command $XMPP_BIN is not in path."
return 1
fi
_XMPP_BIN=$(_xmpp_bin)
if [ -n "$XMPP_BIN" ]; then
_saveaccountconf_mutable XMPP_BIN "$XMPP_BIN"
else
_clearaccountconf "XMPP_BIN"
fi
XMPP_BIN_ARGS="${XMPP_BIN_ARGS:-$(_readaccountconf_mutable XMPP_BIN_ARGS)}"
if [ -n "$XMPP_BIN_ARGS" ]; then
_saveaccountconf_mutable XMPP_BIN_ARGS "$XMPP_BIN_ARGS"
else
_clearaccountconf "XMPP_BIN_ARGS"
fi
XMPP_TO="${XMPP_TO:-$(_readaccountconf_mutable XMPP_TO)}"
if [ -n "$XMPP_TO" ]; then
if ! _xmpp_valid "$XMPP_TO"; then
_err "It seems that the XMPP_TO=$XMPP_TO is not a valid xmpp address."
return 1
fi
_saveaccountconf_mutable XMPP_TO "$XMPP_TO"
fi
result=$({ _xmpp_message | eval "$(_xmpp_cmnd)"; } 2>&1)
# shellcheck disable=SC2181
if [ $? -ne 0 ]; then
_debug "xmpp send error."
_err "$result"
return 1
fi
_debug "xmpp send success."
return 0
}
_xmpp_bin() {
if [ -n "$XMPP_BIN" ]; then
_XMPP_BIN="$XMPP_BIN"
elif _exists "sendxmpp"; then
_XMPP_BIN="sendxmpp"
else
_err "Please install sendxmpp first."
return 1
fi
echo "$_XMPP_BIN"
}
_xmpp_cmnd() {
case $(basename "$_XMPP_BIN") in
sendxmpp)
echo "'$_XMPP_BIN' '$XMPP_TO' $XMPP_BIN_ARGS"
;;
*)
_err "Command $XMPP_BIN is not supported, use sendxmpp."
return 1
;;
esac
}
_xmpp_message() {
echo "$_subject"
}
_xmpp_valid() {
_contains "$1" "@"
}