Unverified Commit 6e2669ed authored by sjau's avatar sjau Committed by GitHub
Browse files

Merge pull request #1 from Neilpang/master

Update from original
parents 5eed02f7 7b8a82ce
<!-- <!--
请确保已经更新到最新的代码, 然后贴上来 `--debug 2` 的调试输出. 没有调试输出,我帮不了你.
如何调试 https://github.com/Neilpang/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.
...@@ -8,13 +10,11 @@ You can install the latest version by: `acme.sh --upgrade` ...@@ -8,13 +10,11 @@ You can install the latest version by: `acme.sh --upgrade`
- 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/Neilpang/acme.sh/wiki/How-to-debug-acme.sh).
--> -->
Steps to reproduce Steps to reproduce
------------------ ------------------
Debug log Debug log
----------------- -----------------
......
<!-- <!--
Do NOT send pull request to `master` branch. Do NOT send pull request to `master` branch.
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.
--> -->
\ No newline at end of file
language: shell language: shell
sudo: required sudo: required
dist: trusty
os:
- linux os:
- osx - linux
- osx
env:
global: services:
- SHFMT_URL=https://github.com/mvdan/sh/releases/download/v0.4.0/shfmt_v0.4.0_linux_amd64 - docker
addons: env:
apt: global:
sources: - SHFMT_URL=https://github.com/mvdan/sh/releases/download/v0.4.0/shfmt_v0.4.0_linux_amd64
- debian-sid # Grab shellcheck from the Debian repo (o_O)
packages: addons:
- shellcheck apt:
sources:
install: - debian-sid # Grab shellcheck from the Debian repo (o_O)
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then packages:
brew update && brew install openssl; - shellcheck
brew info openssl;
ln -s /usr/local/opt/openssl/lib/libcrypto.1.0.0.dylib /usr/local/lib/; install:
ln -s /usr/local/opt/openssl/lib/libssl.1.0.0.dylib /usr/local/lib/; - if [ "$TRAVIS_OS_NAME" = 'osx' ]; then
ln -s /usr/local/Cellar/openssl/1.0.2j/bin/openssl /usr/local/openssl; brew update && brew install socat;
_old_path="$PATH"; export PATH="/usr/local/opt/openssl@1.1/bin:$PATH" ;
echo "PATH=$PATH"; fi
export PATH="";
export OPENSSL_BIN="/usr/local/openssl"; script:
openssl version 2>&1 || true; - echo "NGROK_TOKEN=$(echo "$NGROK_TOKEN" | wc -c)"
$OPENSSL_BIN version 2>&1 || true; - command -V openssl && openssl version
export PATH="$_old_path"; - if [ "$TRAVIS_OS_NAME" = "linux" ]; then curl -sSL $SHFMT_URL -o ~/shfmt ; fi
fi - if [ "$TRAVIS_OS_NAME" = "linux" ]; then chmod +x ~/shfmt ; fi
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then ~/shfmt -l -w -i 2 . ; fi
script: - if [ "$TRAVIS_OS_NAME" = "linux" ]; then git diff --exit-code && echo "shfmt OK" ; fi
- echo "TEST_LOCAL=$TEST_LOCAL" - if [ "$TRAVIS_OS_NAME" = "linux" ]; then shellcheck -V ; fi
- echo "NGROK_TOKEN=$(echo "$NGROK_TOKEN" | wc -c)" - if [ "$TRAVIS_OS_NAME" = "linux" ]; then shellcheck -e SC2181 **/*.sh && echo "shellcheck OK" ; fi
- which openssl && openssl version - cd ..
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then curl -sSL $SHFMT_URL -o ~/shfmt ; fi - git clone https://github.com/Neilpang/acmetest.git && cp -r acme.sh acmetest/ && cd acmetest
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then chmod +x ~/shfmt ; 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" == "linux" ]]; then ~/shfmt -l -w -i 2 . ; 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" == "linux" ]]; then git diff --exit-code && echo "shfmt OK" ; fi
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then shellcheck -V ; fi
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then shellcheck -e SC2021,SC2126,SC2034 **/*.sh && echo "shellcheck OK" ; fi matrix:
- cd .. fast_finish: true
- git clone https://github.com/Neilpang/acmetest.git && cp -r acme.sh acmetest/ && cd acmetest
- if [[ "$TRAVIS_OS_NAME" == "linux" ]] && [[ "$NGROK_TOKEN" ]]; then sudo NGROK_TOKEN="$NGROK_TOKEN" ./letest.sh ; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]] && [[ "$NGROK_TOKEN" ]]; then sudo NGROK_TOKEN="$NGROK_TOKEN" OPENSSL_BIN="$OPENSSL_BIN" ./letest.sh ; fi
matrix:
fast_finish: true
FROM alpine:3.6
RUN apk update -f \
&& apk --no-cache add -f \
openssl \
curl \
socat \
&& rm -rf /var/cache/apk/*
ENV LE_CONFIG_HOME /acme.sh
ENV AUTO_UPGRADE 1
#Install
ADD ./ /install_acme.sh/
RUN cd /install_acme.sh && ([ -f /install_acme.sh/acme.sh ] && /install_acme.sh/acme.sh --install || curl https://get.acme.sh | sh) && rm -rf /install_acme.sh/
RUN ln -s /root/.acme.sh/acme.sh /usr/local/bin/acme.sh && crontab -l | grep acme.sh | sed 's#> /dev/null##' | crontab -
RUN for verb in help \
version \
install \
uninstall \
upgrade \
issue \
signcsr \
deploy \
install-cert \
renew \
renew-all \
revoke \
remove \
list \
showcsr \
install-cronjob \
uninstall-cronjob \
cron \
toPkcs \
toPkcs8 \
update-account \
register-account \
create-account-key \
create-domain-key \
createCSR \
deactivate \
deactivate-account \
; do \
printf -- "%b" "#!/usr/bin/env sh\n/root/.acme.sh/acme.sh --${verb} --config-home /acme.sh \"\$@\"" >/usr/local/bin/--${verb} && chmod +x /usr/local/bin/--${verb} \
; done
RUN printf "%b" '#!'"/usr/bin/env sh\n \
if [ \"\$1\" = \"daemon\" ]; then \n \
trap \"echo stop && killall crond && exit 0\" SIGTERM SIGINT \n \
crond && while true; do sleep 1; done;\n \
else \n \
exec -- \"\$@\"\n \
fi" >/entry.sh && chmod +x /entry.sh
VOLUME /acme.sh
ENTRYPOINT ["/entry.sh"]
CMD ["--help"]
# 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/Neilpang/acme.sh.svg?branch=master)](https://travis-ci.org/Neilpang/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)
- 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.
- Simple, powerful and very easy to use. You only need 3 minutes to learn it. - Simple, powerful and very easy to use. You only need 3 minutes to learn it.
...@@ -7,14 +9,33 @@ ...@@ -7,14 +9,33 @@
- Purely written in Shell with no dependencies on python or the official Let's Encrypt client. - Purely written in Shell with no dependencies on python or the official Let's Encrypt client.
- Just one script to issue, renew and install your certificates automatically. - Just one script to issue, renew and install your certificates automatically.
- DOES NOT require `root/sudoer` access. - DOES NOT require `root/sudoer` access.
- Docker friendly
- IPv6 support
It's probably the `easiest&smallest&smartest` shell script to automatically issue & renew the free certificates from Let's Encrypt. It's probably the `easiest & smartest` shell script to automatically issue & renew the free certificates from Let's Encrypt.
Wiki: https://github.com/Neilpang/acme.sh/wiki Wiki: https://github.com/Neilpang/acme.sh/wiki
For Docker Fans: [acme.sh :two_hearts: Docker ](https://github.com/Neilpang/acme.sh/wiki/Run-acme.sh-in-docker)
Twitter: [@neilpangxa](https://twitter.com/neilpangxa)
# [中文说明](https://github.com/Neilpang/acme.sh/wiki/%E8%AF%B4%E6%98%8E) # [中文说明](https://github.com/Neilpang/acme.sh/wiki/%E8%AF%B4%E6%98%8E)
# Who are using **acme.sh**
- [FreeBSD.org](https://blog.crashed.org/letsencrypt-in-freebsd-org/)
- [ruby-china.org](https://ruby-china.org/topics/31983)
- [Proxmox](https://pve.proxmox.com/wiki/HTTPS_Certificate_Configuration_(Version_4.x_and_newer))
- [pfsense](https://github.com/pfsense/FreeBSD-ports/pull/89)
- [webfaction](https://community.webfaction.com/questions/19988/using-letsencrypt)
- [Loadbalancer.org](https://www.loadbalancer.org/blog/loadbalancer-org-with-lets-encrypt-quick-and-dirty)
- [discourse.org](https://meta.discourse.org/t/setting-up-lets-encrypt/40709)
- [Centminmod](http://centminmod.com/letsencrypt-acmetool-https.html)
- [splynx](https://forum.splynx.com/t/free-ssl-cert-for-splynx-lets-encrypt/297)
- [archlinux](https://aur.archlinux.org/packages/acme.sh-git/)
- [opnsense.org](https://github.com/opnsense/plugins/tree/master/security/acme-client/src/opnsense/scripts/OPNsense/AcmeClient)
- [more...](https://github.com/Neilpang/acme.sh/wiki/Blogs-and-tutorials)
# Tested OS # Tested OS
...@@ -39,8 +60,9 @@ Wiki: https://github.com/Neilpang/acme.sh/wiki ...@@ -39,8 +60,9 @@ Wiki: https://github.com/Neilpang/acme.sh/wiki
|17|-----| OpenWRT: Tested and working. See [wiki page](https://github.com/Neilpang/acme.sh/wiki/How-to-run-on-OpenWRT) |17|-----| OpenWRT: Tested and working. See [wiki page](https://github.com/Neilpang/acme.sh/wiki/How-to-run-on-OpenWRT)
|18|[![](https://cdn.rawgit.com/Neilpang/acmetest/master/status/solaris.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|SunOS/Solaris |18|[![](https://cdn.rawgit.com/Neilpang/acmetest/master/status/solaris.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|SunOS/Solaris
|19|[![](https://cdn.rawgit.com/Neilpang/acmetest/master/status/gentoo-stage3-amd64.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|Gentoo Linux |19|[![](https://cdn.rawgit.com/Neilpang/acmetest/master/status/gentoo-stage3-amd64.svg)](https://github.com/Neilpang/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
For all build statuses, check our [daily build project](https://github.com/Neilpang/acmetest): For all build statuses, check our [weekly build project](https://github.com/Neilpang/acmetest):
https://github.com/Neilpang/acmetest https://github.com/Neilpang/acmetest
...@@ -50,7 +72,9 @@ https://github.com/Neilpang/acmetest ...@@ -50,7 +72,9 @@ https://github.com/Neilpang/acmetest
- Webroot mode - Webroot mode
- Standalone mode - Standalone mode
- Apache mode - Apache mode
- Nginx mode ( Beta )
- DNS mode - DNS mode
- [Stateless mode](https://github.com/Neilpang/acme.sh/wiki/Stateless-Mode)
# 1. How to install # 1. How to install
...@@ -115,13 +139,25 @@ root@v1:~# acme.sh -h ...@@ -115,13 +139,25 @@ root@v1:~# acme.sh -h
acme.sh --issue -d example.com -w /home/wwwroot/example.com acme.sh --issue -d example.com -w /home/wwwroot/example.com
``` ```
or:
```bash
acme.sh --issue -d example.com -w /home/username/public_html
```
or:
```bash
acme.sh --issue -d example.com -w /var/www/html
```
**Example 2:** Multiple domains in the same cert. **Example 2:** Multiple domains in the same cert.
```bash ```bash
acme.sh --issue -d example.com -d www.example.com -d cp.example.com -w /home/wwwroot/example.com acme.sh --issue -d example.com -d www.example.com -d cp.example.com -w /home/wwwroot/example.com
``` ```
The parameter `/home/wwwroot/example.com` is the web root folder. You **MUST** have `write access` to this folder. The parameter `/home/wwwroot/example.com` or `/home/username/public_html` or `/var/www/html` is the web root folder where you host your website files. You **MUST** have `write access` to this folder.
Second argument **"example.com"** is the main domain you want to issue the cert for. Second argument **"example.com"** is the main domain you want to issue the cert for.
You must have at least one domain there. You must have at least one domain there.
...@@ -142,26 +178,28 @@ You **MUST** use this command to copy the certs to the target files, **DO NOT** ...@@ -142,26 +178,28 @@ You **MUST** use this command to copy the certs to the target files, **DO NOT**
**Apache** example: **Apache** example:
```bash ```bash
acme.sh --installcert -d example.com \ acme.sh --install-cert -d example.com \
--certpath /path/to/certfile/in/apache/cert.pem \ --cert-file /path/to/certfile/in/apache/cert.pem \
--keypath /path/to/keyfile/in/apache/key.pem \ --key-file /path/to/keyfile/in/apache/key.pem \
--fullchainpath /path/to/fullchain/certfile/apache/fullchain.pem \ --fullchain-file /path/to/fullchain/certfile/apache/fullchain.pem \
--reloadcmd "service apache2 restart" --reloadcmd "service apache2 force-reload"
``` ```
**Nginx** example: **Nginx** example:
```bash ```bash
acme.sh --installcert -d example.com \ acme.sh --install-cert -d example.com \
--keypath /path/to/keyfile/in/nginx/key.pem \ --key-file /path/to/keyfile/in/nginx/key.pem \
--fullchainpath /path/to/fullchain/nginx/cert.pem \ --fullchain-file /path/to/fullchain/nginx/cert.pem \
--reloadcmd "service nginx restart" --reloadcmd "service nginx force-reload"
``` ```
Only the domain is required, all the other parameters are optional. Only the domain is required, all the other parameters are optional.
The ownership and permission info of existing files are preserved. You may want to precreate the files to have defined ownership and permission.
Install/copy the issued cert/key to the production Apache or Nginx path. Install/copy the issued cert/key to the production Apache or Nginx path.
The cert will be `renewed every **60** days by default` (which is configurable). Once the cert is renewed, the Apache/Nginx service will be restarted automatically by the command: `service apache2 restart` or `service nginx restart`. The cert will be renewed every **60** days by default (which is configurable). Once the cert is renewed, the Apache/Nginx service will be reloaded automatically by the command: `service apache2 force-reload` or `service nginx force-reload`.
# 4. Use Standalone server to issue cert # 4. Use Standalone server to issue cert
...@@ -208,8 +246,27 @@ acme.sh --issue --apache -d example.com -d www.example.com -d cp.example.com ...@@ -208,8 +246,27 @@ acme.sh --issue --apache -d example.com -d www.example.com -d cp.example.com
More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert
# 7. Use Nginx mode
**(requires you to be root/sudoer, since it is required to interact with Nginx server)**
If you are running a web server, Apache or Nginx, it is recommended to use the `Webroot mode`.
Particularly, if you are running an nginx server, you can use nginx mode instead. This mode doesn't write any files to your web root folder.
Just set string "nginx" as the second argument.
It will configure nginx server automatically to verify the domain and then restore the nginx config to the original version.
So, the config is not changed.
# 7. Use DNS mode: ```
acme.sh --issue --nginx -d example.com -d www.example.com -d cp.example.com
```
More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert
# 8. Use DNS mode:
Support the `dns-01` challenge. Support the `dns-01` challenge.
...@@ -239,8 +296,11 @@ acme.sh --renew -d example.com ...@@ -239,8 +296,11 @@ acme.sh --renew -d example.com
Ok, it's finished. Ok, it's finished.
**Take care, this is dns manual mode, it can not be renewed automatically. you will have to add a new txt record to your domain by your hand when you renew your cert.**
# 8. Automatic DNS API integration **Please use dns api mode instead.**
# 9. Automatic DNS API integration
If your DNS provider supports API access, we can use that API to automatically issue the certs. If your DNS provider supports API access, we can use that API to automatically issue the certs.
...@@ -252,17 +312,45 @@ You don't have to do anything manually! ...@@ -252,17 +312,45 @@ You don't have to do anything manually!
1. DNSPod.cn API 1. DNSPod.cn API
1. CloudXNS.com API 1. CloudXNS.com API
1. GoDaddy.com API 1. GoDaddy.com API
1. OVH, kimsufi, soyoustart and runabove API
1. AWS Route 53
1. PowerDNS.com API 1. PowerDNS.com API
1. lexicon DNS API: https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api 1. OVH, kimsufi, soyoustart and runabove API
(DigitalOcean, DNSimple, DNSMadeEasy, DNSPark, EasyDNS, Namesilo, NS1, PointHQ, Rage4 and Vultr etc.) 1. nsupdate API
1. LuaDNS.com API 1. LuaDNS.com API
1. DNSMadeEasy.com API 1. DNSMadeEasy.com API
1. nsupdate API 1. AWS Route 53
1. aliyun.com(阿里云) API 1. aliyun.com(阿里云) API
1. ISPConfig 3.1 API 1. ISPConfig 3.1 API
1. Alwaysdata.com API
1. Linode.com API
1. FreeDNS (https://freedns.afraid.org/)
1. cyon.ch
1. Domain-Offensive/Resellerinterface/Domainrobot API
1. Gandi LiveDNS API
1. Knot DNS API
1. DigitalOcean API (native)
1. ClouDNS.net API
1. Infoblox NIOS API (https://www.infoblox.com/)
1. VSCALE (https://vscale.io/)
1. Dynu API (https://www.dynu.com)
1. DNSimple API
1. NS1.com API
1. DuckDNS.org API
1. Name.com API
1. Dyn Managed DNS API
1. Yandex PDD API (https://pdd.yandex.ru)
1. Hurricane Electric DNS service (https://dns.he.net)
1. UnoEuro API (https://www.unoeuro.com/)
1. INWX (https://www.inwx.de/)
1. Servercow (https://servercow.de)
And:
1. lexicon DNS API: https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api
(DigitalOcean, DNSimple, DNSMadeEasy, DNSPark, EasyDNS, Namesilo, NS1, PointHQ, Rage4 and Vultr etc.)
**More APIs coming soon...** **More APIs coming soon...**
If your DNS provider is not on the supported list above, you can write your own DNS API script easily. If you do, please consider submitting a [Pull Request](https://github.com/Neilpang/acme.sh/pulls) and contribute it to the project. If your DNS provider is not on the supported list above, you can write your own DNS API script easily. If you do, please consider submitting a [Pull Request](https://github.com/Neilpang/acme.sh/pulls) and contribute it to the project.
...@@ -270,7 +358,7 @@ If your DNS provider is not on the supported list above, you can write your own ...@@ -270,7 +358,7 @@ If your DNS provider is not on the supported list above, you can write your own
For more details: [How to use DNS API](dnsapi) For more details: [How to use DNS API](dnsapi)
# 9. Issue ECC certificates # 10. Issue ECC certificates
`Let's Encrypt` can now issue **ECDSA** certificates. `Let's Encrypt` can now issue **ECDSA** certificates.
...@@ -280,7 +368,7 @@ Just set the `length` parameter with a prefix `ec-`. ...@@ -280,7 +368,7 @@ Just set the `length` parameter with a prefix `ec-`.
For example: For example:
### Single domain ECC cerfiticate ### Single domain ECC certificate
```bash ```bash
acme.sh --issue -w /home/wwwroot/example.com -d example.com --keylength ec-256 acme.sh --issue -w /home/wwwroot/example.com -d example.com --keylength ec-256
...@@ -301,7 +389,7 @@ Valid values are: ...@@ -301,7 +389,7 @@ Valid values are:
3. **ec-521 (secp521r1, "ECDSA P-521", which is not supported by Let's Encrypt yet.)** 3. **ec-521 (secp521r1, "ECDSA P-521", which is not supported by Let's Encrypt yet.)**
# 10. How to renew the issued certs # 11. How to renew the issued certs
No, you don't need to renew the certs manually. All the certs will be renewed automatically every **60** days. No, you don't need to renew the certs manually. All the certs will be renewed automatically every **60** days.
...@@ -318,9 +406,9 @@ acme.sh --renew -d example.com --force --ecc ...@@ -318,9 +406,9 @@ acme.sh --renew -d example.com --force --ecc
``` ```
# 11. How to upgrade `acme.sh` # 12. How to upgrade `acme.sh`
acme.sh is in constant developement, so it's strongly recommended to use the latest code. acme.sh is in constant development, so it's strongly recommended to use the latest code.
You can update acme.sh to the latest code: You can update acme.sh to the latest code:
...@@ -343,26 +431,26 @@ acme.sh --upgrade --auto-upgrade 0 ...@@ -343,26 +431,26 @@ acme.sh --upgrade --auto-upgrade 0
``` ```
# 12. Issue a cert from an existing CSR # 13. Issue a cert from an existing CSR
https://github.com/Neilpang/acme.sh/wiki/Issue-a-cert-from-existing-CSR https://github.com/Neilpang/acme.sh/wiki/Issue-a-cert-from-existing-CSR
# Under the Hood # 14. Under the Hood
Speak ACME language using shell, directly to "Let's Encrypt". Speak ACME language using shell, directly to "Let's Encrypt".
TODO: TODO:
# Acknowledgments # 15. Acknowledgments
1. Acme-tiny: https://github.com/diafygi/acme-tiny 1. Acme-tiny: https://github.com/diafygi/acme-tiny
2. ACME protocol: https://github.com/ietf-wg-acme/acme 2. ACME protocol: https://github.com/ietf-wg-acme/acme
3. Certbot: https://github.com/certbot/certbot 3. Certbot: https://github.com/certbot/certbot
# License & Others # 16. License & Others
License is GPLv3 License is GPLv3
...@@ -371,8 +459,9 @@ Please Star and Fork me. ...@@ -371,8 +459,9 @@ Please Star and Fork me.
[Issues](https://github.com/Neilpang/acme.sh/issues) and [pull requests](https://github.com/Neilpang/acme.sh/pulls) are welcome. [Issues](https://github.com/Neilpang/acme.sh/issues) and [pull requests](https://github.com/Neilpang/acme.sh/pulls) are welcome.
# Donate # 17. Donate
Your donation makes **acme.sh** better:
1. PayPal: donate@acme.sh
1. PayPal/Alipay(支付宝)/Wechat(微信): [https://donate.acme.sh/](https://donate.acme.sh/)
[Donate List](https://github.com/Neilpang/acme.sh/wiki/Donate-list) [Donate List](https://github.com/Neilpang/acme.sh/wiki/Donate-list)
#!/usr/bin/env sh #!/usr/bin/env sh
VER=2.6.5 VER=2.7.6
PROJECT_NAME="acme.sh" PROJECT_NAME="acme.sh"
...@@ -13,8 +13,8 @@ _SCRIPT_="$0" ...@@ -13,8 +13,8 @@ _SCRIPT_="$0"
_SUB_FOLDERS="dnsapi deploy" _SUB_FOLDERS="dnsapi deploy"
DEFAULT_CA="https://acme-v01.api.letsencrypt.org" _OLD_CA_HOST="https://acme-v01.api.letsencrypt.org"
DEFAULT_AGREEMENT="https://letsencrypt.org/documents/LE-SA-v1.1.1-August-1-2016.pdf" DEFAULT_CA="https://acme-v01.api.letsencrypt.org/directory"
DEFAULT_USER_AGENT="$PROJECT_NAME/$VER ($PROJECT)" DEFAULT_USER_AGENT="$PROJECT_NAME/$VER ($PROJECT)"
DEFAULT_ACCOUNT_EMAIL="" DEFAULT_ACCOUNT_EMAIL=""
...@@ -24,7 +24,8 @@ DEFAULT_DOMAIN_KEY_LENGTH=2048 ...@@ -24,7 +24,8 @@ DEFAULT_DOMAIN_KEY_LENGTH=2048
DEFAULT_OPENSSL_BIN="openssl" DEFAULT_OPENSSL_BIN="openssl"
STAGE_CA="https://acme-staging.api.letsencrypt.org" STAGE_CA="https://acme-staging.api.letsencrypt.org/directory"
_OLD_STAGE_CA_HOST="https://acme-staging.api.letsencrypt.org"
VTYPE_HTTP="http-01" VTYPE_HTTP="http-01"
VTYPE_DNS="dns-01" VTYPE_DNS="dns-01"
...@@ -41,8 +42,14 @@ NO_VALUE="no" ...@@ -41,8 +42,14 @@ NO_VALUE="no"
W_TLS="tls" W_TLS="tls"
MODE_STATELESS="stateless"
STATE_VERIFIED="verified_ok" STATE_VERIFIED="verified_ok"
NGINX="nginx:"
NGINX_START="#ACME_NGINX_START"
NGINX_END="#ACME_NGINX_END"
BEGIN_CSR="-----BEGIN CERTIFICATE REQUEST-----" BEGIN_CSR="-----BEGIN CERTIFICATE REQUEST-----"
END_CSR="-----END CERTIFICATE REQUEST-----" END_CSR="-----END CERTIFICATE REQUEST-----"
...@@ -59,66 +66,128 @@ LOG_LEVEL_2=2 ...@@ -59,66 +66,128 @@ LOG_LEVEL_2=2
LOG_LEVEL_3=3 LOG_LEVEL_3=3
DEFAULT_LOG_LEVEL="$LOG_LEVEL_1" DEFAULT_LOG_LEVEL="$LOG_LEVEL_1"
DEBUG_LEVEL_1=1
DEBUG_LEVEL_2=2
DEBUG_LEVEL_3=3
DEBUG_LEVEL_DEFAULT=$DEBUG_LEVEL_1
DEBUG_LEVEL_NONE=0
HIDDEN_VALUE="[hidden](please add '--output-insecure' to see this value)"
SYSLOG_ERROR="user.error"
SYSLOG_INFO="user.info"
SYSLOG_DEBUG="user.debug"
#error
SYSLOG_LEVEL_ERROR=3
#info
SYSLOG_LEVEL_INFO=6
#debug
SYSLOG_LEVEL_DEBUG=7
#debug2
SYSLOG_LEVEL_DEBUG_2=8
#debug3
SYSLOG_LEVEL_DEBUG_3=9
SYSLOG_LEVEL_DEFAULT=$SYSLOG_LEVEL_ERROR
#none
SYSLOG_LEVEL_NONE=0
_DEBUG_WIKI="https://github.com/Neilpang/acme.sh/wiki/How-to-debug-acme.sh" _DEBUG_WIKI="https://github.com/Neilpang/acme.sh/wiki/How-to-debug-acme.sh"
_PREPARE_LINK="https://github.com/Neilpang/acme.sh/wiki/Install-preparations"
_STATELESS_WIKI="https://github.com/Neilpang/acme.sh/wiki/Stateless-Mode"
_DNS_MANUAL_ERR="The dns manual mode can not renew automatically, you must issue it again manually. You'd better use the other modes instead."
_DNS_MANUAL_WARN="It seems that you are using dns manual mode. please take care: $_DNS_MANUAL_ERR"
__INTERACTIVE="" __INTERACTIVE=""
if [ -t 1 ]; then if [ -t 1 ]; then
__INTERACTIVE="1" __INTERACTIVE="1"
fi fi
__green() { __green() {
if [ "$__INTERACTIVE" ]; then if [ "$__INTERACTIVE${ACME_NO_COLOR}" = "1" ]; then
printf '\033[1;31;32m' printf '\033[1;31;32m'
fi fi
printf -- "$1" printf -- "%b" "$1"
if [ "$__INTERACTIVE" ]; then if [ "$__INTERACTIVE${ACME_NO_COLOR}" = "1" ]; then
printf '\033[0m' printf '\033[0m'
fi fi
} }
__red() { __red() {
if [ "$__INTERACTIVE" ]; then if [ "$__INTERACTIVE${ACME_NO_COLOR}" = "1" ]; then
printf '\033[1;31;40m' printf '\033[1;31;40m'
fi fi
printf -- "$1" printf -- "%b" "$1"
if [ "$__INTERACTIVE" ]; then if [ "$__INTERACTIVE${ACME_NO_COLOR}" = "1" ]; then
printf '\033[0m' printf '\033[0m'
fi fi
} }
_printargs() { _printargs() {
if [ -z "$NO_TIMESTAMP" ] || [ "$NO_TIMESTAMP" = "0" ]; then
printf -- "%s" "[$(date)] "
fi
if [ -z "$2" ]; then if [ -z "$2" ]; then
printf -- "[$(date)] $1" printf -- "%s" "$1"
else else
printf -- "%s" "[$(date)] $1='$2'" printf -- "%s" "$1='$2'"
fi fi
printf "\n" printf "\n"
} }
_dlg_versions() { _dlg_versions() {
echo "Diagnosis versions: " echo "Diagnosis versions: "
echo "openssl:$OPENSSL_BIN" echo "openssl:$ACME_OPENSSL_BIN"
if _exists "$OPENSSL_BIN"; then if _exists "${ACME_OPENSSL_BIN:-openssl}"; then
$OPENSSL_BIN version 2>&1 ${ACME_OPENSSL_BIN:-openssl} version 2>&1
else else
echo "$OPENSSL_BIN doesn't exists." echo "$ACME_OPENSSL_BIN doesn't exists."
fi fi
echo "apache:" echo "apache:"
if [ "$_APACHECTL" ] && _exists "$_APACHECTL"; then if [ "$_APACHECTL" ] && _exists "$_APACHECTL"; then
_APACHECTL -V 2>&1 $_APACHECTL -V 2>&1
else else
echo "apache doesn't exists." echo "apache doesn't exists."
fi fi
echo "nc:" echo "nginx:"
if _exists "nc"; then if _exists "nginx"; then
nc -h 2>&1 nginx -V 2>&1
else
echo "nginx doesn't exists."
fi
echo "socat:"
if _exists "socat"; then
socat -h 2>&1
else else
_debug "nc doesn't exists." _debug "socat doesn't exists."
fi fi
} }
#class
_syslog() {
if [ "${SYS_LOG:-$SYSLOG_LEVEL_NONE}" = "$SYSLOG_LEVEL_NONE" ]; then
return
fi
_logclass="$1"
shift
if [ -z "$__logger_i" ]; then
if _contains "$(logger --help 2>&1)" "-i"; then
__logger_i="logger -i"
else
__logger_i="logger"
fi
fi
$__logger_i -t "$PROJECT_NAME" -p "$_logclass" "$(_printargs "$@")" >/dev/null 2>&1
}
_log() { _log() {
[ -z "$LOG_FILE" ] && return [ -z "$LOG_FILE" ] && return
_printargs "$@" >>"$LOG_FILE" _printargs "$@" >>"$LOG_FILE"
...@@ -126,12 +195,18 @@ _log() { ...@@ -126,12 +195,18 @@ _log() {
_info() { _info() {
_log "$@" _log "$@"
if [ "${SYS_LOG:-$SYSLOG_LEVEL_NONE}" -ge "$SYSLOG_LEVEL_INFO" ]; then
_syslog "$SYSLOG_INFO" "$@"
fi
_printargs "$@" _printargs "$@"
} }
_err() { _err() {
_syslog "$SYSLOG_ERROR" "$@"
_log "$@" _log "$@"
printf -- "[$(date)] " >&2 if [ -z "$NO_TIMESTAMP" ] || [ "$NO_TIMESTAMP" = "0" ]; then
printf -- "%s" "[$(date)] " >&2
fi
if [ -z "$2" ]; then if [ -z "$2" ]; then
__red "$1" >&2 __red "$1" >&2
else else
...@@ -147,33 +222,112 @@ _usage() { ...@@ -147,33 +222,112 @@ _usage() {
} }
_debug() { _debug() {
if [ -z "$LOG_LEVEL" ] || [ "$LOG_LEVEL" -ge "$LOG_LEVEL_1" ]; then if [ "${LOG_LEVEL:-$DEFAULT_LOG_LEVEL}" -ge "$LOG_LEVEL_1" ]; then
_log "$@" _log "$@"
fi fi
if [ -z "$DEBUG" ]; then if [ "${SYS_LOG:-$SYSLOG_LEVEL_NONE}" -ge "$SYSLOG_LEVEL_DEBUG" ]; then
return _syslog "$SYSLOG_DEBUG" "$@"
fi
if [ "${DEBUG:-$DEBUG_LEVEL_NONE}" -ge "$DEBUG_LEVEL_1" ]; then
_printargs "$@" >&2
fi
}
#output the sensitive messages
_secure_debug() {
if [ "${LOG_LEVEL:-$DEFAULT_LOG_LEVEL}" -ge "$LOG_LEVEL_1" ]; then
if [ "$OUTPUT_INSECURE" = "1" ]; then
_log "$@"
else
_log "$1" "$HIDDEN_VALUE"
fi
fi
if [ "${SYS_LOG:-$SYSLOG_LEVEL_NONE}" -ge "$SYSLOG_LEVEL_DEBUG" ]; then
_syslog "$SYSLOG_DEBUG" "$1" "$HIDDEN_VALUE"
fi
if [ "${DEBUG:-$DEBUG_LEVEL_NONE}" -ge "$DEBUG_LEVEL_1" ]; then
if [ "$OUTPUT_INSECURE" = "1" ]; then
_printargs "$@" >&2
else
_printargs "$1" "$HIDDEN_VALUE" >&2
fi
fi fi
_printargs "$@" >&2
} }
_debug2() { _debug2() {
if [ "$LOG_LEVEL" ] && [ "$LOG_LEVEL" -ge "$LOG_LEVEL_2" ]; then if [ "${LOG_LEVEL:-$DEFAULT_LOG_LEVEL}" -ge "$LOG_LEVEL_2" ]; then
_log "$@" _log "$@"
fi fi
if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then if [ "${SYS_LOG:-$SYSLOG_LEVEL_NONE}" -ge "$SYSLOG_LEVEL_DEBUG_2" ]; then
_debug "$@" _syslog "$SYSLOG_DEBUG" "$@"
fi
if [ "${DEBUG:-$DEBUG_LEVEL_NONE}" -ge "$DEBUG_LEVEL_2" ]; then
_printargs "$@" >&2
fi
}
_secure_debug2() {
if [ "${LOG_LEVEL:-$DEFAULT_LOG_LEVEL}" -ge "$LOG_LEVEL_2" ]; then
if [ "$OUTPUT_INSECURE" = "1" ]; then
_log "$@"
else
_log "$1" "$HIDDEN_VALUE"
fi
fi
if [ "${SYS_LOG:-$SYSLOG_LEVEL_NONE}" -ge "$SYSLOG_LEVEL_DEBUG_2" ]; then
_syslog "$SYSLOG_DEBUG" "$1" "$HIDDEN_VALUE"
fi
if [ "${DEBUG:-$DEBUG_LEVEL_NONE}" -ge "$DEBUG_LEVEL_2" ]; then
if [ "$OUTPUT_INSECURE" = "1" ]; then
_printargs "$@" >&2
else
_printargs "$1" "$HIDDEN_VALUE" >&2
fi
fi fi
} }
_debug3() { _debug3() {
if [ "$LOG_LEVEL" ] && [ "$LOG_LEVEL" -ge "$LOG_LEVEL_3" ]; then if [ "${LOG_LEVEL:-$DEFAULT_LOG_LEVEL}" -ge "$LOG_LEVEL_3" ]; then
_log "$@" _log "$@"
fi fi
if [ "$DEBUG" ] && [ "$DEBUG" -ge "3" ]; then if [ "${SYS_LOG:-$SYSLOG_LEVEL_NONE}" -ge "$SYSLOG_LEVEL_DEBUG_3" ]; then
_debug "$@" _syslog "$SYSLOG_DEBUG" "$@"
fi
if [ "${DEBUG:-$DEBUG_LEVEL_NONE}" -ge "$DEBUG_LEVEL_3" ]; then
_printargs "$@" >&2
fi
}
_secure_debug3() {
if [ "${LOG_LEVEL:-$DEFAULT_LOG_LEVEL}" -ge "$LOG_LEVEL_3" ]; then
if [ "$OUTPUT_INSECURE" = "1" ]; then
_log "$@"
else
_log "$1" "$HIDDEN_VALUE"
fi
fi
if [ "${SYS_LOG:-$SYSLOG_LEVEL_NONE}" -ge "$SYSLOG_LEVEL_DEBUG_3" ]; then
_syslog "$SYSLOG_DEBUG" "$1" "$HIDDEN_VALUE"
fi
if [ "${DEBUG:-$DEBUG_LEVEL_NONE}" -ge "$DEBUG_LEVEL_3" ]; then
if [ "$OUTPUT_INSECURE" = "1" ]; then
_printargs "$@" >&2
else
_printargs "$1" "$HIDDEN_VALUE" >&2
fi
fi fi
} }
_upper_case() {
# shellcheck disable=SC2018,SC2019
tr 'a-z' 'A-Z'
}
_lower_case() {
# shellcheck disable=SC2018,SC2019
tr 'A-Z' 'a-z'
}
_startswith() { _startswith() {
_str="$1" _str="$1"
_sub="$2" _sub="$2"
...@@ -205,16 +359,17 @@ _hasfield() { ...@@ -205,16 +359,17 @@ _hasfield() {
_sep="," _sep=","
fi fi
for f in $(echo "$_str" | tr ',' ' '); do for f in $(echo "$_str" | tr "$_sep" ' '); do
if [ "$f" = "$_field" ]; then if [ "$f" = "$_field" ]; then
_debug2 "'$_str' contains '$_field'" _debug2 "'$_str' contains '$_field'"
return 0 #contains ok return 0 #contains ok
fi fi
done done
_debug2 "'$_str' does not contain '$_field'" _debug2 "'$_str' does not contain '$_field'"
return 1 #not contains return 1 #not contains
} }
# str index [sep]
_getfield() { _getfield() {
_str="$1" _str="$1"
_findex="$2" _findex="$2"
...@@ -301,48 +456,313 @@ if [ "$(printf '\x41')" != 'A' ]; then ...@@ -301,48 +456,313 @@ if [ "$(printf '\x41')" != 'A' ]; then
_URGLY_PRINTF=1 _URGLY_PRINTF=1
fi fi
_ESCAPE_XARGS=""
if _exists xargs && [ "$(printf %s '\\x41' | xargs printf)" = 'A' ]; then
_ESCAPE_XARGS=1
fi
_h2b() { _h2b() {
if _exists xxd && xxd -r -p 2>/dev/null; then
return
fi
hex=$(cat) hex=$(cat)
i=1 ic=""
j=2 jc=""
_debug2 _URGLY_PRINTF "$_URGLY_PRINTF"
_debug3 _URGLY_PRINTF "$_URGLY_PRINTF" if [ -z "$_URGLY_PRINTF" ]; then
while true; do if [ "$_ESCAPE_XARGS" ] && _exists xargs; then
if [ -z "$_URGLY_PRINTF" ]; then _debug2 "xargs"
h="$(printf "%s" "$hex" | cut -c $i-$j)" echo "$hex" | _upper_case | sed 's/\([0-9A-F]\{2\}\)/\\\\\\x\1/g' | xargs printf
if [ -z "$h" ]; then
break
fi
printf "\x$h%s"
else else
ic="$(printf "%s" "$hex" | cut -c $i)" for h in $(echo "$hex" | _upper_case | sed 's/\([0-9A-F]\{2\}\)/ \1/g'); do
jc="$(printf "%s" "$hex" | cut -c $j)" if [ -z "$h" ]; then
if [ -z "$ic$jc" ]; then break
break fi
printf "\x$h%s"
done
fi
else
for c in $(echo "$hex" | _upper_case | sed 's/\([0-9A-F]\)/ \1/g'); do
if [ -z "$ic" ]; then
ic=$c
continue
fi fi
jc=$c
ic="$(_h_char_2_dec "$ic")" ic="$(_h_char_2_dec "$ic")"
jc="$(_h_char_2_dec "$jc")" jc="$(_h_char_2_dec "$jc")"
printf '\'"$(printf "%o" "$(_math "$ic" \* 16 + $jc)")""%s" printf '\'"$(printf "%o" "$(_math "$ic" \* 16 + $jc)")""%s"
fi ic=""
jc=""
done
fi
i="$(_math "$i" + 2)" }
j="$(_math "$j" + 2)"
done _is_solaris() {
_contains "${__OS__:=$(uname -a)}" "solaris" || _contains "${__OS__:=$(uname -a)}" "SunOS"
} }
#hex string #_ascii_hex str
_hex() { #this can only process ascii chars, should only be used when od command is missing as a backup way.
_ascii_hex() {
_debug2 "Using _ascii_hex"
_str="$1" _str="$1"
_str_len=${#_str} _str_len=${#_str}
_h_i=1 _h_i=1
while [ "$_h_i" -le "$_str_len" ]; do while [ "$_h_i" -le "$_str_len" ]; do
_str_c="$(printf "%s" "$_str" | cut -c "$_h_i")" _str_c="$(printf "%s" "$_str" | cut -c "$_h_i")"
printf "%02x" "'$_str_c" printf " %02x" "'$_str_c"
_h_i="$(_math "$_h_i" + 1)" _h_i="$(_math "$_h_i" + 1)"
done done
} }
#stdin output hexstr splited by one space
#input:"abc"
#output: " 61 62 63"
_hex_dump() {
if _exists od; then
od -A n -v -t x1 | tr -s " " | sed 's/ $//' | tr -d "\r\t\n"
elif _exists hexdump; then
_debug3 "using hexdump"
hexdump -v -e '/1 ""' -e '/1 " %02x" ""'
elif _exists xxd; then
_debug3 "using xxd"
xxd -ps -c 20 -i | sed "s/ 0x/ /g" | tr -d ",\n" | tr -s " "
else
_debug3 "using _ascii_hex"
str=$(cat)
_ascii_hex "$str"
fi
}
#url encode, no-preserved chars
#A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
#41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a
#a b c d e f g h i j k l m n o p q r s t u v w x y z
#61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a
#0 1 2 3 4 5 6 7 8 9 - _ . ~
#30 31 32 33 34 35 36 37 38 39 2d 5f 2e 7e
#stdin stdout
_url_encode() {
_hex_str=$(_hex_dump)
_debug3 "_url_encode"
_debug3 "_hex_str" "$_hex_str"
for _hex_code in $_hex_str; do
#upper case
case "${_hex_code}" in
"41")
printf "%s" "A"
;;
"42")
printf "%s" "B"
;;
"43")
printf "%s" "C"
;;
"44")
printf "%s" "D"
;;
"45")
printf "%s" "E"
;;
"46")
printf "%s" "F"
;;
"47")
printf "%s" "G"
;;
"48")
printf "%s" "H"
;;
"49")
printf "%s" "I"
;;
"4a")
printf "%s" "J"
;;
"4b")
printf "%s" "K"
;;
"4c")
printf "%s" "L"
;;
"4d")
printf "%s" "M"
;;
"4e")
printf "%s" "N"
;;
"4f")
printf "%s" "O"
;;
"50")
printf "%s" "P"
;;
"51")
printf "%s" "Q"
;;
"52")
printf "%s" "R"
;;
"53")
printf "%s" "S"
;;
"54")
printf "%s" "T"
;;
"55")
printf "%s" "U"
;;
"56")
printf "%s" "V"
;;
"57")
printf "%s" "W"
;;
"58")
printf "%s" "X"
;;
"59")
printf "%s" "Y"
;;
"5a")
printf "%s" "Z"
;;
#lower case
"61")
printf "%s" "a"
;;
"62")
printf "%s" "b"
;;
"63")
printf "%s" "c"
;;
"64")
printf "%s" "d"
;;
"65")
printf "%s" "e"
;;
"66")
printf "%s" "f"
;;
"67")
printf "%s" "g"
;;
"68")
printf "%s" "h"
;;
"69")
printf "%s" "i"
;;
"6a")
printf "%s" "j"
;;
"6b")
printf "%s" "k"
;;
"6c")
printf "%s" "l"
;;
"6d")
printf "%s" "m"
;;
"6e")
printf "%s" "n"
;;
"6f")
printf "%s" "o"
;;
"70")
printf "%s" "p"
;;
"71")
printf "%s" "q"
;;
"72")
printf "%s" "r"
;;
"73")
printf "%s" "s"
;;
"74")
printf "%s" "t"
;;
"75")
printf "%s" "u"
;;
"76")
printf "%s" "v"
;;
"77")
printf "%s" "w"
;;
"78")
printf "%s" "x"
;;
"79")
printf "%s" "y"
;;
"7a")
printf "%s" "z"
;;
#numbers
"30")
printf "%s" "0"
;;
"31")
printf "%s" "1"
;;
"32")
printf "%s" "2"
;;
"33")
printf "%s" "3"
;;
"34")
printf "%s" "4"
;;
"35")
printf "%s" "5"
;;
"36")
printf "%s" "6"
;;
"37")
printf "%s" "7"
;;
"38")
printf "%s" "8"
;;
"39")
printf "%s" "9"
;;
"2d")
printf "%s" "-"
;;
"5f")
printf "%s" "_"
;;
"2e")
printf "%s" "."
;;
"7e")
printf "%s" "~"
;;
#other hex
*)
printf '%%%s' "$_hex_code"
;;
esac
done
}
#options file #options file
_sed_i() { _sed_i() {
options="$1" options="$1"
...@@ -400,19 +820,22 @@ _getfile() { ...@@ -400,19 +820,22 @@ _getfile() {
#Usage: multiline #Usage: multiline
_base64() { _base64() {
[ "" ] #urgly
if [ "$1" ]; then if [ "$1" ]; then
$OPENSSL_BIN base64 -e _debug3 "base64 multiline:'$1'"
${ACME_OPENSSL_BIN:-openssl} base64 -e
else else
$OPENSSL_BIN base64 -e | tr -d '\r\n' _debug3 "base64 single line."
${ACME_OPENSSL_BIN:-openssl} base64 -e | tr -d '\r\n'
fi fi
} }
#Usage: multiline #Usage: multiline
_dbase64() { _dbase64() {
if [ "$1" ]; then if [ "$1" ]; then
$OPENSSL_BIN base64 -d -A ${ACME_OPENSSL_BIN:-openssl} base64 -d -A
else else
$OPENSSL_BIN base64 -d ${ACME_OPENSSL_BIN:-openssl} base64 -d
fi fi
} }
...@@ -429,9 +852,9 @@ _digest() { ...@@ -429,9 +852,9 @@ _digest() {
if [ "$alg" = "sha256" ] || [ "$alg" = "sha1" ] || [ "$alg" = "md5" ]; then if [ "$alg" = "sha256" ] || [ "$alg" = "sha1" ] || [ "$alg" = "md5" ]; then
if [ "$outputhex" ]; then if [ "$outputhex" ]; then
$OPENSSL_BIN dgst -"$alg" -hex | cut -d = -f 2 | tr -d ' ' ${ACME_OPENSSL_BIN:-openssl} dgst -"$alg" -hex | cut -d = -f 2 | tr -d ' '
else else
$OPENSSL_BIN dgst -"$alg" -binary | _base64 ${ACME_OPENSSL_BIN:-openssl} dgst -"$alg" -binary | _base64
fi fi
else else
_err "$alg is not supported yet" _err "$alg is not supported yet"
...@@ -454,9 +877,9 @@ _hmac() { ...@@ -454,9 +877,9 @@ _hmac() {
if [ "$alg" = "sha256" ] || [ "$alg" = "sha1" ]; then if [ "$alg" = "sha256" ] || [ "$alg" = "sha1" ]; then
if [ "$outputhex" ]; then if [ "$outputhex" ]; then
$OPENSSL_BIN dgst -"$alg" -mac HMAC -macopt "hexkey:$secret_hex" | cut -d = -f 2 | tr -d ' ' (${ACME_OPENSSL_BIN:-openssl} dgst -"$alg" -mac HMAC -macopt "hexkey:$secret_hex" 2>/dev/null || ${ACME_OPENSSL_BIN:-openssl} dgst -"$alg" -hmac "$(printf "%s" "$secret_hex" | _h2b)") | cut -d = -f 2 | tr -d ' '
else else
$OPENSSL_BIN dgst -"$alg" -mac HMAC -macopt "hexkey:$secret_hex" -binary ${ACME_OPENSSL_BIN:-openssl} dgst -"$alg" -mac HMAC -macopt "hexkey:$secret_hex" -binary 2>/dev/null || ${ACME_OPENSSL_BIN:-openssl} dgst -"$alg" -hmac "$(printf "%s" "$secret_hex" | _h2b)" -binary
fi fi
else else
_err "$alg is not supported yet" _err "$alg is not supported yet"
...@@ -475,21 +898,15 @@ _sign() { ...@@ -475,21 +898,15 @@ _sign() {
return 1 return 1
fi fi
_sign_openssl="$OPENSSL_BIN dgst -sign $keyfile " _sign_openssl="${ACME_OPENSSL_BIN:-openssl} dgst -sign $keyfile "
if [ "$alg" = "sha256" ]; then
_sign_openssl="$_sign_openssl -$alg"
else
_err "$alg is not supported yet"
return 1
fi
if grep "BEGIN RSA PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then if grep "BEGIN RSA PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then
$_sign_openssl | _base64 $_sign_openssl -$alg | _base64
elif grep "BEGIN EC PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then elif grep "BEGIN EC PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then
if ! _signedECText="$($_sign_openssl | $OPENSSL_BIN asn1parse -inform DER)"; then if ! _signedECText="$($_sign_openssl -sha$__ECC_KEY_LEN | ${ACME_OPENSSL_BIN:-openssl} asn1parse -inform DER)"; then
_err "Sign failed: $_sign_openssl" _err "Sign failed: $_sign_openssl"
_err "Key file: $keyfile" _err "Key file: $keyfile"
_err "Key content:$(wc -l <"$keyfile") lises" _err "Key content:$(wc -l <"$keyfile") lines"
return 1 return 1
fi fi
_debug3 "_signedECText" "$_signedECText" _debug3 "_signedECText" "$_signedECText"
...@@ -505,7 +922,7 @@ _sign() { ...@@ -505,7 +922,7 @@ _sign() {
} }
#keylength #keylength or isEcc flag (empty str => not ecc)
_isEccKey() { _isEccKey() {
_length="$1" _length="$1"
...@@ -524,6 +941,7 @@ _isEccKey() { ...@@ -524,6 +941,7 @@ _isEccKey() {
_createkey() { _createkey() {
length="$1" length="$1"
f="$2" f="$2"
_debug2 "_createkey for file:$f"
eccname="$length" eccname="$length"
if _startswith "$length" "ec-"; then if _startswith "$length" "ec-"; then
length=$(printf "%s" "$length" | cut -d '-' -f 2-100) length=$(printf "%s" "$length" | cut -d '-' -f 2-100)
...@@ -546,12 +964,21 @@ _createkey() { ...@@ -546,12 +964,21 @@ _createkey() {
_debug "Use length $length" _debug "Use length $length"
if ! touch "$f" >/dev/null 2>&1; then
_f_path="$(dirname "$f")"
_debug _f_path "$_f_path"
if ! mkdir -p "$_f_path"; then
_err "Can not create path: $_f_path"
return 1
fi
fi
if _isEccKey "$length"; then if _isEccKey "$length"; then
_debug "Using ec name: $eccname" _debug "Using ec name: $eccname"
$OPENSSL_BIN ecparam -name "$eccname" -genkey 2>/dev/null >"$f" ${ACME_OPENSSL_BIN:-openssl} ecparam -name "$eccname" -genkey 2>/dev/null >"$f"
else else
_debug "Using RSA: $length" _debug "Using RSA: $length"
$OPENSSL_BIN genrsa "$length" 2>/dev/null >"$f" ${ACME_OPENSSL_BIN:-openssl} genrsa "$length" 2>/dev/null >"$f"
fi fi
if [ "$?" != "0" ]; then if [ "$?" != "0" ]; then
...@@ -564,7 +991,7 @@ _createkey() { ...@@ -564,7 +991,7 @@ _createkey() {
_is_idn() { _is_idn() {
_is_idn_d="$1" _is_idn_d="$1"
_debug2 _is_idn_d "$_is_idn_d" _debug2 _is_idn_d "$_is_idn_d"
_idn_temp=$(printf "%s" "$_is_idn_d" | tr -d '[0-9]' | tr -d '[a-z]' | tr -d '[A-Z]' | tr -d '.,-') _idn_temp=$(printf "%s" "$_is_idn_d" | tr -d '0-9' | tr -d 'a-z' | tr -d 'A-Z' | tr -d '.,-')
_debug2 _idn_temp "$_idn_temp" _debug2 _idn_temp "$_idn_temp"
[ "$_idn_temp" ] [ "$_idn_temp" ]
} }
...@@ -625,18 +1052,23 @@ _createcsr() { ...@@ -625,18 +1052,23 @@ _createcsr() {
else else
alt="DNS:$domainlist" alt="DNS:$domainlist"
fi fi
#multi #multi
_info "Multi domain" "$alt" _info "Multi domain" "$alt"
printf -- "\nsubjectAltName=$alt" >>"$csrconf" printf -- "\nsubjectAltName=$alt" >>"$csrconf"
fi fi
if [ "$Le_OCSP_Stable" ]; then if [ "$Le_OCSP_Staple" ] || [ "$Le_OCSP_Stable" ]; then
_savedomainconf Le_OCSP_Stable "$Le_OCSP_Stable" _savedomainconf Le_OCSP_Staple "$Le_OCSP_Staple"
_cleardomainconf Le_OCSP_Stable
printf -- "\nbasicConstraints = CA:FALSE\n1.3.6.1.5.5.7.1.24=DER:30:03:02:01:05" >>"$csrconf" printf -- "\nbasicConstraints = CA:FALSE\n1.3.6.1.5.5.7.1.24=DER:30:03:02:01:05" >>"$csrconf"
fi fi
_csr_cn="$(_idn "$domain")" _csr_cn="$(_idn "$domain")"
_debug2 _csr_cn "$_csr_cn" _debug2 _csr_cn "$_csr_cn"
$OPENSSL_BIN req -new -sha256 -key "$csrkey" -subj "/CN=$_csr_cn" -config "$csrconf" -out "$csr" if _contains "$(uname -a)" "MINGW"; then
${ACME_OPENSSL_BIN:-openssl} req -new -sha256 -key "$csrkey" -subj "//CN=$_csr_cn" -config "$csrconf" -out "$csr"
else
${ACME_OPENSSL_BIN:-openssl} req -new -sha256 -key "$csrkey" -subj "/CN=$_csr_cn" -config "$csrconf" -out "$csr"
fi
} }
#_signcsr key csr conf cert #_signcsr key csr conf cert
...@@ -647,7 +1079,7 @@ _signcsr() { ...@@ -647,7 +1079,7 @@ _signcsr() {
cert="$4" cert="$4"
_debug "_signcsr" _debug "_signcsr"
_msg="$($OPENSSL_BIN x509 -req -days 365 -in "$csr" -signkey "$key" -extensions v3_req -extfile "$conf" -out "$cert" 2>&1)" _msg="$(${ACME_OPENSSL_BIN:-openssl} x509 -req -days 365 -in "$csr" -signkey "$key" -extensions v3_req -extfile "$conf" -out "$cert" 2>&1)"
_ret="$?" _ret="$?"
_debug "$_msg" _debug "$_msg"
return $_ret return $_ret
...@@ -660,7 +1092,7 @@ _readSubjectFromCSR() { ...@@ -660,7 +1092,7 @@ _readSubjectFromCSR() {
_usage "_readSubjectFromCSR mycsr.csr" _usage "_readSubjectFromCSR mycsr.csr"
return 1 return 1
fi fi
$OPENSSL_BIN req -noout -in "$_csrfile" -subject | _egrep_o "CN=.*" | cut -d = -f 2 | cut -d / -f 1 | tr -d '\n' ${ACME_OPENSSL_BIN:-openssl} req -noout -in "$_csrfile" -subject | tr ',' "\n" | _egrep_o "CN *=.*" | cut -d = -f 2 | cut -d / -f 1 | tr -d ' \n'
} }
#_csrfile #_csrfile
...@@ -675,7 +1107,7 @@ _readSubjectAltNamesFromCSR() { ...@@ -675,7 +1107,7 @@ _readSubjectAltNamesFromCSR() {
_csrsubj="$(_readSubjectFromCSR "$_csrfile")" _csrsubj="$(_readSubjectFromCSR "$_csrfile")"
_debug _csrsubj "$_csrsubj" _debug _csrsubj "$_csrsubj"
_dnsAltnames="$($OPENSSL_BIN req -noout -text -in "$_csrfile" | grep "^ *DNS:.*" | tr -d ' \n')" _dnsAltnames="$(${ACME_OPENSSL_BIN:-openssl} req -noout -text -in "$_csrfile" | grep "^ *DNS:.*" | tr -d ' \n')"
_debug _dnsAltnames "$_dnsAltnames" _debug _dnsAltnames "$_dnsAltnames"
if _contains "$_dnsAltnames," "DNS:$_csrsubj,"; then if _contains "$_dnsAltnames," "DNS:$_csrsubj,"; then
...@@ -688,7 +1120,7 @@ _readSubjectAltNamesFromCSR() { ...@@ -688,7 +1120,7 @@ _readSubjectAltNamesFromCSR() {
printf "%s" "$_dnsAltnames" | sed "s/DNS://g" printf "%s" "$_dnsAltnames" | sed "s/DNS://g"
} }
#_csrfile #_csrfile
_readKeyLengthFromCSR() { _readKeyLengthFromCSR() {
_csrfile="$1" _csrfile="$1"
if [ -z "$_csrfile" ]; then if [ -z "$_csrfile" ]; then
...@@ -696,13 +1128,19 @@ _readKeyLengthFromCSR() { ...@@ -696,13 +1128,19 @@ _readKeyLengthFromCSR() {
return 1 return 1
fi fi
_outcsr="$($OPENSSL_BIN req -noout -text -in "$_csrfile")" _outcsr="$(${ACME_OPENSSL_BIN:-openssl} req -noout -text -in "$_csrfile")"
_debug2 _outcsr "$_outcsr"
if _contains "$_outcsr" "Public Key Algorithm: id-ecPublicKey"; then if _contains "$_outcsr" "Public Key Algorithm: id-ecPublicKey"; then
_debug "ECC CSR" _debug "ECC CSR"
echo "$_outcsr" | _egrep_o "^ *ASN1 OID:.*" | cut -d ':' -f 2 | tr -d ' ' echo "$_outcsr" | tr "\t" " " | _egrep_o "^ *ASN1 OID:.*" | cut -d ':' -f 2 | tr -d ' '
else else
_debug "RSA CSR" _debug "RSA CSR"
echo "$_outcsr" | _egrep_o "^ *Public-Key:.*" | cut -d '(' -f 2 | cut -d ' ' -f 1 _rkl="$(echo "$_outcsr" | tr "\t" " " | _egrep_o "^ *Public.Key:.*" | cut -d '(' -f 2 | cut -d ' ' -f 1)"
if [ "$_rkl" ]; then
echo "$_rkl"
else
echo "$_outcsr" | tr "\t" " " | _egrep_o "RSA Public.Key:.*" | cut -d '(' -f 2 | cut -d ' ' -f 1
fi
fi fi
} }
...@@ -711,7 +1149,7 @@ _ss() { ...@@ -711,7 +1149,7 @@ _ss() {
if _exists "ss"; then if _exists "ss"; then
_debug "Using: ss" _debug "Using: ss"
ss -ntpl | grep ":$_port " ss -ntpl 2>/dev/null | grep ":$_port "
return 0 return 0
fi fi
...@@ -726,8 +1164,12 @@ _ss() { ...@@ -726,8 +1164,12 @@ _ss() {
elif netstat -help 2>&1 | grep -- '-P protocol' >/dev/null; then elif netstat -help 2>&1 | grep -- '-P protocol' >/dev/null; then
#for solaris #for solaris
netstat -an -P tcp | grep "\.$_port " | grep "LISTEN" netstat -an -P tcp | grep "\.$_port " | grep "LISTEN"
else elif netstat -help 2>&1 | grep "\-p" >/dev/null; then
#for full linux
netstat -ntpl | grep ":$_port " netstat -ntpl | grep ":$_port "
else
#for busybox (embedded linux; no pid support)
netstat -ntl 2>/dev/null | grep ":$_port "
fi fi
fi fi
return 0 return 0
...@@ -736,6 +1178,28 @@ _ss() { ...@@ -736,6 +1178,28 @@ _ss() {
return 1 return 1
} }
#outfile key cert cacert [password [name [caname]]]
_toPkcs() {
_cpfx="$1"
_ckey="$2"
_ccert="$3"
_cca="$4"
pfxPassword="$5"
pfxName="$6"
pfxCaname="$7"
if [ "$pfxCaname" ]; then
${ACME_OPENSSL_BIN:-openssl} pkcs12 -export -out "$_cpfx" -inkey "$_ckey" -in "$_ccert" -certfile "$_cca" -password "pass:$pfxPassword" -name "$pfxName" -caname "$pfxCaname"
elif [ "$pfxName" ]; then
${ACME_OPENSSL_BIN:-openssl} pkcs12 -export -out "$_cpfx" -inkey "$_ckey" -in "$_ccert" -certfile "$_cca" -password "pass:$pfxPassword" -name "$pfxName"
elif [ "$pfxPassword" ]; then
${ACME_OPENSSL_BIN:-openssl} pkcs12 -export -out "$_cpfx" -inkey "$_ckey" -in "$_ccert" -certfile "$_cca" -password "pass:$pfxPassword"
else
${ACME_OPENSSL_BIN:-openssl} pkcs12 -export -out "$_cpfx" -inkey "$_ckey" -in "$_ccert" -certfile "$_cca"
fi
}
#domain [password] [isEcc] #domain [password] [isEcc]
toPkcs() { toPkcs() {
domain="$1" domain="$1"
...@@ -749,11 +1213,7 @@ toPkcs() { ...@@ -749,11 +1213,7 @@ toPkcs() {
_initpath "$domain" "$_isEcc" _initpath "$domain" "$_isEcc"
if [ "$pfxPassword" ]; then _toPkcs "$CERT_PFX_PATH" "$CERT_KEY_PATH" "$CERT_PATH" "$CA_CERT_PATH" "$pfxPassword"
$OPENSSL_BIN pkcs12 -export -out "$CERT_PFX_PATH" -inkey "$CERT_KEY_PATH" -in "$CERT_PATH" -certfile "$CA_CERT_PATH" -password "pass:$pfxPassword"
else
$OPENSSL_BIN pkcs12 -export -out "$CERT_PFX_PATH" -inkey "$CERT_KEY_PATH" -in "$CERT_PATH" -certfile "$CA_CERT_PATH"
fi
if [ "$?" = "0" ]; then if [ "$?" = "0" ]; then
_info "Success, Pfx is exported to: $CERT_PFX_PATH" _info "Success, Pfx is exported to: $CERT_PFX_PATH"
...@@ -761,7 +1221,28 @@ toPkcs() { ...@@ -761,7 +1221,28 @@ toPkcs() {
} }
#[2048] #domain [isEcc]
toPkcs8() {
domain="$1"
if [ -z "$domain" ]; then
_usage "Usage: $PROJECT_ENTRY --toPkcs8 -d domain [--ecc]"
return 1
fi
_isEcc="$2"
_initpath "$domain" "$_isEcc"
${ACME_OPENSSL_BIN:-openssl} pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in "$CERT_KEY_PATH" -out "$CERT_PKCS8_PATH"
if [ "$?" = "0" ]; then
_info "Success, $CERT_PKCS8_PATH"
fi
}
#[2048]
createAccountKey() { createAccountKey() {
_info "Creating account key" _info "Creating account key"
if [ -z "$1" ]; then if [ -z "$1" ]; then
...@@ -806,17 +1287,20 @@ createDomainKey() { ...@@ -806,17 +1287,20 @@ createDomainKey() {
fi fi
domain=$1 domain=$1
length=$2 _cdl=$2
if [ -z "$length" ]; then if [ -z "$_cdl" ]; then
_debug "Use DEFAULT_DOMAIN_KEY_LENGTH=$DEFAULT_DOMAIN_KEY_LENGTH" _debug "Use DEFAULT_DOMAIN_KEY_LENGTH=$DEFAULT_DOMAIN_KEY_LENGTH"
length="$DEFAULT_DOMAIN_KEY_LENGTH" _cdl="$DEFAULT_DOMAIN_KEY_LENGTH"
fi fi
_initpath "$domain" "$length" _initpath "$domain" "$_cdl"
if [ ! -f "$CERT_KEY_PATH" ] || ([ "$FORCE" ] && ! [ "$IS_RENEW" ]); then if [ ! -f "$CERT_KEY_PATH" ] || ([ "$FORCE" ] && ! [ "$IS_RENEW" ]) || [ "$Le_ForceNewDomainKey" = "1" ]; then
_createkey "$length" "$CERT_KEY_PATH" if _createkey "$_cdl" "$CERT_KEY_PATH"; then
_savedomainconf Le_Keylength "$_cdl"
_info "The domain key is here: $(__green $CERT_KEY_PATH)"
fi
else else
if [ "$IS_RENEW" ]; then if [ "$IS_RENEW" ]; then
_info "Domain key exists, skip" _info "Domain key exists, skip"
...@@ -858,17 +1342,17 @@ createCSR() { ...@@ -858,17 +1342,17 @@ createCSR() {
} }
_urlencode() { _url_replace() {
tr '/+' '_-' | tr -d '= ' tr '/+' '_-' | tr -d '= '
} }
_time2str() { _time2str() {
#BSD #Linux
if date -u -d@"$1" 2>/dev/null; then if date -u -d@"$1" 2>/dev/null; then
return return
fi fi
#Linux #BSD
if date -u -r "$1" 2>/dev/null; then if date -u -r "$1" 2>/dev/null; then
return return
fi fi
...@@ -879,6 +1363,10 @@ _time2str() { ...@@ -879,6 +1363,10 @@ _time2str() {
echo "$_t_s_a" echo "$_t_s_a"
fi fi
#Busybox
if echo "$1" | awk '{ print strftime("%c", $0); }' 2>/dev/null; then
return
fi
} }
_normalizeJson() { _normalizeJson() {
...@@ -914,7 +1402,7 @@ _calcjwk() { ...@@ -914,7 +1402,7 @@ _calcjwk() {
if grep "BEGIN RSA PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then if grep "BEGIN RSA PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then
_debug "RSA key" _debug "RSA key"
pub_exp=$($OPENSSL_BIN rsa -in "$keyfile" -noout -text | grep "^publicExponent:" | cut -d '(' -f 2 | cut -d 'x' -f 2 | cut -d ')' -f 1) pub_exp=$(${ACME_OPENSSL_BIN:-openssl} rsa -in "$keyfile" -noout -text | grep "^publicExponent:" | cut -d '(' -f 2 | cut -d 'x' -f 2 | cut -d ')' -f 1)
if [ "${#pub_exp}" = "5" ]; then if [ "${#pub_exp}" = "5" ]; then
pub_exp=0$pub_exp pub_exp=0$pub_exp
fi fi
...@@ -923,9 +1411,11 @@ _calcjwk() { ...@@ -923,9 +1411,11 @@ _calcjwk() {
e=$(echo "$pub_exp" | _h2b | _base64) e=$(echo "$pub_exp" | _h2b | _base64)
_debug3 e "$e" _debug3 e "$e"
modulus=$($OPENSSL_BIN rsa -in "$keyfile" -modulus -noout | cut -d '=' -f 2) modulus=$(${ACME_OPENSSL_BIN:-openssl} rsa -in "$keyfile" -modulus -noout | cut -d '=' -f 2)
_debug3 modulus "$modulus" _debug3 modulus "$modulus"
n="$(printf "%s" "$modulus" | _h2b | _base64 | _urlencode)" n="$(printf "%s" "$modulus" | _h2b | _base64 | _url_replace)"
_debug3 n "$n"
jwk='{"e": "'$e'", "kty": "RSA", "n": "'$n'"}' jwk='{"e": "'$e'", "kty": "RSA", "n": "'$n'"}'
_debug3 jwk "$jwk" _debug3 jwk "$jwk"
...@@ -934,22 +1424,29 @@ _calcjwk() { ...@@ -934,22 +1424,29 @@ _calcjwk() {
JWK_HEADERPLACE_PART2='", "alg": "RS256", "jwk": '$jwk'}' JWK_HEADERPLACE_PART2='", "alg": "RS256", "jwk": '$jwk'}'
elif grep "BEGIN EC PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then elif grep "BEGIN EC PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then
_debug "EC key" _debug "EC key"
crv="$($OPENSSL_BIN ec -in "$keyfile" -noout -text 2>/dev/null | grep "^NIST CURVE:" | cut -d ":" -f 2 | tr -d " \r\n")" crv="$(${ACME_OPENSSL_BIN:-openssl} ec -in "$keyfile" -noout -text 2>/dev/null | grep "^NIST CURVE:" | cut -d ":" -f 2 | tr -d " \r\n")"
_debug3 crv "$crv" _debug3 crv "$crv"
__ECC_KEY_LEN=$(echo "$crv" | cut -d "-" -f 2)
if [ "$__ECC_KEY_LEN" = "521" ]; then
__ECC_KEY_LEN=512
fi
_debug3 __ECC_KEY_LEN "$__ECC_KEY_LEN"
if [ -z "$crv" ]; then if [ -z "$crv" ]; then
_debug "Let's try ASN1 OID" _debug "Let's try ASN1 OID"
crv_oid="$($OPENSSL_BIN ec -in "$keyfile" -noout -text 2>/dev/null | grep "^ASN1 OID:" | cut -d ":" -f 2 | tr -d " \r\n")" crv_oid="$(${ACME_OPENSSL_BIN:-openssl} ec -in "$keyfile" -noout -text 2>/dev/null | grep "^ASN1 OID:" | cut -d ":" -f 2 | tr -d " \r\n")"
_debug3 crv_oid "$crv_oid" _debug3 crv_oid "$crv_oid"
case "${crv_oid}" in case "${crv_oid}" in
"prime256v1") "prime256v1")
crv="P-256" crv="P-256"
__ECC_KEY_LEN=256
;; ;;
"secp384r1") "secp384r1")
crv="P-384" crv="P-384"
__ECC_KEY_LEN=384
;; ;;
"secp521r1") "secp521r1")
crv="P-521" crv="P-521"
__ECC_KEY_LEN=512
;; ;;
*) *)
_err "ECC oid : $crv_oid" _err "ECC oid : $crv_oid"
...@@ -959,15 +1456,15 @@ _calcjwk() { ...@@ -959,15 +1456,15 @@ _calcjwk() {
_debug3 crv "$crv" _debug3 crv "$crv"
fi fi
pubi="$($OPENSSL_BIN ec -in "$keyfile" -noout -text 2>/dev/null | grep -n pub: | cut -d : -f 1)" pubi="$(${ACME_OPENSSL_BIN:-openssl} ec -in "$keyfile" -noout -text 2>/dev/null | grep -n pub: | cut -d : -f 1)"
pubi=$(_math "$pubi" + 1) pubi=$(_math "$pubi" + 1)
_debug3 pubi "$pubi" _debug3 pubi "$pubi"
pubj="$($OPENSSL_BIN ec -in "$keyfile" -noout -text 2>/dev/null | grep -n "ASN1 OID:" | cut -d : -f 1)" pubj="$(${ACME_OPENSSL_BIN:-openssl} ec -in "$keyfile" -noout -text 2>/dev/null | grep -n "ASN1 OID:" | cut -d : -f 1)"
pubj=$(_math "$pubj" - 1) pubj=$(_math "$pubj" - 1)
_debug3 pubj "$pubj" _debug3 pubj "$pubj"
pubtext="$($OPENSSL_BIN ec -in "$keyfile" -noout -text 2>/dev/null | sed -n "$pubi,${pubj}p" | tr -d " \n\r")" pubtext="$(${ACME_OPENSSL_BIN:-openssl} ec -in "$keyfile" -noout -text 2>/dev/null | sed -n "$pubi,${pubj}p" | tr -d " \n\r")"
_debug3 pubtext "$pubtext" _debug3 pubtext "$pubtext"
xlen="$(printf "%s" "$pubtext" | tr -d ':' | wc -c)" xlen="$(printf "%s" "$pubtext" | tr -d ':' | wc -c)"
...@@ -978,22 +1475,22 @@ _calcjwk() { ...@@ -978,22 +1475,22 @@ _calcjwk() {
x="$(printf "%s" "$pubtext" | cut -d : -f 2-"$xend")" x="$(printf "%s" "$pubtext" | cut -d : -f 2-"$xend")"
_debug3 x "$x" _debug3 x "$x"
x64="$(printf "%s" "$x" | tr -d : | _h2b | _base64 | _urlencode)" x64="$(printf "%s" "$x" | tr -d : | _h2b | _base64 | _url_replace)"
_debug3 x64 "$x64" _debug3 x64 "$x64"
xend=$(_math "$xend" + 1) xend=$(_math "$xend" + 1)
y="$(printf "%s" "$pubtext" | cut -d : -f "$xend"-10000)" y="$(printf "%s" "$pubtext" | cut -d : -f "$xend"-10000)"
_debug3 y "$y" _debug3 y "$y"
y64="$(printf "%s" "$y" | tr -d : | _h2b | _base64 | _urlencode)" y64="$(printf "%s" "$y" | tr -d : | _h2b | _base64 | _url_replace)"
_debug3 y64 "$y64" _debug3 y64 "$y64"
jwk='{"crv": "'$crv'", "kty": "EC", "x": "'$x64'", "y": "'$y64'"}' jwk='{"crv": "'$crv'", "kty": "EC", "x": "'$x64'", "y": "'$y64'"}'
_debug3 jwk "$jwk" _debug3 jwk "$jwk"
JWK_HEADER='{"alg": "ES256", "jwk": '$jwk'}' JWK_HEADER='{"alg": "ES'$__ECC_KEY_LEN'", "jwk": '$jwk'}'
JWK_HEADERPLACE_PART1='{"nonce": "' JWK_HEADERPLACE_PART1='{"nonce": "'
JWK_HEADERPLACE_PART2='", "alg": "ES256", "jwk": '$jwk'}' JWK_HEADERPLACE_PART2='", "alg": "ES'$__ECC_KEY_LEN'", "jwk": '$jwk'}'
else else
_err "Only RSA or EC key is supported." _err "Only RSA or EC key is supported."
return 1 return 1
...@@ -1007,6 +1504,10 @@ _time() { ...@@ -1007,6 +1504,10 @@ _time() {
date -u "+%s" date -u "+%s"
} }
_utc_date() {
date -u "+%Y-%m-%d %H:%M:%S"
}
_mktemp() { _mktemp() {
if _exists mktemp; then if _exists mktemp; then
if mktemp 2>/dev/null; then if mktemp 2>/dev/null; then
...@@ -1047,7 +1548,9 @@ _inithttp() { ...@@ -1047,7 +1548,9 @@ _inithttp() {
_ACME_CURL="$_ACME_CURL --trace-ascii $_CURL_DUMP " _ACME_CURL="$_ACME_CURL --trace-ascii $_CURL_DUMP "
fi fi
if [ "$CA_BUNDLE" ]; then if [ "$CA_PATH" ]; then
_ACME_CURL="$_ACME_CURL --capath $CA_PATH "
elif [ "$CA_BUNDLE" ]; then
_ACME_CURL="$_ACME_CURL --cacert $CA_BUNDLE " _ACME_CURL="$_ACME_CURL --cacert $CA_BUNDLE "
fi fi
...@@ -1058,11 +1561,18 @@ _inithttp() { ...@@ -1058,11 +1561,18 @@ _inithttp() {
if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then
_ACME_WGET="$_ACME_WGET -d " _ACME_WGET="$_ACME_WGET -d "
fi fi
if [ "$CA_BUNDLE" ]; then if [ "$CA_PATH" ]; then
_ACME_WGET="$_ACME_WGET --ca-certificate $CA_BUNDLE " _ACME_WGET="$_ACME_WGET --ca-directory=$CA_PATH "
elif [ "$CA_BUNDLE" ]; then
_ACME_WGET="$_ACME_WGET --ca-certificate=$CA_BUNDLE "
fi fi
fi fi
#from wget 1.14: do not skip body on 404 error
if [ "$_ACME_WGET" ] && _contains "$($_ACME_WGET --help 2>&1)" "--content-on-error"; then
_ACME_WGET="$_ACME_WGET --content-on-error "
fi
__HTTP_INITIALIZED=1 __HTTP_INITIALIZED=1
} }
...@@ -1083,7 +1593,7 @@ _post() { ...@@ -1083,7 +1593,7 @@ _post() {
_inithttp _inithttp
if [ "$_ACME_CURL" ]; then if [ "$_ACME_CURL" ] && [ "${ACME_USE_WGET:-0}" = "0" ]; then
_CURL="$_ACME_CURL" _CURL="$_ACME_CURL"
if [ "$HTTPS_INSECURE" ]; then if [ "$HTTPS_INSECURE" ]; then
_CURL="$_CURL --insecure " _CURL="$_CURL --insecure "
...@@ -1124,7 +1634,7 @@ _post() { ...@@ -1124,7 +1634,7 @@ _post() {
_ret="$?" _ret="$?"
if [ "$_ret" = "8" ]; then if [ "$_ret" = "8" ]; then
_ret=0 _ret=0
_debug "wget returns 8, the server returns a 'Bad request' respons, lets process the response later." _debug "wget returns 8, the server returns a 'Bad request' response, lets process the response later."
fi fi
if [ "$_ret" != "0" ]; then if [ "$_ret" != "0" ]; then
_err "Please refer to https://www.gnu.org/software/wget/manual/html_node/Exit-Status.html for error code: $_ret" _err "Please refer to https://www.gnu.org/software/wget/manual/html_node/Exit-Status.html for error code: $_ret"
...@@ -1150,7 +1660,7 @@ _get() { ...@@ -1150,7 +1660,7 @@ _get() {
_inithttp _inithttp
if [ "$_ACME_CURL" ]; then if [ "$_ACME_CURL" ] && [ "${ACME_USE_WGET:-0}" = "0" ]; then
_CURL="$_ACME_CURL" _CURL="$_ACME_CURL"
if [ "$HTTPS_INSECURE" ]; then if [ "$HTTPS_INSECURE" ]; then
_CURL="$_CURL --insecure " _CURL="$_CURL --insecure "
...@@ -1187,9 +1697,9 @@ _get() { ...@@ -1187,9 +1697,9 @@ _get() {
$_WGET --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" -O - "$url" $_WGET --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" -O - "$url"
fi fi
ret=$? ret=$?
if [ "$_ret" = "8" ]; then if [ "$ret" = "8" ]; then
_ret=0 ret=0
_debug "wget returns 8, the server returns a 'Bad request' respons, lets process the response later." _debug "wget returns 8, the server returns a 'Bad request' response, lets process the response later."
fi fi
if [ "$ret" != "0" ]; then if [ "$ret" != "0" ]; then
_err "Please refer to https://www.gnu.org/software/wget/manual/html_node/Exit-Status.html for error code: $ret" _err "Please refer to https://www.gnu.org/software/wget/manual/html_node/Exit-Status.html for error code: $ret"
...@@ -1229,65 +1739,94 @@ _send_signed_request() { ...@@ -1229,65 +1739,94 @@ _send_signed_request() {
return 1 return 1
fi fi
payload64=$(printf "%s" "$payload" | _base64 | _urlencode) payload64=$(printf "%s" "$payload" | _base64 | _url_replace)
_debug3 payload64 "$payload64" _debug3 payload64 "$payload64"
if [ -z "$_CACHED_NONCE" ]; then MAX_REQUEST_RETRY_TIMES=5
_debug2 "Get nonce." _request_retry_times=0
nonceurl="$API/directory" while [ "${_request_retry_times}" -lt "$MAX_REQUEST_RETRY_TIMES" ]; do
_headers="$(_get "$nonceurl" "onlyheader")" _debug3 _request_retry_times "$_request_retry_times"
if [ -z "$_CACHED_NONCE" ]; then
_headers=""
if [ "$ACME_NEW_NONCE" ]; then
_debug2 "Get nonce. ACME_NEW_NONCE" "$ACME_NEW_NONCE"
nonceurl="$ACME_NEW_NONCE"
if _post "" "$nonceurl" "" "HEAD"; then
_headers="$(cat "$HTTP_HEADER")"
fi
fi
if [ -z "$_headers" ]; then
_debug2 "Get nonce. ACME_DIRECTORY" "$ACME_DIRECTORY"
nonceurl="$ACME_DIRECTORY"
_headers="$(_get "$nonceurl" "onlyheader")"
fi
if [ "$?" != "0" ]; then
_err "Can not connect to $nonceurl to get nonce."
return 1
fi
if [ "$?" != "0" ]; then _debug2 _headers "$_headers"
_err "Can not connect to $nonceurl to get nonce."
return 1 _CACHED_NONCE="$(echo "$_headers" | grep "Replay-Nonce:" | _head_n 1 | tr -d "\r\n " | cut -d ':' -f 2)"
_debug2 _CACHED_NONCE "$_CACHED_NONCE"
else
_debug2 "Use _CACHED_NONCE" "$_CACHED_NONCE"
fi fi
nonce="$_CACHED_NONCE"
_debug2 nonce "$nonce"
_debug2 _headers "$_headers" protected="$JWK_HEADERPLACE_PART1$nonce\", \"url\": \"${url}$JWK_HEADERPLACE_PART2"
_debug3 protected "$protected"
_CACHED_NONCE="$(echo "$_headers" | grep "Replay-Nonce:" | _head_n 1 | tr -d "\r\n " | cut -d ':' -f 2)" protected64="$(printf "%s" "$protected" | _base64 | _url_replace)"
_debug2 _CACHED_NONCE "$_CACHED_NONCE" _debug3 protected64 "$protected64"
else
_debug2 "Use _CACHED_NONCE" "$_CACHED_NONCE"
fi
nonce="$_CACHED_NONCE"
_debug2 nonce "$nonce"
protected="$JWK_HEADERPLACE_PART1$nonce$JWK_HEADERPLACE_PART2" if ! _sig_t="$(printf "%s" "$protected64.$payload64" | _sign "$keyfile" "sha256")"; then
_debug3 protected "$protected" _err "Sign request failed."
return 1
fi
_debug3 _sig_t "$_sig_t"
protected64="$(printf "%s" "$protected" | _base64 | _urlencode)" sig="$(printf "%s" "$_sig_t" | _url_replace)"
_debug3 protected64 "$protected64" _debug3 sig "$sig"
if ! _sig_t="$(printf "%s" "$protected64.$payload64" | _sign "$keyfile" "sha256")"; then body="{\"header\": $JWK_HEADER, \"protected\": \"$protected64\", \"payload\": \"$payload64\", \"signature\": \"$sig\"}"
_err "Sign request failed." _debug3 body "$body"
return 1
fi
_debug3 _sig_t "$_sig_t"
sig="$(printf "%s" "$_sig_t" | _urlencode)" response="$(_post "$body" "$url" "$needbase64")"
_debug3 sig "$sig" _CACHED_NONCE=""
body="{\"header\": $JWK_HEADER, \"protected\": \"$protected64\", \"payload\": \"$payload64\", \"signature\": \"$sig\"}" if [ "$?" != "0" ]; then
_debug3 body "$body" _err "Can not post to $url"
return 1
fi
_debug2 original "$response"
response="$(echo "$response" | _normalizeJson)"
response="$(_post "$body" "$url" "$needbase64")" responseHeaders="$(cat "$HTTP_HEADER")"
_CACHED_NONCE=""
if [ "$?" != "0" ]; then
_err "Can not post to $url"
return 1
fi
_debug2 original "$response"
response="$(echo "$response" | _normalizeJson)" _debug2 responseHeaders "$responseHeaders"
_debug2 response "$response"
code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\r\n")"
_debug code "$code"
responseHeaders="$(cat "$HTTP_HEADER")" _CACHED_NONCE="$(echo "$responseHeaders" | grep "Replay-Nonce:" | _head_n 1 | tr -d "\r\n " | cut -d ':' -f 2)"
_debug2 responseHeaders "$responseHeaders" _body="$response"
_debug2 response "$response" if [ "$needbase64" ]; then
code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\r\n")" _body="$(echo "$_body" | _dbase64)"
_debug code "$code" _debug2 _body "$_body"
fi
_CACHED_NONCE="$(echo "$responseHeaders" | grep "Replay-Nonce:" | _head_n 1 | tr -d "\r\n " | cut -d ':' -f 2)" if _contains "$_body" "JWS has invalid anti-replay nonce"; then
_info "It seems the CA server is busy now, let's wait and retry."
_request_retry_times=$(_math "$_request_retry_times" + 1)
_sleep 5
continue
fi
break
done
} }
...@@ -1312,20 +1851,20 @@ _setopt() { ...@@ -1312,20 +1851,20 @@ _setopt() {
__val="$(echo "$__val" | sed 's/&/\\&/g')" __val="$(echo "$__val" | sed 's/&/\\&/g')"
fi fi
text="$(cat "$__conf")" text="$(cat "$__conf")"
echo "$text" | sed "s|^$__opt$__sep.*$|$__opt$__sep$__val$__end|" >"$__conf" printf -- "%s\n" "$text" | sed "s|^$__opt$__sep.*$|$__opt$__sep$__val$__end|" >"$__conf"
elif grep -n "^#$__opt$__sep" "$__conf" >/dev/null; then elif grep -n "^#$__opt$__sep" "$__conf" >/dev/null; then
if _contains "$__val" "&"; then if _contains "$__val" "&"; then
__val="$(echo "$__val" | sed 's/&/\\&/g')" __val="$(echo "$__val" | sed 's/&/\\&/g')"
fi fi
text="$(cat "$__conf")" text="$(cat "$__conf")"
echo "$text" | sed "s|^#$__opt$__sep.*$|$__opt$__sep$__val$__end|" >"$__conf" printf -- "%s\n" "$text" | sed "s|^#$__opt$__sep.*$|$__opt$__sep$__val$__end|" >"$__conf"
else else
_debug3 APP _debug3 APP
echo "$__opt$__sep$__val$__end" >>"$__conf" echo "$__opt$__sep$__val$__end" >>"$__conf"
fi fi
_debug2 "$(grep -n "^$__opt$__sep" "$__conf")" _debug3 "$(grep -n "^$__opt$__sep" "$__conf")"
} }
#_save_conf file key value #_save_conf file key value
...@@ -1388,6 +1927,24 @@ _saveaccountconf() { ...@@ -1388,6 +1927,24 @@ _saveaccountconf() {
_save_conf "$ACCOUNT_CONF_PATH" "$1" "$2" _save_conf "$ACCOUNT_CONF_PATH" "$1" "$2"
} }
#key value
_saveaccountconf_mutable() {
_save_conf "$ACCOUNT_CONF_PATH" "SAVED_$1" "$2"
#remove later
_clearaccountconf "$1"
}
#key
_readaccountconf() {
_read_conf "$ACCOUNT_CONF_PATH" "$1"
}
#key
_readaccountconf_mutable() {
_rac_key="$1"
_readaccountconf "SAVED_$_rac_key"
}
#_clearaccountconf key #_clearaccountconf key
_clearaccountconf() { _clearaccountconf() {
_clear_conf "$ACCOUNT_CONF_PATH" "$1" _clear_conf "$ACCOUNT_CONF_PATH" "$1"
...@@ -1415,60 +1972,22 @@ _startserver() { ...@@ -1415,60 +1972,22 @@ _startserver() {
_debug "ncaddr" "$ncaddr" _debug "ncaddr" "$ncaddr"
_debug "startserver: $$" _debug "startserver: $$"
nchelp="$(nc -h 2>&1)"
_debug Le_HTTPPort "$Le_HTTPPort" _debug Le_HTTPPort "$Le_HTTPPort"
_debug Le_Listen_V4 "$Le_Listen_V4" _debug Le_Listen_V4 "$Le_Listen_V4"
_debug Le_Listen_V6 "$Le_Listen_V6" _debug Le_Listen_V6 "$Le_Listen_V6"
_NC="nc"
_NC="socat"
if [ "$Le_Listen_V4" ]; then if [ "$Le_Listen_V4" ]; then
_NC="$_NC -4" _NC="$_NC -4"
elif [ "$Le_Listen_V6" ]; then elif [ "$Le_Listen_V6" ]; then
_NC="$_NC -6" _NC="$_NC -6"
fi fi
if echo "$nchelp" | grep "\-q[ ,]" >/dev/null; then
_NC="$_NC -q 1 -l $ncaddr"
else
if echo "$nchelp" | grep "GNU netcat" >/dev/null && echo "$nchelp" | grep "\-c, \-\-close" >/dev/null; then
_NC="$_NC -c -l $ncaddr"
elif echo "$nchelp" | grep "\-N" | grep "Shutdown the network socket after EOF on stdin" >/dev/null; then
_NC="$_NC -N -l $ncaddr"
else
_NC="$_NC -l $ncaddr"
fi
fi
_debug "_NC" "$_NC" _debug "_NC" "$_NC"
#todo listen address
#for centos ncat $_NC TCP-LISTEN:$Le_HTTPPort,crlf,reuseaddr,fork SYSTEM:"sleep 0.5; echo HTTP/1.1 200 OK; echo ; echo $content; echo;" &
if _contains "$nchelp" "nmap.org"; then serverproc="$!"
_debug "Using ncat: nmap.org"
if ! _exec "printf \"%s\r\n\r\n%s\" \"HTTP/1.1 200 OK\" \"$content\" | $_NC \"$Le_HTTPPort\" >&2"; then
_exec_err
return 1
fi
if [ "$DEBUG" ]; then
_exec_err
fi
return
fi
# while true ; do
if ! _exec "printf \"%s\r\n\r\n%s\" \"HTTP/1.1 200 OK\" \"$content\" | $_NC -p \"$Le_HTTPPort\" >&2"; then
_exec "printf \"%s\r\n\r\n%s\" \"HTTP/1.1 200 OK\" \"$content\" | $_NC \"$Le_HTTPPort\" >&2"
fi
if [ "$?" != "0" ]; then
_err "nc listen error."
_exec_err
exit 1
fi
if [ "$DEBUG" ]; then
_exec_err
fi
# done
} }
_stopserver() { _stopserver() {
...@@ -1478,25 +1997,8 @@ _stopserver() { ...@@ -1478,25 +1997,8 @@ _stopserver() {
return return
fi fi
_debug2 "Le_HTTPPort" "$Le_HTTPPort" kill $pid
if [ "$Le_HTTPPort" ]; then
if [ "$DEBUG" ] && [ "$DEBUG" -gt "3" ]; then
_get "http://localhost:$Le_HTTPPort" "" 1
else
_get "http://localhost:$Le_HTTPPort" "" 1 >/dev/null 2>&1
fi
fi
_debug2 "Le_TLSPort" "$Le_TLSPort"
if [ "$Le_TLSPort" ]; then
if [ "$DEBUG" ] && [ "$DEBUG" -gt "3" ]; then
_get "https://localhost:$Le_TLSPort" "" 1
_get "https://localhost:$Le_TLSPort" "" 1
else
_get "https://localhost:$Le_TLSPort" "" 1 >/dev/null 2>&1
_get "https://localhost:$Le_TLSPort" "" 1 >/dev/null 2>&1
fi
fi
} }
# sleep sec # sleep sec
...@@ -1551,7 +2053,7 @@ _starttlsserver() { ...@@ -1551,7 +2053,7 @@ _starttlsserver() {
return 1 return 1
fi fi
__S_OPENSSL="$OPENSSL_BIN s_server -cert $TLS_CERT -key $TLS_KEY " __S_OPENSSL="${ACME_OPENSSL_BIN:-openssl} s_server -www -cert $TLS_CERT -key $TLS_KEY "
if [ "$opaddr" ]; then if [ "$opaddr" ]; then
__S_OPENSSL="$__S_OPENSSL -accept $opaddr:$port" __S_OPENSSL="$__S_OPENSSL -accept $opaddr:$port"
else else
...@@ -1568,9 +2070,9 @@ _starttlsserver() { ...@@ -1568,9 +2070,9 @@ _starttlsserver() {
_debug "$__S_OPENSSL" _debug "$__S_OPENSSL"
if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then
(printf "%s\r\n\r\n%s" "HTTP/1.1 200 OK" "$content" | $__S_OPENSSL -tlsextdebug) & $__S_OPENSSL -tlsextdebug &
else else
(printf "%s\r\n\r\n%s" "HTTP/1.1 200 OK" "$content" | $__S_OPENSSL >/dev/null 2>&1) & $__S_OPENSSL >/dev/null 2>&1 &
fi fi
serverproc="$!" serverproc="$!"
...@@ -1582,14 +2084,18 @@ _starttlsserver() { ...@@ -1582,14 +2084,18 @@ _starttlsserver() {
_readlink() { _readlink() {
_rf="$1" _rf="$1"
if ! readlink -f "$_rf" 2>/dev/null; then if ! readlink -f "$_rf" 2>/dev/null; then
if _startswith "$_rf" "\./$PROJECT_ENTRY"; then if _startswith "$_rf" "/"; then
printf -- "%s" "$(pwd)/$PROJECT_ENTRY" echo "$_rf"
return 0 return 0
fi fi
readlink "$_rf" echo "$(pwd)/$_rf" | _conapath
fi fi
} }
_conapath() {
sed "s#/\./#/#g"
}
__initHome() { __initHome() {
if [ -z "$_SCRIPT_HOME" ]; then if [ -z "$_SCRIPT_HOME" ]; then
if _exists readlink && _exists dirname; then if _exists readlink && _exists dirname; then
...@@ -1607,14 +2113,14 @@ __initHome() { ...@@ -1607,14 +2113,14 @@ __initHome() {
fi fi
fi fi
if [ -z "$LE_WORKING_DIR" ]; then # if [ -z "$LE_WORKING_DIR" ]; then
if [ -f "$DEFAULT_INSTALL_HOME/account.conf" ]; then # if [ -f "$DEFAULT_INSTALL_HOME/account.conf" ]; then
_debug "It seems that $PROJECT_NAME is already installed in $DEFAULT_INSTALL_HOME" # _debug "It seems that $PROJECT_NAME is already installed in $DEFAULT_INSTALL_HOME"
LE_WORKING_DIR="$DEFAULT_INSTALL_HOME" # LE_WORKING_DIR="$DEFAULT_INSTALL_HOME"
else # else
LE_WORKING_DIR="$_SCRIPT_HOME" # LE_WORKING_DIR="$_SCRIPT_HOME"
fi # fi
fi # fi
if [ -z "$LE_WORKING_DIR" ]; then if [ -z "$LE_WORKING_DIR" ]; then
_debug "Using default home:$DEFAULT_INSTALL_HOME" _debug "Using default home:$DEFAULT_INSTALL_HOME"
...@@ -1622,7 +2128,13 @@ __initHome() { ...@@ -1622,7 +2128,13 @@ __initHome() {
fi fi
export LE_WORKING_DIR export LE_WORKING_DIR
_DEFAULT_ACCOUNT_CONF_PATH="$LE_WORKING_DIR/account.conf" if [ -z "$LE_CONFIG_HOME" ]; then
LE_CONFIG_HOME="$LE_WORKING_DIR"
fi
_debug "Using config home:$LE_CONFIG_HOME"
export LE_CONFIG_HOME
_DEFAULT_ACCOUNT_CONF_PATH="$LE_CONFIG_HOME/account.conf"
if [ -z "$ACCOUNT_CONF_PATH" ]; then if [ -z "$ACCOUNT_CONF_PATH" ]; then
if [ -f "$_DEFAULT_ACCOUNT_CONF_PATH" ]; then if [ -f "$_DEFAULT_ACCOUNT_CONF_PATH" ]; then
...@@ -1634,16 +2146,73 @@ __initHome() { ...@@ -1634,16 +2146,73 @@ __initHome() {
ACCOUNT_CONF_PATH="$_DEFAULT_ACCOUNT_CONF_PATH" ACCOUNT_CONF_PATH="$_DEFAULT_ACCOUNT_CONF_PATH"
fi fi
DEFAULT_LOG_FILE="$LE_WORKING_DIR/$PROJECT_NAME.log" DEFAULT_LOG_FILE="$LE_CONFIG_HOME/$PROJECT_NAME.log"
DEFAULT_CA_HOME="$LE_WORKING_DIR/ca" DEFAULT_CA_HOME="$LE_CONFIG_HOME/ca"
if [ -z "$LE_TEMP_DIR" ]; then if [ -z "$LE_TEMP_DIR" ]; then
LE_TEMP_DIR="$LE_WORKING_DIR/tmp" LE_TEMP_DIR="$LE_CONFIG_HOME/tmp"
fi
}
#server
_initAPI() {
_api_server="${1:-$ACME_DIRECTORY}"
_debug "_init api for server: $_api_server"
if [ -z "$ACME_NEW_ACCOUNT" ]; then
response=$(_get "$_api_server")
if [ "$?" != "0" ]; then
_debug2 "response" "$response"
_err "Can not init api."
return 1
fi
_debug2 "response" "$response"
ACME_KEY_CHANGE=$(echo "$response" | _egrep_o 'key-change" *: *"[^"]*"' | cut -d '"' -f 3)
export ACME_KEY_CHANGE
ACME_NEW_AUTHZ=$(echo "$response" | _egrep_o 'new-authz" *: *"[^"]*"' | cut -d '"' -f 3)
export ACME_NEW_AUTHZ
ACME_NEW_ORDER=$(echo "$response" | _egrep_o 'new-cert" *: *"[^"]*"' | cut -d '"' -f 3)
ACME_NEW_ORDER_RES="new-cert"
if [ -z "$ACME_NEW_ORDER" ]; then
ACME_NEW_ORDER=$(echo "$response" | _egrep_o 'new-order" *: *"[^"]*"' | cut -d '"' -f 3)
ACME_NEW_ORDER_RES="new-order"
fi
export ACME_NEW_ORDER
export ACME_NEW_ORDER_RES
ACME_NEW_ACCOUNT=$(echo "$response" | _egrep_o 'new-reg" *: *"[^"]*"' | cut -d '"' -f 3)
ACME_NEW_ACCOUNT_RES="new-reg"
if [ -z "$ACME_NEW_ACCOUNT" ]; then
ACME_NEW_ACCOUNT=$(echo "$response" | _egrep_o 'new-account" *: *"[^"]*"' | cut -d '"' -f 3)
ACME_NEW_ACCOUNT_RES="new-account"
fi
export ACME_NEW_ACCOUNT
export ACME_NEW_ACCOUNT_RES
ACME_REVOKE_CERT=$(echo "$response" | _egrep_o 'revoke-cert" *: *"[^"]*"' | cut -d '"' -f 3)
export ACME_REVOKE_CERT
ACME_NEW_NONCE=$(echo "$response" | _egrep_o 'new-nonce" *: *"[^"]*"' | cut -d '"' -f 3)
export ACME_NEW_NONCE
ACME_AGREEMENT=$(echo "$response" | _egrep_o 'terms-of-service" *: *"[^"]*"' | cut -d '"' -f 3)
export ACME_AGREEMENT
_debug "ACME_KEY_CHANGE" "$ACME_KEY_CHANGE"
_debug "ACME_NEW_AUTHZ" "$ACME_NEW_AUTHZ"
_debug "ACME_NEW_ORDER" "$ACME_NEW_ORDER"
_debug "ACME_NEW_ACCOUNT" "$ACME_NEW_ACCOUNT"
_debug "ACME_REVOKE_CERT" "$ACME_REVOKE_CERT"
_debug "ACME_AGREEMENT" "$ACME_AGREEMENT"
fi fi
} }
#[domain] [keylength] #[domain] [keylength or isEcc flag]
_initpath() { _initpath() {
__initHome __initHome
...@@ -1663,23 +2232,27 @@ _initpath() { ...@@ -1663,23 +2232,27 @@ _initpath() {
CA_HOME="$DEFAULT_CA_HOME" CA_HOME="$DEFAULT_CA_HOME"
fi fi
if [ -z "$API" ]; then if [ -z "$ACME_DIRECTORY" ]; then
if [ -z "$STAGE" ]; then if [ -z "$STAGE" ]; then
API="$DEFAULT_CA" ACME_DIRECTORY="$DEFAULT_CA"
else else
API="$STAGE_CA" ACME_DIRECTORY="$STAGE_CA"
_info "Using stage api:$API" _info "Using stage ACME_DIRECTORY: $ACME_DIRECTORY"
fi fi
fi fi
_API_HOST="$(echo "$API" | cut -d : -f 2 | tr -d '/')" _debug2 ACME_DIRECTORY "$ACME_DIRECTORY"
CA_DIR="$CA_HOME/$_API_HOST" _ACME_SERVER_HOST="$(echo "$ACME_DIRECTORY" | cut -d : -f 2 | tr -s / | cut -d / -f 2)"
_debug2 "_ACME_SERVER_HOST" "$_ACME_SERVER_HOST"
CA_DIR="$CA_HOME/$_ACME_SERVER_HOST"
_DEFAULT_CA_CONF="$CA_DIR/ca.conf" _DEFAULT_CA_CONF="$CA_DIR/ca.conf"
if [ -z "$CA_CONF" ]; then if [ -z "$CA_CONF" ]; then
CA_CONF="$_DEFAULT_CA_CONF" CA_CONF="$_DEFAULT_CA_CONF"
fi fi
_debug3 CA_CONF "$CA_CONF"
if [ -f "$CA_CONF" ]; then if [ -f "$CA_CONF" ]; then
. "$CA_CONF" . "$CA_CONF"
...@@ -1690,7 +2263,7 @@ _initpath() { ...@@ -1690,7 +2263,7 @@ _initpath() {
fi fi
if [ -z "$APACHE_CONF_BACKUP_DIR" ]; then if [ -z "$APACHE_CONF_BACKUP_DIR" ]; then
APACHE_CONF_BACKUP_DIR="$LE_WORKING_DIR" APACHE_CONF_BACKUP_DIR="$LE_CONFIG_HOME"
fi fi
if [ -z "$USER_AGENT" ]; then if [ -z "$USER_AGENT" ]; then
...@@ -1698,7 +2271,7 @@ _initpath() { ...@@ -1698,7 +2271,7 @@ _initpath() {
fi fi
if [ -z "$HTTP_HEADER" ]; then if [ -z "$HTTP_HEADER" ]; then
HTTP_HEADER="$LE_WORKING_DIR/http.header" HTTP_HEADER="$LE_CONFIG_HOME/http.header"
fi fi
_OLD_ACCOUNT_KEY="$LE_WORKING_DIR/account.key" _OLD_ACCOUNT_KEY="$LE_WORKING_DIR/account.key"
...@@ -1714,13 +2287,13 @@ _initpath() { ...@@ -1714,13 +2287,13 @@ _initpath() {
ACCOUNT_JSON_PATH="$_DEFAULT_ACCOUNT_JSON_PATH" ACCOUNT_JSON_PATH="$_DEFAULT_ACCOUNT_JSON_PATH"
fi fi
_DEFAULT_CERT_HOME="$LE_WORKING_DIR" _DEFAULT_CERT_HOME="$LE_CONFIG_HOME"
if [ -z "$CERT_HOME" ]; then if [ -z "$CERT_HOME" ]; then
CERT_HOME="$_DEFAULT_CERT_HOME" CERT_HOME="$_DEFAULT_CERT_HOME"
fi fi
if [ -z "$OPENSSL_BIN" ]; then if [ -z "$ACME_OPENSSL_BIN" ] || [ ! -f "$ACME_OPENSSL_BIN" ] || [ ! -x "$ACME_OPENSSL_BIN" ]; then
OPENSSL_BIN="$DEFAULT_OPENSSL_BIN" ACME_OPENSSL_BIN="$DEFAULT_OPENSSL_BIN"
fi fi
if [ -z "$1" ]; then if [ -z "$1" ]; then
...@@ -1746,6 +2319,10 @@ _initpath() { ...@@ -1746,6 +2319,10 @@ _initpath() {
_debug DOMAIN_PATH "$DOMAIN_PATH" _debug DOMAIN_PATH "$DOMAIN_PATH"
fi fi
if [ -z "$DOMAIN_BACKUP_PATH" ]; then
DOMAIN_BACKUP_PATH="$DOMAIN_PATH/backup"
fi
if [ -z "$DOMAIN_CONF" ]; then if [ -z "$DOMAIN_CONF" ]; then
DOMAIN_CONF="$DOMAIN_PATH/$domain.conf" DOMAIN_CONF="$DOMAIN_PATH/$domain.conf"
fi fi
...@@ -1772,18 +2349,21 @@ _initpath() { ...@@ -1772,18 +2349,21 @@ _initpath() {
if [ -z "$CERT_PFX_PATH" ]; then if [ -z "$CERT_PFX_PATH" ]; then
CERT_PFX_PATH="$DOMAIN_PATH/$domain.pfx" CERT_PFX_PATH="$DOMAIN_PATH/$domain.pfx"
fi fi
if [ -z "$CERT_PKCS8_PATH" ]; then
CERT_PKCS8_PATH="$DOMAIN_PATH/$domain.pkcs8"
fi
if [ -z "$TLS_CONF" ]; then if [ -z "$TLS_CONF" ]; then
TLS_CONF="$DOMAIN_PATH/tls.valdation.conf" TLS_CONF="$DOMAIN_PATH/tls.validation.conf"
fi fi
if [ -z "$TLS_CERT" ]; then if [ -z "$TLS_CERT" ]; then
TLS_CERT="$DOMAIN_PATH/tls.valdation.cert" TLS_CERT="$DOMAIN_PATH/tls.validation.cert"
fi fi
if [ -z "$TLS_KEY" ]; then if [ -z "$TLS_KEY" ]; then
TLS_KEY="$DOMAIN_PATH/tls.valdation.key" TLS_KEY="$DOMAIN_PATH/tls.validation.key"
fi fi
if [ -z "$TLS_CSR" ]; then if [ -z "$TLS_CSR" ]; then
TLS_CSR="$DOMAIN_PATH/tls.valdation.csr" TLS_CSR="$DOMAIN_PATH/tls.validation.csr"
fi fi
} }
...@@ -1901,12 +2481,12 @@ _setApache() { ...@@ -1901,12 +2481,12 @@ _setApache() {
_debug "Backup apache config file" "$httpdconf" _debug "Backup apache config file" "$httpdconf"
if ! cp "$httpdconf" "$APACHE_CONF_BACKUP_DIR/"; then if ! cp "$httpdconf" "$APACHE_CONF_BACKUP_DIR/"; then
_err "Can not backup apache config file, so abort. Don't worry, the apache config is not changed." _err "Can not backup apache config file, so abort. Don't worry, the apache config is not changed."
_err "This might be a bug of $PROJECT_NAME , pleae report issue: $PROJECT" _err "This might be a bug of $PROJECT_NAME , please report issue: $PROJECT"
return 1 return 1
fi fi
_info "JFYI, Config file $httpdconf is backuped to $APACHE_CONF_BACKUP_DIR/$httpdconfname" _info "JFYI, Config file $httpdconf is backuped to $APACHE_CONF_BACKUP_DIR/$httpdconfname"
_info "In case there is an error that can not be restored automatically, you may try restore it yourself." _info "In case there is an error that can not be restored automatically, you may try restore it yourself."
_info "The backup file will be deleted on sucess, just forget it." _info "The backup file will be deleted on success, just forget it."
#add alias #add alias
...@@ -1960,10 +2540,230 @@ Allow from all ...@@ -1960,10 +2540,230 @@ Allow from all
return 0 return 0
} }
#find the real nginx conf file
#backup
#set the nginx conf
#returns the real nginx conf file
_setNginx() {
_d="$1"
_croot="$2"
_thumbpt="$3"
if ! _exists "nginx"; then
_err "nginx command is not found."
return 1
fi
FOUND_REAL_NGINX_CONF=""
FOUND_REAL_NGINX_CONF_LN=""
BACKUP_NGINX_CONF=""
_debug _croot "$_croot"
_start_f="$(echo "$_croot" | cut -d : -f 2)"
_debug _start_f "$_start_f"
if [ -z "$_start_f" ]; then
_debug "find start conf from nginx command"
if [ -z "$NGINX_CONF" ]; then
NGINX_CONF="$(nginx -V 2>&1 | _egrep_o "--conf-path=[^ ]* " | tr -d " ")"
_debug NGINX_CONF "$NGINX_CONF"
NGINX_CONF="$(echo "$NGINX_CONF" | cut -d = -f 2)"
_debug NGINX_CONF "$NGINX_CONF"
if [ ! -f "$NGINX_CONF" ]; then
_err "'$NGINX_CONF' doesn't exist."
NGINX_CONF=""
return 1
fi
_debug "Found nginx conf file:$NGINX_CONF"
fi
_start_f="$NGINX_CONF"
fi
_debug "Start detect nginx conf for $_d from:$_start_f"
if ! _checkConf "$_d" "$_start_f"; then
_err "Can not find conf file for domain $d"
return 1
fi
_info "Found conf file: $FOUND_REAL_NGINX_CONF"
_ln=$FOUND_REAL_NGINX_CONF_LN
_debug "_ln" "$_ln"
_lnn=$(_math $_ln + 1)
_debug _lnn "$_lnn"
_start_tag="$(sed -n "$_lnn,${_lnn}p" "$FOUND_REAL_NGINX_CONF")"
_debug "_start_tag" "$_start_tag"
if [ "$_start_tag" = "$NGINX_START" ]; then
_info "The domain $_d is already configured, skip"
FOUND_REAL_NGINX_CONF=""
return 0
fi
mkdir -p "$DOMAIN_BACKUP_PATH"
_backup_conf="$DOMAIN_BACKUP_PATH/$_d.nginx.conf"
_debug _backup_conf "$_backup_conf"
BACKUP_NGINX_CONF="$_backup_conf"
_info "Backup $FOUND_REAL_NGINX_CONF to $_backup_conf"
if ! cp "$FOUND_REAL_NGINX_CONF" "$_backup_conf"; then
_err "backup error."
FOUND_REAL_NGINX_CONF=""
return 1
fi
_info "Check the nginx conf before setting up."
if ! _exec "nginx -t" >/dev/null; then
_exec_err
return 1
fi
_info "OK, Set up nginx config file"
if ! sed -n "1,${_ln}p" "$_backup_conf" >"$FOUND_REAL_NGINX_CONF"; then
cat "$_backup_conf" >"$FOUND_REAL_NGINX_CONF"
_err "write nginx conf error, but don't worry, the file is restored to the original version."
return 1
fi
echo "$NGINX_START
location ~ \"^/\.well-known/acme-challenge/([-_a-zA-Z0-9]+)\$\" {
default_type text/plain;
return 200 \"\$1.$_thumbpt\";
}
#NGINX_START
" >>"$FOUND_REAL_NGINX_CONF"
if ! sed -n "${_lnn},99999p" "$_backup_conf" >>"$FOUND_REAL_NGINX_CONF"; then
cat "$_backup_conf" >"$FOUND_REAL_NGINX_CONF"
_err "write nginx conf error, but don't worry, the file is restored."
return 1
fi
_debug3 "Modified config:$(cat $FOUND_REAL_NGINX_CONF)"
_info "nginx conf is done, let's check it again."
if ! _exec "nginx -t" >/dev/null; then
_exec_err
_err "It seems that nginx conf was broken, let's restore."
cat "$_backup_conf" >"$FOUND_REAL_NGINX_CONF"
return 1
fi
_info "Reload nginx"
if ! _exec "nginx -s reload" >/dev/null; then
_exec_err
_err "It seems that nginx reload error, let's restore."
cat "$_backup_conf" >"$FOUND_REAL_NGINX_CONF"
return 1
fi
return 0
}
#d , conf
_checkConf() {
_d="$1"
_c_file="$2"
_debug "Start _checkConf from:$_c_file"
if [ ! -f "$2" ] && ! echo "$2" | grep '*$' >/dev/null && echo "$2" | grep '*' >/dev/null; then
_debug "wildcard"
for _w_f in $2; do
if [ -f "$_w_f" ] && _checkConf "$1" "$_w_f"; then
return 0
fi
done
#not found
return 1
elif [ -f "$2" ]; then
_debug "single"
if _isRealNginxConf "$1" "$2"; then
_debug "$2 is found."
FOUND_REAL_NGINX_CONF="$2"
return 0
fi
if cat "$2" | tr "\t" " " | grep "^ *include *.*;" >/dev/null; then
_debug "Try include files"
for included in $(cat "$2" | tr "\t" " " | grep "^ *include *.*;" | sed "s/include //" | tr -d " ;"); do
_debug "check included $included"
if _checkConf "$1" "$included"; then
return 0
fi
done
fi
return 1
else
_debug "$2 not found."
return 1
fi
return 1
}
#d , conf
_isRealNginxConf() {
_debug "_isRealNginxConf $1 $2"
if [ -f "$2" ]; then
for _fln in $(tr "\t" ' ' <"$2" | grep -n "^ *server_name.* $1" | cut -d : -f 1); do
_debug _fln "$_fln"
if [ "$_fln" ]; then
_start=$(tr "\t" ' ' <"$2" | _head_n "$_fln" | grep -n "^ *server *{" | _tail_n 1)
_debug "_start" "$_start"
_start_n=$(echo "$_start" | cut -d : -f 1)
_start_nn=$(_math $_start_n + 1)
_debug "_start_n" "$_start_n"
_debug "_start_nn" "$_start_nn"
_left="$(sed -n "${_start_nn},99999p" "$2")"
_debug2 _left "$_left"
if echo "$_left" | tr "\t" ' ' | grep -n "^ *server *{" >/dev/null; then
_end=$(echo "$_left" | tr "\t" ' ' | grep -n "^ *server *{" | _head_n 1)
_debug "_end" "$_end"
_end_n=$(echo "$_end" | cut -d : -f 1)
_debug "_end_n" "$_end_n"
_seg_n=$(echo "$_left" | sed -n "1,${_end_n}p")
else
_seg_n="$_left"
fi
_debug "_seg_n" "$_seg_n"
if [ "$(echo "$_seg_n" | _egrep_o "^ *ssl *on *;")" ] \
|| [ "$(echo "$_seg_n" | _egrep_o "listen .* ssl[ |;]")" ]; then
_debug "ssl on, skip"
else
FOUND_REAL_NGINX_CONF_LN=$_fln
_debug3 "found FOUND_REAL_NGINX_CONF_LN" "$FOUND_REAL_NGINX_CONF_LN"
return 0
fi
fi
done
fi
return 1
}
#restore all the nginx conf
_restoreNginx() {
if [ -z "$NGINX_RESTORE_VLIST" ]; then
_debug "No need to restore nginx, skip."
return
fi
_debug "_restoreNginx"
_debug "NGINX_RESTORE_VLIST" "$NGINX_RESTORE_VLIST"
for ng_entry in $(echo "$NGINX_RESTORE_VLIST" | tr "$dvsep" ' '); do
_debug "ng_entry" "$ng_entry"
_nd=$(echo "$ng_entry" | cut -d "$sep" -f 1)
_ngconf=$(echo "$ng_entry" | cut -d "$sep" -f 2)
_ngbackupconf=$(echo "$ng_entry" | cut -d "$sep" -f 3)
_info "Restoring from $_ngbackupconf to $_ngconf"
cat "$_ngbackupconf" >"$_ngconf"
done
_info "Reload nginx"
if ! _exec "nginx -s reload" >/dev/null; then
_exec_err
_err "It seems that nginx reload error, please report bug."
return 1
fi
return 0
}
_clearup() { _clearup() {
_stopserver "$serverproc" _stopserver "$serverproc"
serverproc="" serverproc=""
_restoreApache _restoreApache
_restoreNginx
_clearupdns _clearupdns
if [ -z "$DEBUG" ]; then if [ -z "$DEBUG" ]; then
rm -f "$TLS_CONF" rm -f "$TLS_CONF"
...@@ -1976,7 +2776,7 @@ _clearup() { ...@@ -1976,7 +2776,7 @@ _clearup() {
_clearupdns() { _clearupdns() {
_debug "_clearupdns" _debug "_clearupdns"
if [ "$dnsadded" != 1 ] || [ -z "$vlist" ]; then if [ "$dnsadded" != 1 ] || [ -z "$vlist" ]; then
_debug "Dns not added, skip." _debug "skip dns."
return return
fi fi
...@@ -1986,9 +2786,10 @@ _clearupdns() { ...@@ -1986,9 +2786,10 @@ _clearupdns() {
keyauthorization=$(echo "$ventry" | cut -d "$sep" -f 2) keyauthorization=$(echo "$ventry" | cut -d "$sep" -f 2)
vtype=$(echo "$ventry" | cut -d "$sep" -f 4) vtype=$(echo "$ventry" | cut -d "$sep" -f 4)
_currentRoot=$(echo "$ventry" | cut -d "$sep" -f 5) _currentRoot=$(echo "$ventry" | cut -d "$sep" -f 5)
txt="$(printf "%s" "$keyauthorization" | _digest "sha256" | _url_replace)"
_debug txt "$txt"
if [ "$keyauthorization" = "$STATE_VERIFIED" ]; then if [ "$keyauthorization" = "$STATE_VERIFIED" ]; then
_info "$d is already verified, skip $vtype." _debug "$d is already verified, skip $vtype."
continue continue
fi fi
...@@ -2019,7 +2820,7 @@ _clearupdns() { ...@@ -2019,7 +2820,7 @@ _clearupdns() {
txtdomain="_acme-challenge.$d" txtdomain="_acme-challenge.$d"
if ! $rmcommand "$txtdomain"; then if ! $rmcommand "$txtdomain" "$txt"; then
_err "Error removing txt for domain:$txtdomain" _err "Error removing txt for domain:$txtdomain"
return 1 return 1
fi fi
...@@ -2060,23 +2861,39 @@ _clearupwebbroot() { ...@@ -2060,23 +2861,39 @@ _clearupwebbroot() {
} }
_on_before_issue() { _on_before_issue() {
_chk_web_roots="$1"
_chk_main_domain="$2"
_chk_alt_domains="$3"
_chk_pre_hook="$4"
_chk_local_addr="$5"
_debug _on_before_issue _debug _on_before_issue
if _hasfield "$Le_Webroot" "$NO_VALUE"; then #run pre hook
if ! _exists "nc"; then if [ "$_chk_pre_hook" ]; then
_err "Please install netcat(nc) tools first." _info "Run pre hook:'$_chk_pre_hook'"
if ! (
cd "$DOMAIN_PATH" && eval "$_chk_pre_hook"
); then
_err "Error when run pre hook."
return 1
fi
fi
if _hasfield "$_chk_web_roots" "$NO_VALUE"; then
if ! _exists "socat"; then
_err "Please install socat tools first."
return 1 return 1
fi fi
fi fi
_debug Le_LocalAddress "$Le_LocalAddress" _debug Le_LocalAddress "$_chk_local_addr"
alldomains=$(echo "$Le_Domain,$Le_Alt" | tr ',' ' ') alldomains=$(echo "$_chk_main_domain,$_chk_alt_domains" | tr ',' ' ')
_index=1 _index=1
_currentRoot="" _currentRoot=""
_addrIndex=1 _addrIndex=1
for d in $alldomains; do for d in $alldomains; do
_debug "Check for domain" "$d" _debug "Check for domain" "$d"
_currentRoot="$(_getfield "$Le_Webroot" $_index)" _currentRoot="$(_getfield "$_chk_web_roots" $_index)"
_debug "_currentRoot" "$_currentRoot" _debug "_currentRoot" "$_currentRoot"
_index=$(_math $_index + 1) _index=$(_math $_index + 1)
_checkport="" _checkport=""
...@@ -2100,7 +2917,7 @@ _on_before_issue() { ...@@ -2100,7 +2917,7 @@ _on_before_issue() {
if [ "$_checkport" ]; then if [ "$_checkport" ]; then
_debug _checkport "$_checkport" _debug _checkport "$_checkport"
_checkaddr="$(_getfield "$Le_LocalAddress" $_addrIndex)" _checkaddr="$(_getfield "$_chk_local_addr" $_addrIndex)"
_debug _checkaddr "$_checkaddr" _debug _checkaddr "$_checkaddr"
_addrIndex="$(_math $_addrIndex + 1)" _addrIndex="$(_math $_addrIndex + 1)"
...@@ -2119,7 +2936,7 @@ _on_before_issue() { ...@@ -2119,7 +2936,7 @@ _on_before_issue() {
fi fi
done done
if _hasfield "$Le_Webroot" "apache"; then if _hasfield "$_chk_web_roots" "apache"; then
if ! _setApache; then if ! _setApache; then
_err "set up apache error. Report error to me." _err "set up apache error. Report error to me."
return 1 return 1
...@@ -2128,19 +2945,11 @@ _on_before_issue() { ...@@ -2128,19 +2945,11 @@ _on_before_issue() {
usingApache="" usingApache=""
fi fi
#run pre hook
if [ "$Le_PreHook" ]; then
_info "Run pre hook:'$Le_PreHook'"
if ! (
cd "$DOMAIN_PATH" && eval "$Le_PreHook"
); then
_err "Error when run pre hook."
return 1
fi
fi
} }
_on_issue_err() { _on_issue_err() {
_chk_post_hook="$1"
_chk_vlist="$2"
_debug _on_issue_err _debug _on_issue_err
if [ "$LOG_FILE" ]; then if [ "$LOG_FILE" ]; then
_err "Please check log file for more details: $LOG_FILE" _err "Please check log file for more details: $LOG_FILE"
...@@ -2149,29 +2958,53 @@ _on_issue_err() { ...@@ -2149,29 +2958,53 @@ _on_issue_err() {
_err "See: $_DEBUG_WIKI" _err "See: $_DEBUG_WIKI"
fi fi
if [ "$DEBUG" ] && [ "$DEBUG" -gt "0" ]; then
_debug "$(_dlg_versions)"
fi
#run the post hook #run the post hook
if [ "$Le_PostHook" ]; then if [ "$_chk_post_hook" ]; then
_info "Run post hook:'$Le_PostHook'" _info "Run post hook:'$_chk_post_hook'"
if ! ( if ! (
cd "$DOMAIN_PATH" && eval "$Le_PostHook" cd "$DOMAIN_PATH" && eval "$_chk_post_hook"
); then ); then
_err "Error when run post hook." _err "Error when run post hook."
return 1 return 1
fi fi
fi fi
#trigger the validation to flush the pending authz
_debug2 "_chk_vlist" "$_chk_vlist"
if [ "$_chk_vlist" ]; then
(
_debug2 "start to deactivate authz"
ventries=$(echo "$_chk_vlist" | tr "$dvsep" ' ')
for ventry in $ventries; do
d=$(echo "$ventry" | cut -d "$sep" -f 1)
keyauthorization=$(echo "$ventry" | cut -d "$sep" -f 2)
uri=$(echo "$ventry" | cut -d "$sep" -f 3)
vtype=$(echo "$ventry" | cut -d "$sep" -f 4)
_currentRoot=$(echo "$ventry" | cut -d "$sep" -f 5)
__trigger_validation "$uri" "$keyauthorization"
done
)
fi
if [ "$IS_RENEW" = "1" ] && _hasfield "$Le_Webroot" "dns"; then
_err "$_DNS_MANUAL_ERR"
fi
if [ "$DEBUG" ] && [ "$DEBUG" -gt "0" ]; then
_debug "$(_dlg_versions)"
fi
} }
_on_issue_success() { _on_issue_success() {
_chk_post_hook="$1"
_chk_renew_hook="$2"
_debug _on_issue_success _debug _on_issue_success
#run the post hook #run the post hook
if [ "$Le_PostHook" ]; then if [ "$_chk_post_hook" ]; then
_info "Run post hook:'$Le_PostHook'" _info "Run post hook:'$_chk_post_hook'"
if ! ( if ! (
cd "$DOMAIN_PATH" && eval "$Le_PostHook" cd "$DOMAIN_PATH" && eval "$_chk_post_hook"
); then ); then
_err "Error when run post hook." _err "Error when run post hook."
return 1 return 1
...@@ -2179,16 +3012,20 @@ _on_issue_success() { ...@@ -2179,16 +3012,20 @@ _on_issue_success() {
fi fi
#run renew hook #run renew hook
if [ "$IS_RENEW" ] && [ "$Le_RenewHook" ]; then if [ "$IS_RENEW" ] && [ "$_chk_renew_hook" ]; then
_info "Run renew hook:'$Le_RenewHook'" _info "Run renew hook:'$_chk_renew_hook'"
if ! ( if ! (
cd "$DOMAIN_PATH" && eval "$Le_RenewHook" cd "$DOMAIN_PATH" && eval "$_chk_renew_hook"
); then ); then
_err "Error when run renew hook." _err "Error when run renew hook."
return 1 return 1
fi fi
fi fi
if _hasfield "$Le_Webroot" "dns"; then
_err "$_DNS_MANUAL_WARN"
fi
} }
updateaccount() { updateaccount() {
...@@ -2206,11 +3043,16 @@ __calcAccountKeyHash() { ...@@ -2206,11 +3043,16 @@ __calcAccountKeyHash() {
[ -f "$ACCOUNT_KEY_PATH" ] && _digest sha256 <"$ACCOUNT_KEY_PATH" [ -f "$ACCOUNT_KEY_PATH" ] && _digest sha256 <"$ACCOUNT_KEY_PATH"
} }
__calc_account_thumbprint() {
printf "%s" "$jwk" | tr -d ' ' | _digest "sha256" | _url_replace
}
#keylength #keylength
_regAccount() { _regAccount() {
_initpath _initpath
_reg_length="$1" _reg_length="$1"
_debug3 _regAccount "$_regAccount"
mkdir -p "$CA_DIR"
if [ ! -f "$ACCOUNT_KEY_PATH" ] && [ -f "$_OLD_ACCOUNT_KEY" ]; then if [ ! -f "$ACCOUNT_KEY_PATH" ] && [ -f "$_OLD_ACCOUNT_KEY" ]; then
_info "mv $_OLD_ACCOUNT_KEY to $ACCOUNT_KEY_PATH" _info "mv $_OLD_ACCOUNT_KEY to $ACCOUNT_KEY_PATH"
mv "$_OLD_ACCOUNT_KEY" "$ACCOUNT_KEY_PATH" mv "$_OLD_ACCOUNT_KEY" "$ACCOUNT_KEY_PATH"
...@@ -2231,72 +3073,109 @@ _regAccount() { ...@@ -2231,72 +3073,109 @@ _regAccount() {
if ! _calcjwk "$ACCOUNT_KEY_PATH"; then if ! _calcjwk "$ACCOUNT_KEY_PATH"; then
return 1 return 1
fi fi
_initAPI
_reg_res="$ACME_NEW_ACCOUNT_RES"
regjson='{"resource": "'$_reg_res'", "terms-of-service-agreed": true, "agreement": "'$ACME_AGREEMENT'"}'
if [ "$ACCOUNT_EMAIL" ]; then
regjson='{"resource": "'$_reg_res'", "contact": ["mailto: '$ACCOUNT_EMAIL'"], "terms-of-service-agreed": true, "agreement": "'$ACME_AGREEMENT'"}'
fi
_updateTos="" _info "Registering account"
_reg_res="new-reg"
while true; do
_debug AGREEMENT "$AGREEMENT"
regjson='{"resource": "'$_reg_res'", "agreement": "'$AGREEMENT'"}' if ! _send_signed_request "${ACME_NEW_ACCOUNT}" "$regjson"; then
_err "Register account Error: $response"
return 1
fi
if [ "$ACCOUNT_EMAIL" ]; then if [ "$code" = "" ] || [ "$code" = '201' ]; then
regjson='{"resource": "'$_reg_res'", "contact": ["mailto: '$ACCOUNT_EMAIL'"], "agreement": "'$AGREEMENT'"}' echo "$response" >"$ACCOUNT_JSON_PATH"
fi _info "Registered"
elif [ "$code" = '409' ]; then
_info "Already registered"
else
_err "Register account Error: $response"
return 1
fi
if [ -z "$_updateTos" ]; then _accUri="$(echo "$responseHeaders" | grep "^Location:" | _head_n 1 | cut -d ' ' -f 2 | tr -d "\r\n")"
_info "Registering account" _debug "_accUri" "$_accUri"
_savecaconf "ACCOUNT_URL" "$_accUri"
if ! _send_signed_request "$API/acme/new-reg" "$regjson"; then echo "$response" >"$ACCOUNT_JSON_PATH"
_err "Register account Error: $response" CA_KEY_HASH="$(__calcAccountKeyHash)"
return 1 _debug "Calc CA_KEY_HASH" "$CA_KEY_HASH"
fi _savecaconf CA_KEY_HASH "$CA_KEY_HASH"
if [ "$code" = "" ] || [ "$code" = '201' ]; then if [ "$code" = '403' ]; then
echo "$response" >"$ACCOUNT_JSON_PATH" _err "It seems that the account key is already deactivated, please use a new account key."
_info "Registered" return 1
elif [ "$code" = '409' ]; then fi
_info "Already registered"
else ACCOUNT_THUMBPRINT="$(__calc_account_thumbprint)"
_err "Register account Error: $response" _info "ACCOUNT_THUMBPRINT" "$ACCOUNT_THUMBPRINT"
return 1
fi }
#Implement deactivate account
deactivateaccount() {
_initpath
if [ ! -f "$ACCOUNT_KEY_PATH" ] && [ -f "$_OLD_ACCOUNT_KEY" ]; then
_info "mv $_OLD_ACCOUNT_KEY to $ACCOUNT_KEY_PATH"
mv "$_OLD_ACCOUNT_KEY" "$ACCOUNT_KEY_PATH"
fi
if [ ! -f "$ACCOUNT_JSON_PATH" ] && [ -f "$_OLD_ACCOUNT_JSON" ]; then
_info "mv $_OLD_ACCOUNT_JSON to $ACCOUNT_JSON_PATH"
mv "$_OLD_ACCOUNT_JSON" "$ACCOUNT_JSON_PATH"
fi
if [ ! -f "$ACCOUNT_KEY_PATH" ]; then
_err "Account key is not found at: $ACCOUNT_KEY_PATH"
return 1
fi
_accUri=$(_readcaconf "ACCOUNT_URL")
_debug _accUri "$_accUri"
if [ -z "$_accUri" ]; then
_err "The account url is empty, please run '--update-account' first to update the account info first,"
_err "Then try again."
return 1
fi
_accUri="$(echo "$responseHeaders" | grep "^Location:" | _head_n 1 | cut -d ' ' -f 2 | tr -d "\r\n")" if ! _calcjwk "$ACCOUNT_KEY_PATH"; then
_debug "_accUri" "$_accUri" return 1
fi
_initAPI
_tos="$(echo "$responseHeaders" | grep "^Link:.*rel=\"terms-of-service\"" | _head_n 1 | _egrep_o "<.*>" | tr -d '<>')" if _send_signed_request "$_accUri" "{\"resource\": \"reg\", \"status\":\"deactivated\"}" && _contains "$response" '"deactivated"'; then
_debug "_tos" "$_tos" _info "Deactivate account success for $_accUri."
if [ -z "$_tos" ]; then _accid=$(echo "$response" | _egrep_o "\"id\" *: *[^,]*," | cut -d : -f 2 | tr -d ' ,')
_debug "Use default tos: $DEFAULT_AGREEMENT" elif [ "$code" = "403" ]; then
_tos="$DEFAULT_AGREEMENT" _info "The account is already deactivated."
fi _accid=$(_getfield "$_accUri" "999" "/")
if [ "$_tos" != "$AGREEMENT" ]; then else
_updateTos=1 _err "Deactivate: account failed for $_accUri."
AGREEMENT="$_tos" return 1
_reg_res="reg" fi
continue
fi
_debug "Account id: $_accid"
if [ "$_accid" ]; then
_deactivated_account_path="$CA_DIR/deactivated/$_accid"
_debug _deactivated_account_path "$_deactivated_account_path"
if mkdir -p "$_deactivated_account_path"; then
_info "Moving deactivated account info to $_deactivated_account_path/"
mv "$CA_CONF" "$_deactivated_account_path/"
mv "$ACCOUNT_JSON_PATH" "$_deactivated_account_path/"
mv "$ACCOUNT_KEY_PATH" "$_deactivated_account_path/"
else else
_debug "Update tos: $_tos" _err "Can not create dir: $_deactivated_account_path, try to remove the deactivated account key."
if ! _send_signed_request "$_accUri" "$regjson"; then rm -f "$CA_CONF"
_err "Update tos error." rm -f "$ACCOUNT_JSON_PATH"
return 1 rm -f "$ACCOUNT_KEY_PATH"
fi
if [ "$code" = '202' ]; then
_info "Update success."
CA_KEY_HASH="$(__calcAccountKeyHash)"
_debug "Calc CA_KEY_HASH" "$CA_KEY_HASH"
_savecaconf CA_KEY_HASH "$CA_KEY_HASH"
else
_err "Update account error."
return 1
fi
fi fi
return 0 fi
done
} }
# domain folder file # domain folder file
...@@ -2330,15 +3209,21 @@ _findHook() { ...@@ -2330,15 +3209,21 @@ _findHook() {
__get_domain_new_authz() { __get_domain_new_authz() {
_gdnd="$1" _gdnd="$1"
_info "Getting new-authz for domain" "$_gdnd" _info "Getting new-authz for domain" "$_gdnd"
_initAPI
_Max_new_authz_retry_times=5 _Max_new_authz_retry_times=5
_authz_i=0 _authz_i=0
while [ "$_authz_i" -lt "$_Max_new_authz_retry_times" ]; do while [ "$_authz_i" -lt "$_Max_new_authz_retry_times" ]; do
_debug "Try new-authz for the $_authz_i time." _debug "Try new-authz for the $_authz_i time."
if ! _send_signed_request "$API/acme/new-authz" "{\"resource\": \"new-authz\", \"identifier\": {\"type\": \"dns\", \"value\": \"$(_idn "$_gdnd")\"}}"; then if ! _send_signed_request "${ACME_NEW_AUTHZ}" "{\"resource\": \"new-authz\", \"identifier\": {\"type\": \"dns\", \"value\": \"$(_idn "$_gdnd")\"}}"; then
_err "Can not get domain new authz." _err "Can not get domain new authz."
return 1 return 1
fi fi
if _contains "$response" "No registration exists matching provided key"; then
_err "It seems there is an error, but it's recovered now, please try again."
_err "If you see this message for a second time, please report bug: $(__green "$PROJECT")"
_clearcaconf "CA_KEY_HASH"
break
fi
if ! _contains "$response" "An error occurred while processing your request"; then if ! _contains "$response" "An error occurred while processing your request"; then
_info "The new-authz request is ok." _info "The new-authz request is ok."
break break
...@@ -2359,43 +3244,64 @@ __get_domain_new_authz() { ...@@ -2359,43 +3244,64 @@ __get_domain_new_authz() {
} }
#webroot, domain domainlist keylength #uri keyAuthorization
__trigger_validation() {
_debug2 "tigger domain validation."
_t_url="$1"
_debug2 _t_url "$_t_url"
_t_key_authz="$2"
_debug2 _t_key_authz "$_t_key_authz"
_send_signed_request "$_t_url" "{\"resource\": \"challenge\", \"keyAuthorization\": \"$_t_key_authz\"}"
}
#webroot, domain domainlist keylength
issue() { issue() {
if [ -z "$2" ]; then if [ -z "$2" ]; then
_usage "Usage: $PROJECT_ENTRY --issue -d a.com -w /path/to/webroot/a.com/ " _usage "Usage: $PROJECT_ENTRY --issue -d a.com -w /path/to/webroot/a.com/ "
return 1 return 1
fi fi
Le_Webroot="$1" if [ -z "$1" ]; then
Le_Domain="$2" _usage "Please specify at least one validation method: '--webroot', '--standalone', '--apache', '--nginx' or '--dns' etc."
Le_Alt="$3" return 1
Le_Keylength="$4" fi
Le_RealCertPath="$5" _web_roots="$1"
Le_RealKeyPath="$6" _main_domain="$2"
Le_RealCACertPath="$7" _alt_domains="$3"
Le_ReloadCmd="$8" if _contains "$_main_domain" ","; then
Le_RealFullChainPath="$9" _main_domain=$(echo "$2,$3" | cut -d , -f 1)
Le_PreHook="${10}" _alt_domains=$(echo "$2,$3" | cut -d , -f 2- | sed "s/,${NO_VALUE}$//")
Le_PostHook="${11}" fi
Le_RenewHook="${12}" _key_length="$4"
Le_LocalAddress="${13}" _real_cert="$5"
_real_key="$6"
_real_ca="$7"
_reload_cmd="$8"
_real_fullchain="$9"
_pre_hook="${10}"
_post_hook="${11}"
_renew_hook="${12}"
_local_addr="${13}"
#remove these later. #remove these later.
if [ "$Le_Webroot" = "dns-cf" ]; then if [ "$_web_roots" = "dns-cf" ]; then
Le_Webroot="dns_cf" _web_roots="dns_cf"
fi fi
if [ "$Le_Webroot" = "dns-dp" ]; then if [ "$_web_roots" = "dns-dp" ]; then
Le_Webroot="dns_dp" _web_roots="dns_dp"
fi fi
if [ "$Le_Webroot" = "dns-cx" ]; then if [ "$_web_roots" = "dns-cx" ]; then
Le_Webroot="dns_cx" _web_roots="dns_cx"
fi fi
_debug "Using api: $API"
if [ ! "$IS_RENEW" ]; then if [ ! "$IS_RENEW" ]; then
_initpath "$Le_Domain" "$Le_Keylength" _initpath "$_main_domain" "$_key_length"
mkdir -p "$DOMAIN_PATH" mkdir -p "$DOMAIN_PATH"
fi fi
_debug "Using ACME_DIRECTORY: $ACME_DIRECTORY"
_initAPI
if [ -f "$DOMAIN_CONF" ]; then if [ -f "$DOMAIN_CONF" ]; then
Le_NextRenewTime=$(_readdomainconf Le_NextRenewTime) Le_NextRenewTime=$(_readdomainconf Le_NextRenewTime)
_debug Le_NextRenewTime "$Le_NextRenewTime" _debug Le_NextRenewTime "$Le_NextRenewTime"
...@@ -2404,7 +3310,7 @@ issue() { ...@@ -2404,7 +3310,7 @@ issue() {
_debug _saved_domain "$_saved_domain" _debug _saved_domain "$_saved_domain"
_saved_alt=$(_readdomainconf Le_Alt) _saved_alt=$(_readdomainconf Le_Alt)
_debug _saved_alt "$_saved_alt" _debug _saved_alt "$_saved_alt"
if [ "$_saved_domain,$_saved_alt" = "$Le_Domain,$Le_Alt" ]; then if [ "$_saved_domain,$_saved_alt" = "$_main_domain,$_alt_domains" ]; then
_info "Domains not changed." _info "Domains not changed."
_info "Skip, Next renewal time is: $(__green "$(_readdomainconf Le_NextRenewTimeStr)")" _info "Skip, Next renewal time is: $(__green "$(_readdomainconf Le_NextRenewTimeStr)")"
_info "Add '$(__red '--force')' to force to renew." _info "Add '$(__red '--force')' to force to renew."
...@@ -2415,32 +3321,32 @@ issue() { ...@@ -2415,32 +3321,32 @@ issue() {
fi fi
fi fi
_savedomainconf "Le_Domain" "$Le_Domain" _savedomainconf "Le_Domain" "$_main_domain"
_savedomainconf "Le_Alt" "$Le_Alt" _savedomainconf "Le_Alt" "$_alt_domains"
_savedomainconf "Le_Webroot" "$Le_Webroot" _savedomainconf "Le_Webroot" "$_web_roots"
_savedomainconf "Le_PreHook" "$Le_PreHook" _savedomainconf "Le_PreHook" "$_pre_hook"
_savedomainconf "Le_PostHook" "$Le_PostHook" _savedomainconf "Le_PostHook" "$_post_hook"
_savedomainconf "Le_RenewHook" "$Le_RenewHook" _savedomainconf "Le_RenewHook" "$_renew_hook"
if [ "$Le_LocalAddress" ]; then if [ "$_local_addr" ]; then
_savedomainconf "Le_LocalAddress" "$Le_LocalAddress" _savedomainconf "Le_LocalAddress" "$_local_addr"
else else
_cleardomainconf "Le_LocalAddress" _cleardomainconf "Le_LocalAddress"
fi fi
Le_API="$API" Le_API="$ACME_DIRECTORY"
_savedomainconf "Le_API" "$Le_API" _savedomainconf "Le_API" "$Le_API"
if [ "$Le_Alt" = "$NO_VALUE" ]; then if [ "$_alt_domains" = "$NO_VALUE" ]; then
Le_Alt="" _alt_domains=""
fi fi
if [ "$Le_Keylength" = "$NO_VALUE" ]; then if [ "$_key_length" = "$NO_VALUE" ]; then
Le_Keylength="" _key_length=""
fi fi
if ! _on_before_issue; then if ! _on_before_issue "$_web_roots" "$_main_domain" "$_alt_domains" "$_pre_hook" "$_local_addr"; then
_err "_on_before_issue." _err "_on_before_issue."
return 1 return 1
fi fi
...@@ -2450,7 +3356,7 @@ issue() { ...@@ -2450,7 +3356,7 @@ issue() {
if [ -z "$_saved_account_key_hash" ] || [ "$_saved_account_key_hash" != "$(__calcAccountKeyHash)" ]; then if [ -z "$_saved_account_key_hash" ] || [ "$_saved_account_key_hash" != "$(__calcAccountKeyHash)" ]; then
if ! _regAccount "$_accountkeylength"; then if ! _regAccount "$_accountkeylength"; then
_on_issue_err _on_issue_err "$_post_hook"
return 1 return 1
fi fi
else else
...@@ -2462,37 +3368,38 @@ issue() { ...@@ -2462,37 +3368,38 @@ issue() {
else else
_key=$(_readdomainconf Le_Keylength) _key=$(_readdomainconf Le_Keylength)
_debug "Read key length:$_key" _debug "Read key length:$_key"
if [ ! -f "$CERT_KEY_PATH" ] || [ "$Le_Keylength" != "$_key" ]; then if [ ! -f "$CERT_KEY_PATH" ] || [ "$_key_length" != "$_key" ] || [ "$Le_ForceNewDomainKey" = "1" ]; then
if ! createDomainKey "$Le_Domain" "$Le_Keylength"; then if ! createDomainKey "$_main_domain" "$_key_length"; then
_err "Create domain key error." _err "Create domain key error."
_clearup _clearup
_on_issue_err _on_issue_err "$_post_hook"
return 1 return 1
fi fi
fi fi
if ! _createcsr "$Le_Domain" "$Le_Alt" "$CERT_KEY_PATH" "$CSR_PATH" "$DOMAIN_SSL_CONF"; then if ! _createcsr "$_main_domain" "$_alt_domains" "$CERT_KEY_PATH" "$CSR_PATH" "$DOMAIN_SSL_CONF"; then
_err "Create CSR error." _err "Create CSR error."
_clearup _clearup
_on_issue_err _on_issue_err "$_post_hook"
return 1 return 1
fi fi
fi fi
_savedomainconf "Le_Keylength" "$Le_Keylength" _savedomainconf "Le_Keylength" "$_key_length"
vlist="$Le_Vlist" vlist="$Le_Vlist"
_info "Getting domain auth token for each domain" _info "Getting domain auth token for each domain"
sep='#' sep='#'
dvsep=','
if [ -z "$vlist" ]; then if [ -z "$vlist" ]; then
alldomains=$(echo "$Le_Domain,$Le_Alt" | tr ',' ' ') alldomains=$(echo "$_main_domain,$_alt_domains" | tr ',' ' ')
_index=1 _index=1
_currentRoot="" _currentRoot=""
for d in $alldomains; do for d in $alldomains; do
_info "Getting webroot for domain" "$d" _info "Getting webroot for domain" "$d"
_w="$(echo $Le_Webroot | cut -d , -f $_index)" _w="$(echo $_web_roots | cut -d , -f $_index)"
_info _w "$_w" _debug _w "$_w"
if [ "$_w" ]; then if [ "$_w" ]; then
_currentRoot="$_w" _currentRoot="$_w"
fi fi
...@@ -2510,13 +3417,12 @@ issue() { ...@@ -2510,13 +3417,12 @@ issue() {
if ! __get_domain_new_authz "$d"; then if ! __get_domain_new_authz "$d"; then
_clearup _clearup
_on_issue_err _on_issue_err "$_post_hook"
return 1 return 1
fi fi
if [ -z "$thumbprint" ]; then if [ -z "$thumbprint" ]; then
accountkey_json=$(printf "%s" "$jwk" | tr -d ' ') thumbprint="$(__calc_account_thumbprint)"
thumbprint=$(printf "%s" "$accountkey_json" | _digest "sha256" | _urlencode)
fi fi
entry="$(printf "%s\n" "$response" | _egrep_o '[^\{]*"type":"'$vtype'"[^\}]*')" entry="$(printf "%s\n" "$response" | _egrep_o '[^\{]*"type":"'$vtype'"[^\}]*')"
...@@ -2524,20 +3430,20 @@ issue() { ...@@ -2524,20 +3430,20 @@ issue() {
if [ -z "$entry" ]; then if [ -z "$entry" ]; then
_err "Error, can not get domain token $d" _err "Error, can not get domain token $d"
_clearup _clearup
_on_issue_err _on_issue_err "$_post_hook"
return 1 return 1
fi fi
token="$(printf "%s\n" "$entry" | _egrep_o '"token":"[^"]*' | cut -d : -f 2 | tr -d '"')" token="$(printf "%s\n" "$entry" | _egrep_o '"token":"[^"]*' | cut -d : -f 2 | tr -d '"')"
_debug token "$token" _debug token "$token"
uri="$(printf "%s\n" "$entry" | _egrep_o '"uri":"[^"]*' | cut -d : -f 2,3 | tr -d '"')" uri="$(printf "%s\n" "$entry" | _egrep_o '"uri":"[^"]*' | cut -d '"' -f 4)"
_debug uri "$uri" _debug uri "$uri"
keyauthorization="$token.$thumbprint" keyauthorization="$token.$thumbprint"
_debug keyauthorization "$keyauthorization" _debug keyauthorization "$keyauthorization"
if printf "%s" "$response" | grep '"status":"valid"' >/dev/null 2>&1; then if printf "%s" "$response" | grep '"status":"valid"' >/dev/null 2>&1; then
_info "$d is already verified, skip." _debug "$d is already verified, skip."
keyauthorization="$STATE_VERIFIED" keyauthorization="$STATE_VERIFIED"
_debug keyauthorization "$keyauthorization" _debug keyauthorization "$keyauthorization"
fi fi
...@@ -2545,13 +3451,13 @@ issue() { ...@@ -2545,13 +3451,13 @@ issue() {
dvlist="$d$sep$keyauthorization$sep$uri$sep$vtype$sep$_currentRoot" dvlist="$d$sep$keyauthorization$sep$uri$sep$vtype$sep$_currentRoot"
_debug dvlist "$dvlist" _debug dvlist "$dvlist"
vlist="$vlist$dvlist," vlist="$vlist$dvlist$dvsep"
done done
_debug vlist "$vlist"
#add entry #add entry
dnsadded="" dnsadded=""
ventries=$(echo "$vlist" | tr ',' ' ') ventries=$(echo "$vlist" | tr "$dvsep" ' ')
for ventry in $ventries; do for ventry in $ventries; do
d=$(echo "$ventry" | cut -d "$sep" -f 1) d=$(echo "$ventry" | cut -d "$sep" -f 1)
keyauthorization=$(echo "$ventry" | cut -d "$sep" -f 2) keyauthorization=$(echo "$ventry" | cut -d "$sep" -f 2)
...@@ -2559,7 +3465,7 @@ issue() { ...@@ -2559,7 +3465,7 @@ issue() {
_currentRoot=$(echo "$ventry" | cut -d "$sep" -f 5) _currentRoot=$(echo "$ventry" | cut -d "$sep" -f 5)
if [ "$keyauthorization" = "$STATE_VERIFIED" ]; then if [ "$keyauthorization" = "$STATE_VERIFIED" ]; then
_info "$d is already verified, skip $vtype." _debug "$d is already verified, skip $vtype."
continue continue
fi fi
...@@ -2567,7 +3473,7 @@ issue() { ...@@ -2567,7 +3473,7 @@ issue() {
dnsadded='0' dnsadded='0'
txtdomain="_acme-challenge.$d" txtdomain="_acme-challenge.$d"
_debug txtdomain "$txtdomain" _debug txtdomain "$txtdomain"
txt="$(printf "%s" "$keyauthorization" | _digest "sha256" | _urlencode)" txt="$(printf "%s" "$keyauthorization" | _digest "sha256" | _url_replace)"
_debug txt "$txt" _debug txt "$txt"
d_api="$(_findHook "$d" dnsapi "$_currentRoot")" d_api="$(_findHook "$d" dnsapi "$_currentRoot")"
...@@ -2577,11 +3483,11 @@ issue() { ...@@ -2577,11 +3483,11 @@ issue() {
if [ "$d_api" ]; then if [ "$d_api" ]; then
_info "Found domain api file: $d_api" _info "Found domain api file: $d_api"
else else
_err "Add the following TXT record:" _info "$(__red "Add the following TXT record:")"
_err "Domain: '$(__green "$txtdomain")'" _info "$(__red "Domain: '$(__green "$txtdomain")'")"
_err "TXT value: '$(__green "$txt")'" _info "$(__red "TXT value: '$(__green "$txt")'")"
_err "Please be aware that you prepend _acme-challenge. before your domain" _info "$(__red "Please be aware that you prepend _acme-challenge. before your domain")"
_err "so the resulting subdomain will be: $txtdomain" _info "$(__red "so the resulting subdomain will be: $txtdomain")"
continue continue
fi fi
...@@ -2605,7 +3511,7 @@ issue() { ...@@ -2605,7 +3511,7 @@ issue() {
if [ "$?" != "0" ]; then if [ "$?" != "0" ]; then
_clearup _clearup
_on_issue_err _on_issue_err "$_post_hook" "$vlist"
return 1 return 1
fi fi
dnsadded='1' dnsadded='1'
...@@ -2617,7 +3523,7 @@ issue() { ...@@ -2617,7 +3523,7 @@ issue() {
_debug "Dns record not added yet, so, save to $DOMAIN_CONF and exit." _debug "Dns record not added yet, so, save to $DOMAIN_CONF and exit."
_err "Please add the TXT records to the domains, and retry again." _err "Please add the TXT records to the domains, and retry again."
_clearup _clearup
_on_issue_err _on_issue_err "$_post_hook"
return 1 return 1
fi fi
...@@ -2634,10 +3540,11 @@ issue() { ...@@ -2634,10 +3540,11 @@ issue() {
_sleep "$Le_DNSSleep" _sleep "$Le_DNSSleep"
fi fi
NGINX_RESTORE_VLIST=""
_debug "ok, let's start to verify" _debug "ok, let's start to verify"
_ncIndex=1 _ncIndex=1
ventries=$(echo "$vlist" | tr ',' ' ') ventries=$(echo "$vlist" | tr "$dvsep" ' ')
for ventry in $ventries; do for ventry in $ventries; do
d=$(echo "$ventry" | cut -d "$sep" -f 1) d=$(echo "$ventry" | cut -d "$sep" -f 1)
keyauthorization=$(echo "$ventry" | cut -d "$sep" -f 2) keyauthorization=$(echo "$ventry" | cut -d "$sep" -f 2)
...@@ -2662,18 +3569,37 @@ issue() { ...@@ -2662,18 +3569,37 @@ issue() {
if [ "$vtype" = "$VTYPE_HTTP" ]; then if [ "$vtype" = "$VTYPE_HTTP" ]; then
if [ "$_currentRoot" = "$NO_VALUE" ]; then if [ "$_currentRoot" = "$NO_VALUE" ]; then
_info "Standalone mode server" _info "Standalone mode server"
_ncaddr="$(_getfield "$Le_LocalAddress" "$_ncIndex")" _ncaddr="$(_getfield "$_local_addr" "$_ncIndex")"
_ncIndex="$(_math $_ncIndex + 1)" _ncIndex="$(_math $_ncIndex + 1)"
_startserver "$keyauthorization" "$_ncaddr" & _startserver "$keyauthorization" "$_ncaddr"
if [ "$?" != "0" ]; then if [ "$?" != "0" ]; then
_clearup _clearup
_on_issue_err _on_issue_err "$_post_hook" "$vlist"
return 1 return 1
fi fi
serverproc="$!"
sleep 1 sleep 1
_debug serverproc "$serverproc" _debug serverproc "$serverproc"
elif [ "$_currentRoot" = "$MODE_STATELESS" ]; then
_info "Stateless mode for domain:$d"
_sleep 1
elif _startswith "$_currentRoot" "$NGINX"; then
_info "Nginx mode for domain:$d"
#set up nginx server
FOUND_REAL_NGINX_CONF=""
BACKUP_NGINX_CONF=""
if ! _setNginx "$d" "$_currentRoot" "$thumbprint"; then
_clearup
_on_issue_err "$_post_hook" "$vlist"
return 1
fi
if [ "$FOUND_REAL_NGINX_CONF" ]; then
_realConf="$FOUND_REAL_NGINX_CONF"
_backup="$BACKUP_NGINX_CONF"
_debug _realConf "$_realConf"
NGINX_RESTORE_VLIST="$d$sep$_realConf$sep$_backup$dvsep$NGINX_RESTORE_VLIST"
fi
_sleep 1
else else
if [ "$_currentRoot" = "apache" ]; then if [ "$_currentRoot" = "apache" ]; then
wellknown_path="$ACME_DIR" wellknown_path="$ACME_DIR"
...@@ -2698,16 +3624,19 @@ issue() { ...@@ -2698,16 +3624,19 @@ issue() {
_err "$d:Can not write token to file : $wellknown_path/$token" _err "$d:Can not write token to file : $wellknown_path/$token"
_clearupwebbroot "$_currentRoot" "$removelevel" "$token" _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
_clearup _clearup
_on_issue_err _on_issue_err "$_post_hook" "$vlist"
return 1 return 1
fi fi
if [ ! "$usingApache" ]; then if [ ! "$usingApache" ]; then
if webroot_owner=$(_stat "$_currentRoot"); then if webroot_owner=$(_stat "$_currentRoot"); then
_debug "Changing owner/group of .well-known to $webroot_owner" _debug "Changing owner/group of .well-known to $webroot_owner"
chown -R "$webroot_owner" "$_currentRoot/.well-known" if ! _exec "chown -R \"$webroot_owner\" \"$_currentRoot/.well-known\""; then
_debug "$(cat "$_EXEC_TEMP_ERR")"
_exec_err >/dev/null 2>&1
fi
else else
_debug "not chaning owner/group of webroot" _debug "not changing owner/group of webroot"
fi fi
fi fi
...@@ -2737,22 +3666,22 @@ issue() { ...@@ -2737,22 +3666,22 @@ issue() {
_SAN_B="$_x.$_y.acme.invalid" _SAN_B="$_x.$_y.acme.invalid"
_debug2 _SAN_B "$_SAN_B" _debug2 _SAN_B "$_SAN_B"
_ncaddr="$(_getfield "$Le_LocalAddress" "$_ncIndex")" _ncaddr="$(_getfield "$_local_addr" "$_ncIndex")"
_ncIndex="$(_math "$_ncIndex" + 1)" _ncIndex="$(_math "$_ncIndex" + 1)"
if ! _starttlsserver "$_SAN_B" "$_SAN_A" "$Le_TLSPort" "$keyauthorization" "$_ncaddr"; then if ! _starttlsserver "$_SAN_B" "$_SAN_A" "$Le_TLSPort" "$keyauthorization" "$_ncaddr"; then
_err "Start tls server error." _err "Start tls server error."
_clearupwebbroot "$_currentRoot" "$removelevel" "$token" _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
_clearup _clearup
_on_issue_err _on_issue_err "$_post_hook" "$vlist"
return 1 return 1
fi fi
fi fi
if ! _send_signed_request "$uri" "{\"resource\": \"challenge\", \"keyAuthorization\": \"$keyauthorization\"}"; then if ! __trigger_validation "$uri" "$keyauthorization"; then
_err "$d:Can not get challenge: $response" _err "$d:Can not get challenge: $response"
_clearupwebbroot "$_currentRoot" "$removelevel" "$token" _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
_clearup _clearup
_on_issue_err _on_issue_err "$_post_hook" "$vlist"
return 1 return 1
fi fi
...@@ -2760,7 +3689,7 @@ issue() { ...@@ -2760,7 +3689,7 @@ issue() {
_err "$d:Challenge error: $response" _err "$d:Challenge error: $response"
_clearupwebbroot "$_currentRoot" "$removelevel" "$token" _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
_clearup _clearup
_on_issue_err _on_issue_err "$_post_hook" "$vlist"
return 1 return 1
fi fi
...@@ -2775,7 +3704,7 @@ issue() { ...@@ -2775,7 +3704,7 @@ issue() {
_err "$d:Timeout" _err "$d:Timeout"
_clearupwebbroot "$_currentRoot" "$removelevel" "$token" _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
_clearup _clearup
_on_issue_err _on_issue_err "$_post_hook" "$vlist"
return 1 return 1
fi fi
...@@ -2787,7 +3716,7 @@ issue() { ...@@ -2787,7 +3716,7 @@ issue() {
_err "$d:Verify error:$response" _err "$d:Verify error:$response"
_clearupwebbroot "$_currentRoot" "$removelevel" "$token" _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
_clearup _clearup
_on_issue_err _on_issue_err "$_post_hook" "$vlist"
return 1 return 1
fi fi
_debug2 original "$response" _debug2 original "$response"
...@@ -2822,7 +3751,7 @@ issue() { ...@@ -2822,7 +3751,7 @@ issue() {
fi fi
_clearupwebbroot "$_currentRoot" "$removelevel" "$token" _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
_clearup _clearup
_on_issue_err _on_issue_err "$_post_hook" "$vlist"
return 1 return 1
fi fi
...@@ -2832,7 +3761,7 @@ issue() { ...@@ -2832,7 +3761,7 @@ issue() {
_err "$d:Verify error:$response" _err "$d:Verify error:$response"
_clearupwebbroot "$_currentRoot" "$removelevel" "$token" _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
_clearup _clearup
_on_issue_err _on_issue_err "$_post_hook" "$vlist"
return 1 return 1
fi fi
...@@ -2842,16 +3771,17 @@ issue() { ...@@ -2842,16 +3771,17 @@ issue() {
_clearup _clearup
_info "Verify finished, start to sign." _info "Verify finished, start to sign."
der="$(_getfile "${CSR_PATH}" "${BEGIN_CSR}" "${END_CSR}" | tr -d "\r\n" | _urlencode)" der="$(_getfile "${CSR_PATH}" "${BEGIN_CSR}" "${END_CSR}" | tr -d "\r\n" | _url_replace)"
if ! _send_signed_request "$API/acme/new-cert" "{\"resource\": \"new-cert\", \"csr\": \"$der\"}" "needbase64"; then if ! _send_signed_request "${ACME_NEW_ORDER}" "{\"resource\": \"$ACME_NEW_ORDER_RES\", \"csr\": \"$der\"}" "needbase64"; then
_err "Sign failed." _err "Sign failed."
_on_issue_err _on_issue_err "$_post_hook"
return 1 return 1
fi fi
_rcert="$response" _rcert="$response"
Le_LinkCert="$(grep -i '^Location.*$' "$HTTP_HEADER" | _head_n 1 | tr -d "\r\n" | cut -d " " -f 2)" Le_LinkCert="$(grep -i '^Location.*$' "$HTTP_HEADER" | _head_n 1 | tr -d "\r\n" | cut -d " " -f 2)"
_debug "Le_LinkCert" "$Le_LinkCert"
_savedomainconf "Le_LinkCert" "$Le_LinkCert" _savedomainconf "Le_LinkCert" "$Le_LinkCert"
if [ "$Le_LinkCert" ]; then if [ "$Le_LinkCert" ]; then
...@@ -2859,7 +3789,7 @@ issue() { ...@@ -2859,7 +3789,7 @@ issue() {
#if ! _get "$Le_LinkCert" | _base64 "multiline" >> "$CERT_PATH" ; then #if ! _get "$Le_LinkCert" | _base64 "multiline" >> "$CERT_PATH" ; then
# _debug "Get cert failed. Let's try last response." # _debug "Get cert failed. Let's try last response."
# printf -- "%s" "$_rcert" | _dbase64 "multiline" | _base64 "multiline" >> "$CERT_PATH" # printf -- "%s" "$_rcert" | _dbase64 "multiline" | _base64 "multiline" >> "$CERT_PATH"
#fi #fi
if ! printf -- "%s" "$_rcert" | _dbase64 "multiline" | _base64 "multiline" >>"$CERT_PATH"; then if ! printf -- "%s" "$_rcert" | _dbase64 "multiline" | _base64 "multiline" >>"$CERT_PATH"; then
...@@ -2888,7 +3818,7 @@ issue() { ...@@ -2888,7 +3818,7 @@ issue() {
if [ -z "$Le_LinkCert" ]; then if [ -z "$Le_LinkCert" ]; then
response="$(echo "$response" | _dbase64 "multiline" | _normalizeJson)" response="$(echo "$response" | _dbase64 "multiline" | _normalizeJson)"
_err "Sign failed: $(echo "$response" | _egrep_o '"detail":"[^"]*"')" _err "Sign failed: $(echo "$response" | _egrep_o '"detail":"[^"]*"')"
_on_issue_err _on_issue_err "$_post_hook"
return 1 return 1
fi fi
...@@ -2896,18 +3826,37 @@ issue() { ...@@ -2896,18 +3826,37 @@ issue() {
Le_LinkIssuer=$(grep -i '^Link' "$HTTP_HEADER" | _head_n 1 | cut -d " " -f 2 | cut -d ';' -f 1 | tr -d '<>') Le_LinkIssuer=$(grep -i '^Link' "$HTTP_HEADER" | _head_n 1 | cut -d " " -f 2 | cut -d ';' -f 1 | tr -d '<>')
if ! _contains "$Le_LinkIssuer" ":"; then if ! _contains "$Le_LinkIssuer" ":"; then
Le_LinkIssuer="$API$Le_LinkIssuer" _info "$(__red "Relative issuer link found.")"
Le_LinkIssuer="$_ACME_SERVER_HOST$Le_LinkIssuer"
fi fi
_debug Le_LinkIssuer "$Le_LinkIssuer"
_savedomainconf "Le_LinkIssuer" "$Le_LinkIssuer" _savedomainconf "Le_LinkIssuer" "$Le_LinkIssuer"
if [ "$Le_LinkIssuer" ]; then if [ "$Le_LinkIssuer" ]; then
echo "$BEGIN_CERT" >"$CA_CERT_PATH" _link_issuer_retry=0
_get "$Le_LinkIssuer" | _base64 "multiline" >>"$CA_CERT_PATH" _MAX_ISSUER_RETRY=5
echo "$END_CERT" >>"$CA_CERT_PATH" while [ "$_link_issuer_retry" -lt "$_MAX_ISSUER_RETRY" ]; do
_info "The intermediate CA cert is in $(__green " $CA_CERT_PATH ")" _debug _link_issuer_retry "$_link_issuer_retry"
cat "$CA_CERT_PATH" >>"$CERT_FULLCHAIN_PATH" if _get "$Le_LinkIssuer" >"$CA_CERT_PATH.der"; then
_info "And the full chain certs is there: $(__green " $CERT_FULLCHAIN_PATH ")" echo "$BEGIN_CERT" >"$CA_CERT_PATH"
_base64 "multiline" <"$CA_CERT_PATH.der" >>"$CA_CERT_PATH"
echo "$END_CERT" >>"$CA_CERT_PATH"
_info "The intermediate CA cert is in $(__green " $CA_CERT_PATH ")"
cat "$CA_CERT_PATH" >>"$CERT_FULLCHAIN_PATH"
_info "And the full chain certs is there: $(__green " $CERT_FULLCHAIN_PATH ")"
rm -f "$CA_CERT_PATH.der"
break
fi
_link_issuer_retry=$(_math $_link_issuer_retry + 1)
_sleep "$_link_issuer_retry"
done
if [ "$_link_issuer_retry" = "$_MAX_ISSUER_RETRY" ]; then
_err "Max retry for issuer ca cert is reached."
fi
else
_debug "No Le_LinkIssuer header found."
fi fi
Le_CertCreateTime=$(_time) Le_CertCreateTime=$(_time)
...@@ -2928,6 +3877,12 @@ issue() { ...@@ -2928,6 +3877,12 @@ issue() {
_clearaccountconf "CA_BUNDLE" _clearaccountconf "CA_BUNDLE"
fi fi
if [ "$CA_PATH" ]; then
_saveaccountconf CA_PATH "$CA_PATH"
else
_clearaccountconf "CA_PATH"
fi
if [ "$HTTPS_INSECURE" ]; then if [ "$HTTPS_INSECURE" ]; then
_saveaccountconf HTTPS_INSECURE "$HTTPS_INSECURE" _saveaccountconf HTTPS_INSECURE "$HTTPS_INSECURE"
else else
...@@ -2942,6 +3897,12 @@ issue() { ...@@ -2942,6 +3897,12 @@ issue() {
_cleardomainconf Le_Listen_V4 _cleardomainconf Le_Listen_V4
fi fi
if [ "$Le_ForceNewDomainKey" = "1" ]; then
_savedomainconf "Le_ForceNewDomainKey" "$Le_ForceNewDomainKey"
else
_cleardomainconf Le_ForceNewDomainKey
fi
Le_NextRenewTime=$(_math "$Le_CertCreateTime" + "$Le_RenewalDays" \* 24 \* 60 \* 60) Le_NextRenewTime=$(_math "$Le_CertCreateTime" + "$Le_RenewalDays" \* 24 \* 60 \* 60)
Le_NextRenewTimeStr=$(_time2str "$Le_NextRenewTime") Le_NextRenewTimeStr=$(_time2str "$Le_NextRenewTime")
...@@ -2950,10 +3911,18 @@ issue() { ...@@ -2950,10 +3911,18 @@ issue() {
Le_NextRenewTime=$(_math "$Le_NextRenewTime" - 86400) Le_NextRenewTime=$(_math "$Le_NextRenewTime" - 86400)
_savedomainconf "Le_NextRenewTime" "$Le_NextRenewTime" _savedomainconf "Le_NextRenewTime" "$Le_NextRenewTime"
_on_issue_success if ! _on_issue_success "$_post_hook" "$_renew_hook"; then
_err "Call hook error."
return 1
fi
if [ "$Le_RealCertPath$Le_RealKeyPath$Le_RealCACertPath$Le_ReloadCmd$Le_RealFullChainPath" ]; then if [ "$_real_cert$_real_key$_real_ca$_reload_cmd$_real_fullchain" ]; then
_installcert _savedomainconf "Le_RealCertPath" "$_real_cert"
_savedomainconf "Le_RealCACertPath" "$_real_ca"
_savedomainconf "Le_RealKeyPath" "$_real_key"
_savedomainconf "Le_ReloadCmd" "$_reload_cmd"
_savedomainconf "Le_RealFullChainPath" "$_real_fullchain"
_installcert "$_main_domain" "$_real_cert" "$_real_key" "$_real_ca" "$_real_fullchain" "$_reload_cmd"
fi fi
} }
...@@ -2983,7 +3952,21 @@ renew() { ...@@ -2983,7 +3952,21 @@ renew() {
. "$DOMAIN_CONF" . "$DOMAIN_CONF"
if [ "$Le_API" ]; then if [ "$Le_API" ]; then
API="$Le_API" if [ "$_OLD_CA_HOST" = "$Le_API" ]; then
export Le_API="$DEFAULT_CA"
_savedomainconf Le_API "$Le_API"
fi
if [ "$_OLD_STAGE_CA_HOST" = "$Le_API" ]; then
export Le_API="$STAGE_CA"
_savedomainconf Le_API "$Le_API"
fi
export ACME_DIRECTORY="$Le_API"
#reload ca configs
ACCOUNT_KEY_PATH=""
ACCOUNT_JSON_PATH=""
CA_CONF=""
_debug3 "initpath again."
_initpath "$Le_Domain" "$_isEcc"
fi fi
if [ -z "$FORCE" ] && [ "$Le_NextRenewTime" ] && [ "$(_time)" -lt "$Le_NextRenewTime" ]; then if [ -z "$FORCE" ] && [ "$Le_NextRenewTime" ] && [ "$(_time)" -lt "$Le_NextRenewTime" ]; then
...@@ -2992,6 +3975,11 @@ renew() { ...@@ -2992,6 +3975,11 @@ renew() {
return "$RENEW_SKIP" return "$RENEW_SKIP"
fi fi
if [ "$IN_CRON" = "1" ] && [ -z "$Le_CertCreateTime" ]; then
_info "Skip invalid cert for: $Le_Domain"
return 0
fi
IS_RENEW="1" IS_RENEW="1"
issue "$Le_Webroot" "$Le_Domain" "$Le_Alt" "$Le_Keylength" "$Le_RealCertPath" "$Le_RealKeyPath" "$Le_RealCACertPath" "$Le_ReloadCmd" "$Le_RealFullChainPath" "$Le_PreHook" "$Le_PostHook" "$Le_RenewHook" "$Le_LocalAddress" issue "$Le_Webroot" "$Le_Domain" "$Le_Alt" "$Le_Keylength" "$Le_RealCertPath" "$Le_RealKeyPath" "$Le_RealCACertPath" "$Le_ReloadCmd" "$Le_RealFullChainPath" "$Le_PreHook" "$Le_PostHook" "$Le_RenewHook" "$Le_LocalAddress"
res="$?" res="$?"
...@@ -3000,7 +3988,7 @@ renew() { ...@@ -3000,7 +3988,7 @@ renew() {
fi fi
if [ "$Le_DeployHook" ]; then if [ "$Le_DeployHook" ]; then
deploy "$Le_Domain" "$Le_DeployHook" "$Le_Keylength" _deploy "$Le_Domain" "$Le_DeployHook"
res="$?" res="$?"
fi fi
...@@ -3041,7 +4029,7 @@ renewAll() { ...@@ -3041,7 +4029,7 @@ renewAll() {
return "$rc" return "$rc"
else else
_ret="$rc" _ret="$rc"
_err "Error renew $d, Go ahead to next one." _err "Error renew $d."
fi fi
fi fi
done done
...@@ -3065,6 +4053,10 @@ signcsr() { ...@@ -3065,6 +4053,10 @@ signcsr() {
return 1 return 1
fi fi
_debug _csrsubj "$_csrsubj" _debug _csrsubj "$_csrsubj"
if _contains "$_csrsubj" ' ' || ! _contains "$_csrsubj" '.'; then
_info "It seems that the subject: $_csrsubj is not a valid domain name. Drop it."
_csrsubj=""
fi
_csrdomainlist=$(_readSubjectAltNamesFromCSR "$_csrfile") _csrdomainlist=$(_readSubjectAltNamesFromCSR "$_csrfile")
if [ "$?" != "0" ]; then if [ "$?" != "0" ]; then
...@@ -3172,147 +4164,176 @@ list() { ...@@ -3172,147 +4164,176 @@ list() {
} }
_deploy() {
_d="$1"
_hooks="$2"
for _d_api in $(echo "$_hooks" | tr ',' " "); do
_deployApi="$(_findHook "$_d" deploy "$_d_api")"
if [ -z "$_deployApi" ]; then
_err "The deploy hook $_d_api is not found."
return 1
fi
_debug _deployApi "$_deployApi"
if ! (
if ! . "$_deployApi"; then
_err "Load file $_deployApi error. Please check your api file and try again."
return 1
fi
d_command="${_d_api}_deploy"
if ! _exists "$d_command"; then
_err "It seems that your api file is not correct, it must have a function named: $d_command"
return 1
fi
if ! $d_command "$_d" "$CERT_KEY_PATH" "$CERT_PATH" "$CA_CERT_PATH" "$CERT_FULLCHAIN_PATH"; then
_err "Error deploy for domain:$_d"
return 1
fi
); then
_err "Deploy error."
return 1
else
_info "$(__green Success)"
fi
done
}
#domain hooks
deploy() { deploy() {
Le_Domain="$1" _d="$1"
Le_DeployHook="$2" _hooks="$2"
_isEcc="$3" _isEcc="$3"
if [ -z "$Le_DeployHook" ]; then if [ -z "$_hooks" ]; then
_usage "Usage: $PROJECT_ENTRY --deploy -d domain.com --deploy-hook cpanel [--ecc] " _usage "Usage: $PROJECT_ENTRY --deploy -d domain.com --deploy-hook cpanel [--ecc] "
return 1 return 1
fi fi
_initpath "$Le_Domain" "$_isEcc" _initpath "$_d" "$_isEcc"
if [ ! -d "$DOMAIN_PATH" ]; then if [ ! -d "$DOMAIN_PATH" ]; then
_err "Domain is not valid:'$Le_Domain'" _err "Domain is not valid:'$_d'"
return 1 return 1
fi fi
_deployApi="$(_findHook "$Le_Domain" deploy "$Le_DeployHook")" . "$DOMAIN_CONF"
if [ -z "$_deployApi" ]; then
_err "The deploy hook $Le_DeployHook is not found."
return 1
fi
_debug _deployApi "$_deployApi"
_savedomainconf Le_DeployHook "$Le_DeployHook"
if ! (
if ! . "$_deployApi"; then
_err "Load file $_deployApi error. Please check your api file and try again."
return 1
fi
d_command="${Le_DeployHook}_deploy"
if ! _exists "$d_command"; then
_err "It seems that your api file is not correct, it must have a function named: $d_command"
return 1
fi
if ! $d_command "$Le_Domain" "$CERT_KEY_PATH" "$CERT_PATH" "$CA_CERT_PATH" "$CERT_FULLCHAIN_PATH"; then _savedomainconf Le_DeployHook "$_hooks"
_err "Error deploy for domain:$Le_Domain"
_on_issue_err
return 1
fi
); then
_err "Deploy error."
return 1
else
_info "$(__green Success)"
fi
_deploy "$_d" "$_hooks"
} }
installcert() { installcert() {
Le_Domain="$1" _main_domain="$1"
if [ -z "$Le_Domain" ]; then if [ -z "$_main_domain" ]; then
_usage "Usage: $PROJECT_ENTRY --installcert -d domain.com [--ecc] [--certpath cert-file-path] [--keypath key-file-path] [--capath ca-cert-file-path] [ --reloadCmd reloadCmd] [--fullchainpath fullchain-path]" _usage "Usage: $PROJECT_ENTRY --installcert -d domain.com [--ecc] [--cert-file cert-file-path] [--key-file key-file-path] [--ca-file ca-cert-file-path] [ --reloadCmd reloadCmd] [--fullchain-file fullchain-path]"
return 1 return 1
fi fi
Le_RealCertPath="$2" _real_cert="$2"
Le_RealKeyPath="$3" _real_key="$3"
Le_RealCACertPath="$4" _real_ca="$4"
Le_ReloadCmd="$5" _reload_cmd="$5"
Le_RealFullChainPath="$6" _real_fullchain="$6"
_isEcc="$7" _isEcc="$7"
_initpath "$Le_Domain" "$_isEcc" _initpath "$_main_domain" "$_isEcc"
if [ ! -d "$DOMAIN_PATH" ]; then if [ ! -d "$DOMAIN_PATH" ]; then
_err "Domain is not valid:'$Le_Domain'" _err "Domain is not valid:'$_main_domain'"
return 1 return 1
fi fi
_installcert _savedomainconf "Le_RealCertPath" "$_real_cert"
_savedomainconf "Le_RealCACertPath" "$_real_ca"
_savedomainconf "Le_RealKeyPath" "$_real_key"
_savedomainconf "Le_ReloadCmd" "$_reload_cmd"
_savedomainconf "Le_RealFullChainPath" "$_real_fullchain"
_installcert "$_main_domain" "$_real_cert" "$_real_key" "$_real_ca" "$_real_fullchain" "$_reload_cmd"
} }
#domain cert key ca fullchain reloadcmd backup-prefix
_installcert() { _installcert() {
_savedomainconf "Le_RealCertPath" "$Le_RealCertPath" _main_domain="$1"
_savedomainconf "Le_RealCACertPath" "$Le_RealCACertPath" _real_cert="$2"
_savedomainconf "Le_RealKeyPath" "$Le_RealKeyPath" _real_key="$3"
_savedomainconf "Le_ReloadCmd" "$Le_ReloadCmd" _real_ca="$4"
_savedomainconf "Le_RealFullChainPath" "$Le_RealFullChainPath" _real_fullchain="$5"
_reload_cmd="$6"
_backup_prefix="$7"
if [ "$Le_RealCertPath" = "$NO_VALUE" ]; then if [ "$_real_cert" = "$NO_VALUE" ]; then
Le_RealCertPath="" _real_cert=""
fi fi
if [ "$Le_RealKeyPath" = "$NO_VALUE" ]; then if [ "$_real_key" = "$NO_VALUE" ]; then
Le_RealKeyPath="" _real_key=""
fi fi
if [ "$Le_RealCACertPath" = "$NO_VALUE" ]; then if [ "$_real_ca" = "$NO_VALUE" ]; then
Le_RealCACertPath="" _real_ca=""
fi fi
if [ "$Le_ReloadCmd" = "$NO_VALUE" ]; then if [ "$_reload_cmd" = "$NO_VALUE" ]; then
Le_ReloadCmd="" _reload_cmd=""
fi fi
if [ "$Le_RealFullChainPath" = "$NO_VALUE" ]; then if [ "$_real_fullchain" = "$NO_VALUE" ]; then
Le_RealFullChainPath="" _real_fullchain=""
fi fi
if [ "$Le_RealCertPath" ]; then _backup_path="$DOMAIN_BACKUP_PATH/$_backup_prefix"
mkdir -p "$_backup_path"
_info "Installing cert to:$Le_RealCertPath" if [ "$_real_cert" ]; then
if [ -f "$Le_RealCertPath" ] && [ ! "$IS_RENEW" ]; then _info "Installing cert to:$_real_cert"
cp "$Le_RealCertPath" "$Le_RealCertPath".bak if [ -f "$_real_cert" ] && [ ! "$IS_RENEW" ]; then
cp "$_real_cert" "$_backup_path/cert.bak"
fi fi
cat "$CERT_PATH" >"$Le_RealCertPath" cat "$CERT_PATH" >"$_real_cert"
fi fi
if [ "$Le_RealCACertPath" ]; then if [ "$_real_ca" ]; then
_info "Installing CA to:$_real_ca"
_info "Installing CA to:$Le_RealCACertPath" if [ "$_real_ca" = "$_real_cert" ]; then
if [ "$Le_RealCACertPath" = "$Le_RealCertPath" ]; then echo "" >>"$_real_ca"
echo "" >>"$Le_RealCACertPath" cat "$CA_CERT_PATH" >>"$_real_ca"
cat "$CA_CERT_PATH" >>"$Le_RealCACertPath"
else else
if [ -f "$Le_RealCACertPath" ] && [ ! "$IS_RENEW" ]; then if [ -f "$_real_ca" ] && [ ! "$IS_RENEW" ]; then
cp "$Le_RealCACertPath" "$Le_RealCACertPath".bak cp "$_real_ca" "$_backup_path/ca.bak"
fi fi
cat "$CA_CERT_PATH" >"$Le_RealCACertPath" cat "$CA_CERT_PATH" >"$_real_ca"
fi fi
fi fi
if [ "$Le_RealKeyPath" ]; then if [ "$_real_key" ]; then
_info "Installing key to:$_real_key"
_info "Installing key to:$Le_RealKeyPath" if [ -f "$_real_key" ] && [ ! "$IS_RENEW" ]; then
if [ -f "$Le_RealKeyPath" ] && [ ! "$IS_RENEW" ]; then cp "$_real_key" "$_backup_path/key.bak"
cp "$Le_RealKeyPath" "$Le_RealKeyPath".bak
fi fi
cat "$CERT_KEY_PATH" >"$Le_RealKeyPath" if [ -f "$_real_key" ]; then
fi cat "$CERT_KEY_PATH" >"$_real_key"
else
if [ "$Le_RealFullChainPath" ]; then cat "$CERT_KEY_PATH" >"$_real_key"
chmod 700 "$_real_key"
_info "Installing full chain to:$Le_RealFullChainPath"
if [ -f "$Le_RealFullChainPath" ] && [ ! "$IS_RENEW" ]; then
cp "$Le_RealFullChainPath" "$Le_RealFullChainPath".bak
fi fi
cat "$CERT_FULLCHAIN_PATH" >"$Le_RealFullChainPath"
fi fi
if [ "$Le_ReloadCmd" ]; then if [ "$_real_fullchain" ]; then
_info "Installing full chain to:$_real_fullchain"
_info "Run Le_ReloadCmd: $Le_ReloadCmd" if [ -f "$_real_fullchain" ] && [ ! "$IS_RENEW" ]; then
if (cd "$DOMAIN_PATH" && eval "$Le_ReloadCmd"); then cp "$_real_fullchain" "$_backup_path/fullchain.bak"
fi
cat "$CERT_FULLCHAIN_PATH" >"$_real_fullchain"
fi
if [ "$_reload_cmd" ]; then
_info "Run reload cmd: $_reload_cmd"
if (
export CERT_PATH
export CERT_KEY_PATH
export CA_CERT_PATH
export CERT_FULLCHAIN_PATH
export Le_Domain
cd "$DOMAIN_PATH" && eval "$_reload_cmd"
); then
_info "$(__green "Reload success")" _info "$(__green "Reload success")"
else else
_err "Reload error for :$Le_Domain" _err "Reload error for :$Le_Domain"
...@@ -3321,33 +4342,45 @@ _installcert() { ...@@ -3321,33 +4342,45 @@ _installcert() {
} }
#confighome
installcronjob() { installcronjob() {
_c_home="$1"
_initpath _initpath
if ! _exists "crontab"; then _CRONTAB="crontab"
_err "crontab doesn't exist, so, we can not install cron jobs." if ! _exists "$_CRONTAB" && _exists "fcrontab"; then
_CRONTAB="fcrontab"
fi
if ! _exists "$_CRONTAB"; then
_err "crontab/fcrontab doesn't exist, so, we can not install cron jobs."
_err "All your certs will not be renewed automatically." _err "All your certs will not be renewed automatically."
_err "You must add your own cron job to call '$PROJECT_ENTRY --cron' everyday." _err "You must add your own cron job to call '$PROJECT_ENTRY --cron' everyday."
return 1 return 1
fi fi
_info "Installing cron job" _info "Installing cron job"
if ! crontab -l | grep "$PROJECT_ENTRY --cron"; then if ! $_CRONTAB -l | grep "$PROJECT_ENTRY --cron"; then
if [ -f "$LE_WORKING_DIR/$PROJECT_ENTRY" ]; then if [ -f "$LE_WORKING_DIR/$PROJECT_ENTRY" ]; then
lesh="\"$LE_WORKING_DIR\"/$PROJECT_ENTRY" lesh="\"$LE_WORKING_DIR\"/$PROJECT_ENTRY"
else else
_err "Can not install cronjob, $PROJECT_ENTRY not found." _err "Can not install cronjob, $PROJECT_ENTRY not found."
return 1 return 1
fi fi
if _exists uname && uname -a | grep solaris >/dev/null; then
crontab -l | { if [ "$_c_home" ]; then
_c_entry="--config-home \"$_c_home\" "
fi
_t=$(_time)
random_minute=$(_math $_t % 60)
if _exists uname && uname -a | grep SunOS >/dev/null; then
$_CRONTAB -l | {
cat cat
echo "0 0 * * * $lesh --cron --home \"$LE_WORKING_DIR\" > /dev/null" echo "$random_minute 0 * * * $lesh --cron --home \"$LE_WORKING_DIR\" $_c_entry> /dev/null"
} | crontab -- } | $_CRONTAB --
else else
crontab -l | { $_CRONTAB -l | {
cat cat
echo "0 0 * * * $lesh --cron --home \"$LE_WORKING_DIR\" > /dev/null" echo "$random_minute 0 * * * $lesh --cron --home \"$LE_WORKING_DIR\" $_c_entry> /dev/null"
} | crontab - } | $_CRONTAB -
fi fi
fi fi
if [ "$?" != "0" ]; then if [ "$?" != "0" ]; then
...@@ -3359,19 +4392,28 @@ installcronjob() { ...@@ -3359,19 +4392,28 @@ installcronjob() {
} }
uninstallcronjob() { uninstallcronjob() {
if ! _exists "crontab"; then _CRONTAB="crontab"
if ! _exists "$_CRONTAB" && _exists "fcrontab"; then
_CRONTAB="fcrontab"
fi
if ! _exists "$_CRONTAB"; then
return return
fi fi
_info "Removing cron job" _info "Removing cron job"
cr="$(crontab -l | grep "$PROJECT_ENTRY --cron")" cr="$($_CRONTAB -l | grep "$PROJECT_ENTRY --cron")"
if [ "$cr" ]; then if [ "$cr" ]; then
if _exists uname && uname -a | grep solaris >/dev/null; then if _exists uname && uname -a | grep solaris >/dev/null; then
crontab -l | sed "/$PROJECT_ENTRY --cron/d" | crontab -- $_CRONTAB -l | sed "/$PROJECT_ENTRY --cron/d" | $_CRONTAB --
else else
crontab -l | sed "/$PROJECT_ENTRY --cron/d" | crontab - $_CRONTAB -l | sed "/$PROJECT_ENTRY --cron/d" | $_CRONTAB -
fi fi
LE_WORKING_DIR="$(echo "$cr" | cut -d ' ' -f 9 | tr -d '"')" LE_WORKING_DIR="$(echo "$cr" | cut -d ' ' -f 9 | tr -d '"')"
_info LE_WORKING_DIR "$LE_WORKING_DIR" _info LE_WORKING_DIR "$LE_WORKING_DIR"
if _contains "$cr" "--config-home"; then
LE_CONFIG_HOME="$(echo "$cr" | cut -d ' ' -f 11 | tr -d '"')"
_debug LE_CONFIG_HOME "$LE_CONFIG_HOME"
fi
fi fi
_initpath _initpath
...@@ -3380,7 +4422,7 @@ uninstallcronjob() { ...@@ -3380,7 +4422,7 @@ uninstallcronjob() {
revoke() { revoke() {
Le_Domain="$1" Le_Domain="$1"
if [ -z "$Le_Domain" ]; then if [ -z "$Le_Domain" ]; then
_usage "Usage: $PROJECT_ENTRY --revoke -d domain.com" _usage "Usage: $PROJECT_ENTRY --revoke -d domain.com [--ecc]"
return 1 return 1
fi fi
...@@ -3397,15 +4439,17 @@ revoke() { ...@@ -3397,15 +4439,17 @@ revoke() {
return 1 return 1
fi fi
cert="$(_getfile "${CERT_PATH}" "${BEGIN_CERT}" "${END_CERT}" | tr -d "\r\n" | _urlencode)" cert="$(_getfile "${CERT_PATH}" "${BEGIN_CERT}" "${END_CERT}" | tr -d "\r\n" | _url_replace)"
if [ -z "$cert" ]; then if [ -z "$cert" ]; then
_err "Cert for $Le_Domain is empty found, skip." _err "Cert for $Le_Domain is empty found, skip."
return 1 return 1
fi fi
_initAPI
data="{\"resource\": \"revoke-cert\", \"certificate\": \"$cert\"}" data="{\"resource\": \"revoke-cert\", \"certificate\": \"$cert\"}"
uri="$API/acme/revoke-cert" uri="${ACME_REVOKE_CERT}"
if [ -f "$CERT_KEY_PATH" ]; then if [ -f "$CERT_KEY_PATH" ]; then
_info "Try domain key first." _info "Try domain key first."
...@@ -3438,32 +4482,88 @@ revoke() { ...@@ -3438,32 +4482,88 @@ revoke() {
return 1 return 1
} }
#domain ecc
remove() {
Le_Domain="$1"
if [ -z "$Le_Domain" ]; then
_usage "Usage: $PROJECT_ENTRY --remove -d domain.com [--ecc]"
return 1
fi
_isEcc="$2"
_initpath "$Le_Domain" "$_isEcc"
_removed_conf="$DOMAIN_CONF.removed"
if [ ! -f "$DOMAIN_CONF" ]; then
if [ -f "$_removed_conf" ]; then
_err "$Le_Domain is already removed, You can remove the folder by yourself: $DOMAIN_PATH"
else
_err "$Le_Domain is not a issued domain, skip."
fi
return 1
fi
if mv "$DOMAIN_CONF" "$_removed_conf"; then
_info "$Le_Domain is removed, the key and cert files are in $(__green $DOMAIN_PATH)"
_info "You can remove them by yourself."
return 0
else
_err "Remove $Le_Domain failed."
return 1
fi
}
#domain vtype #domain vtype
_deactivate() { _deactivate() {
_d_domain="$1" _d_domain="$1"
_d_type="$2" _d_type="$2"
_initpath _initpath
_d_i=0 if ! __get_domain_new_authz "$_d_domain"; then
_d_max_retry=9 _err "Can not get domain new authz token."
while [ "$_d_i" -lt "$_d_max_retry" ]; do return 1
_info "Deactivate: $_d_domain" fi
_d_i="$(_math $_d_i + 1)"
if ! __get_domain_new_authz "$_d_domain"; then authzUri="$(echo "$responseHeaders" | grep "^Location:" | _head_n 1 | cut -d ' ' -f 2 | tr -d "\r\n")"
_err "Can not get domain new authz token." _debug "authzUri" "$authzUri"
return 1
fi
authzUri="$(echo "$responseHeaders" | grep "^Location:" | _head_n 1 | cut -d ' ' -f 2 | tr -d "\r\n")" if [ "$code" ] && [ ! "$code" = '201' ]; then
_debug "authzUri" "$authzUri" _err "new-authz error: $response"
return 1
fi
if [ ! -z "$code" ] && [ ! "$code" = '201' ]; then entries="$(echo "$response" | _egrep_o '{ *"type":"[^"]*", *"status": *"valid", *"uri"[^}]*')"
_err "new-authz error: $response" if [ -z "$entries" ]; then
_info "No valid entries found."
if [ -z "$thumbprint" ]; then
thumbprint="$(__calc_account_thumbprint)"
fi
_debug "Trigger validation."
vtype="$VTYPE_HTTP"
entry="$(printf "%s\n" "$response" | _egrep_o '[^\{]*"type":"'$vtype'"[^\}]*')"
_debug entry "$entry"
if [ -z "$entry" ]; then
_err "Error, can not get domain token $d"
return 1 return 1
fi fi
token="$(printf "%s\n" "$entry" | _egrep_o '"token":"[^"]*' | cut -d : -f 2 | tr -d '"')"
_debug token "$token"
uri="$(printf "%s\n" "$entry" | _egrep_o '"uri":"[^"]*' | cut -d : -f 2,3 | tr -d '"')"
_debug uri "$uri"
keyauthorization="$token.$thumbprint"
_debug keyauthorization "$keyauthorization"
__trigger_validation "$uri" "$keyauthorization"
fi
entry="$(printf "%s\n" "$response" | _egrep_o '[^\{]*"status":"valid","uri"[^\}]*')" _d_i=0
_d_max_retry=$(echo "$entries" | wc -l)
while [ "$_d_i" -lt "$_d_max_retry" ]; do
_info "Deactivate: $_d_domain"
_d_i="$(_math $_d_i + 1)"
entry="$(echo "$entries" | sed -n "${_d_i}p")"
_debug entry "$entry" _debug entry "$entry"
if [ -z "$entry" ]; then if [ -z "$entry" ]; then
...@@ -3485,16 +4585,16 @@ _deactivate() { ...@@ -3485,16 +4585,16 @@ _deactivate() {
_info "Deactivate: $_vtype" _info "Deactivate: $_vtype"
if ! _send_signed_request "$authzUri" "{\"resource\": \"authz\", \"status\":\"deactivated\"}"; then if _send_signed_request "$authzUri" "{\"resource\": \"authz\", \"status\":\"deactivated\"}" && _contains "$response" '"deactivated"'; then
_info "Deactivate: $_vtype success."
else
_err "Can not deactivate $_vtype." _err "Can not deactivate $_vtype."
return 1 break
fi fi
_info "Deactivate: $_vtype success."
done done
_debug "$_d_i" _debug "$_d_i"
if [ "$_d_i" -lt "$_d_max_retry" ]; then if [ "$_d_i" -eq "$_d_max_retry" ]; then
_info "Deactivated success!" _info "Deactivated success!"
else else
_err "Deactivate failed." _err "Deactivate failed."
...@@ -3506,6 +4606,7 @@ deactivate() { ...@@ -3506,6 +4606,7 @@ deactivate() {
_d_domain_list="$1" _d_domain_list="$1"
_d_type="$2" _d_type="$2"
_initpath _initpath
_initAPI
_debug _d_domain_list "$_d_domain_list" _debug _d_domain_list "$_d_domain_list"
if [ -z "$(echo $_d_domain_list | cut -d , -f 1)" ]; then if [ -z "$(echo $_d_domain_list | cut -d , -f 1)" ]; then
_usage "Usage: $PROJECT_ENTRY --deactivate -d domain.com [-d domain.com]" _usage "Usage: $PROJECT_ENTRY --deactivate -d domain.com [-d domain.com]"
...@@ -3553,83 +4654,20 @@ _detect_profile() { ...@@ -3553,83 +4654,20 @@ _detect_profile() {
fi fi
fi fi
if [ ! -z "$DETECTED_PROFILE" ]; then echo "$DETECTED_PROFILE"
echo "$DETECTED_PROFILE"
fi
} }
_initconf() { _initconf() {
_initpath _initpath
if [ ! -f "$ACCOUNT_CONF_PATH" ]; then if [ ! -f "$ACCOUNT_CONF_PATH" ]; then
echo "#ACCOUNT_CONF_PATH=xxxx echo "
#Account configurations:
#Here are the supported macros, uncomment them to make them take effect.
#ACCOUNT_EMAIL=aaa@example.com # the account email used to register account.
#ACCOUNT_KEY_PATH=\"/path/to/account.key\"
#CERT_HOME=\"/path/to/cert/home\"
#LOG_FILE=\"$DEFAULT_LOG_FILE\" #LOG_FILE=\"$DEFAULT_LOG_FILE\"
#LOG_LEVEL=1 #LOG_LEVEL=1
#AUTO_UPGRADE=\"1\" #AUTO_UPGRADE=\"1\"
#STAGE=1 # Use the staging api #NO_TIMESTAMP=1
#FORCE=1 # Force to issue cert
#DEBUG=1 # Debug mode
#OPENSSL_BIN=openssl
#USER_AGENT=\"$USER_AGENT\"
#USER_PATH=
#dns api
#######################
#Cloudflare:
#api key
#CF_Key=\"sdfsdfsdfljlbjkljlkjsdfoiwje\"
#account email
#CF_Email=\"xxxx@sss.com\"
#######################
#Dnspod.cn:
#api key id
#DP_Id=\"1234\"
#api key
#DP_Key=\"sADDsdasdgdsf\"
#######################
#Cloudxns.com:
#CX_Key=\"1234\"
#
#CX_Secret=\"sADDsdasdgdsf\"
#######################
#Godaddy.com:
#GD_Key=\"sdfdsgdgdfdasfds\"
#
#GD_Secret=\"sADDsdasdfsdfdssdgdsf\"
#######################
#nsupdate:
#NSUPDATE_KEY=\"/path/to/update.key\"
#NSUPDATE_SERVER=\"192.168.0.1\"
#######################
#PowerDNS:
#PDNS_Url=\"http://ns.example.com:8081\"
#PDNS_ServerId=\"localhost\"
#PDNS_Token=\"0123456789ABCDEF\"
#PDNS_Ttl=60
#######################
#Amazon Route53:
#AWS_ACCESS_KEY_ID=XXXXXXXXXX
#AWS_SECRET_ACCESS_KEY=XXXXXXXXXXXXXXX
" >"$ACCOUNT_CONF_PATH" " >"$ACCOUNT_CONF_PATH"
fi fi
...@@ -3645,7 +4683,7 @@ _precheck() { ...@@ -3645,7 +4683,7 @@ _precheck() {
fi fi
if [ -z "$_nocron" ]; then if [ -z "$_nocron" ]; then
if ! _exists "crontab"; then if ! _exists "crontab" && ! _exists "fcrontab"; then
_err "It is recommended to install crontab first. try to install 'cron, crontab, crontabs or vixie-cron'." _err "It is recommended to install crontab first. try to install 'cron, crontab, crontabs or vixie-cron'."
_err "We need to set cron job to renew the certs automatically." _err "We need to set cron job to renew the certs automatically."
_err "Otherwise, your certs will not be able to be renewed automatically." _err "Otherwise, your certs will not be able to be renewed automatically."
...@@ -3657,15 +4695,15 @@ _precheck() { ...@@ -3657,15 +4695,15 @@ _precheck() {
fi fi
fi fi
if ! _exists "$OPENSSL_BIN"; then if ! _exists "${ACME_OPENSSL_BIN:-openssl}"; then
_err "Please install openssl first." _err "Please install openssl first. ACME_OPENSSL_BIN=$ACME_OPENSSL_BIN"
_err "We need openssl to generate keys." _err "We need openssl to generate keys."
return 1 return 1
fi fi
if ! _exists "nc"; then if ! _exists "socat"; then
_err "It is recommended to install nc first, try to install 'nc' or 'netcat'." _err "It is recommended to install socat first."
_err "We use nc for standalone server if you use standalone mode." _err "We use socat for standalone server if you use standalone mode."
_err "If you don't use standalone mode, just ignore this warning." _err "If you don't use standalone mode, just ignore this warning."
fi fi
...@@ -3685,7 +4723,9 @@ _setShebang() { ...@@ -3685,7 +4723,9 @@ _setShebang() {
rm -f "$_file.tmp" rm -f "$_file.tmp"
} }
#confighome
_installalias() { _installalias() {
_c_home="$1"
_initpath _initpath
_envfile="$LE_WORKING_DIR/$PROJECT_ENTRY.env" _envfile="$LE_WORKING_DIR/$PROJECT_ENTRY.env"
...@@ -3695,8 +4735,17 @@ _installalias() { ...@@ -3695,8 +4735,17 @@ _installalias() {
echo "$(cat "$_envfile")" | sed "s|^alias le.sh.*$||" >"$_envfile" echo "$(cat "$_envfile")" | sed "s|^alias le.sh.*$||" >"$_envfile"
fi fi
if [ "$_c_home" ]; then
_c_entry=" --config-home '$_c_home'"
fi
_setopt "$_envfile" "export LE_WORKING_DIR" "=" "\"$LE_WORKING_DIR\"" _setopt "$_envfile" "export LE_WORKING_DIR" "=" "\"$LE_WORKING_DIR\""
_setopt "$_envfile" "alias $PROJECT_ENTRY" "=" "\"$LE_WORKING_DIR/$PROJECT_ENTRY\"" if [ "$_c_home" ]; then
_setopt "$_envfile" "export LE_CONFIG_HOME" "=" "\"$LE_CONFIG_HOME\""
else
_sed_i "/^export LE_CONFIG_HOME/d" "$_envfile"
fi
_setopt "$_envfile" "alias $PROJECT_ENTRY" "=" "\"$LE_WORKING_DIR/$PROJECT_ENTRY$_c_entry\""
_profile="$(_detect_profile)" _profile="$(_detect_profile)"
if [ "$_profile" ]; then if [ "$_profile" ]; then
...@@ -3714,7 +4763,12 @@ _installalias() { ...@@ -3714,7 +4763,12 @@ _installalias() {
if [ -f "$_csh_profile" ]; then if [ -f "$_csh_profile" ]; then
_info "Installing alias to '$_csh_profile'" _info "Installing alias to '$_csh_profile'"
_setopt "$_cshfile" "setenv LE_WORKING_DIR" " " "\"$LE_WORKING_DIR\"" _setopt "$_cshfile" "setenv LE_WORKING_DIR" " " "\"$LE_WORKING_DIR\""
_setopt "$_cshfile" "alias $PROJECT_ENTRY" " " "\"$LE_WORKING_DIR/$PROJECT_ENTRY\"" if [ "$_c_home" ]; then
_setopt "$_cshfile" "setenv LE_CONFIG_HOME" " " "\"$LE_CONFIG_HOME\""
else
_sed_i "/^setenv LE_CONFIG_HOME/d" "$_cshfile"
fi
_setopt "$_cshfile" "alias $PROJECT_ENTRY" " " "\"$LE_WORKING_DIR/$PROJECT_ENTRY$_c_entry\""
_setopt "$_csh_profile" "source \"$_cshfile\"" _setopt "$_csh_profile" "source \"$_cshfile\""
fi fi
...@@ -3723,13 +4777,16 @@ _installalias() { ...@@ -3723,13 +4777,16 @@ _installalias() {
if [ -f "$_tcsh_profile" ]; then if [ -f "$_tcsh_profile" ]; then
_info "Installing alias to '$_tcsh_profile'" _info "Installing alias to '$_tcsh_profile'"
_setopt "$_cshfile" "setenv LE_WORKING_DIR" " " "\"$LE_WORKING_DIR\"" _setopt "$_cshfile" "setenv LE_WORKING_DIR" " " "\"$LE_WORKING_DIR\""
_setopt "$_cshfile" "alias $PROJECT_ENTRY" " " "\"$LE_WORKING_DIR/$PROJECT_ENTRY\"" if [ "$_c_home" ]; then
_setopt "$_cshfile" "setenv LE_CONFIG_HOME" " " "\"$LE_CONFIG_HOME\""
fi
_setopt "$_cshfile" "alias $PROJECT_ENTRY" " " "\"$LE_WORKING_DIR/$PROJECT_ENTRY$_c_entry\""
_setopt "$_tcsh_profile" "source \"$_cshfile\"" _setopt "$_tcsh_profile" "source \"$_cshfile\""
fi fi
} }
# nocron # nocron confighome
install() { install() {
if [ -z "$LE_WORKING_DIR" ]; then if [ -z "$LE_WORKING_DIR" ]; then
...@@ -3737,6 +4794,7 @@ install() { ...@@ -3737,6 +4794,7 @@ install() {
fi fi
_nocron="$1" _nocron="$1"
_c_home="$2"
if ! _initpath; then if ! _initpath; then
_err "Install failed." _err "Install failed."
return 1 return 1
...@@ -3745,9 +4803,16 @@ install() { ...@@ -3745,9 +4803,16 @@ install() {
_debug "Skip install cron job" _debug "Skip install cron job"
fi fi
if ! _precheck "$_nocron"; then if [ "$IN_CRON" != "1" ]; then
_err "Pre-check failed, can not install." if ! _precheck "$_nocron"; then
return 1 _err "Pre-check failed, can not install."
return 1
fi
fi
if [ -z "$_c_home" ] && [ "$LE_CONFIG_HOME" != "$LE_WORKING_DIR" ]; then
_info "Using config home: $LE_CONFIG_HOME"
_c_home="$LE_CONFIG_HOME"
fi fi
#convert from le #convert from le
...@@ -3768,12 +4833,23 @@ install() { ...@@ -3768,12 +4833,23 @@ install() {
_info "Installing to $LE_WORKING_DIR" _info "Installing to $LE_WORKING_DIR"
if ! mkdir -p "$LE_WORKING_DIR"; then if [ ! -d "$LE_WORKING_DIR" ]; then
_err "Can not create working dir: $LE_WORKING_DIR" if ! mkdir -p "$LE_WORKING_DIR"; then
return 1 _err "Can not create working dir: $LE_WORKING_DIR"
return 1
fi
chmod 700 "$LE_WORKING_DIR"
fi fi
chmod 700 "$LE_WORKING_DIR" if [ ! -d "$LE_CONFIG_HOME" ]; then
if ! mkdir -p "$LE_CONFIG_HOME"; then
_err "Can not create config dir: $LE_CONFIG_HOME"
return 1
fi
chmod 700 "$LE_CONFIG_HOME"
fi
cp "$PROJECT_ENTRY" "$LE_WORKING_DIR/" && chmod +x "$LE_WORKING_DIR/$PROJECT_ENTRY" cp "$PROJECT_ENTRY" "$LE_WORKING_DIR/" && chmod +x "$LE_WORKING_DIR/$PROJECT_ENTRY"
...@@ -3784,7 +4860,9 @@ install() { ...@@ -3784,7 +4860,9 @@ install() {
_info "Installed to $LE_WORKING_DIR/$PROJECT_ENTRY" _info "Installed to $LE_WORKING_DIR/$PROJECT_ENTRY"
_installalias if [ "$IN_CRON" != "1" ]; then
_installalias "$_c_home"
fi
for subf in $_SUB_FOLDERS; do for subf in $_SUB_FOLDERS; do
if [ -d "$subf" ]; then if [ -d "$subf" ]; then
...@@ -3810,14 +4888,14 @@ install() { ...@@ -3810,14 +4888,14 @@ install() {
fi fi
if [ -z "$_nocron" ]; then if [ -z "$_nocron" ]; then
installcronjob installcronjob "$_c_home"
fi fi
if [ -z "$NO_DETECT_SH" ]; then if [ -z "$NO_DETECT_SH" ]; then
#Modify shebang #Modify shebang
if _exists bash; then if _exists bash; then
_info "Good, bash is found, so change the shebang to use bash as prefered." _info "Good, bash is found, so change the shebang to use bash as preferred."
_shebang='#!/usr/bin/env bash' _shebang='#!'"$(env bash -c "command -v bash")"
_setShebang "$LE_WORKING_DIR/$PROJECT_ENTRY" "$_shebang" _setShebang "$LE_WORKING_DIR/$PROJECT_ENTRY" "$_shebang"
for subf in $_SUB_FOLDERS; do for subf in $_SUB_FOLDERS; do
if [ -d "$LE_WORKING_DIR/$subf" ]; then if [ -d "$LE_WORKING_DIR/$subf" ]; then
...@@ -3843,7 +4921,7 @@ uninstall() { ...@@ -3843,7 +4921,7 @@ uninstall() {
_uninstallalias _uninstallalias
rm -f "$LE_WORKING_DIR/$PROJECT_ENTRY" rm -f "$LE_WORKING_DIR/$PROJECT_ENTRY"
_info "The keys and certs are in $LE_WORKING_DIR, you can remove them by yourself." _info "The keys and certs are in \"$(__green "$LE_CONFIG_HOME")\", you can remove them by yourself."
} }
...@@ -3874,8 +4952,9 @@ _uninstallalias() { ...@@ -3874,8 +4952,9 @@ _uninstallalias() {
} }
cron() { cron() {
IN_CRON=1 export IN_CRON=1
_initpath _initpath
_info "$(__green "===Starting cron===")"
if [ "$AUTO_UPGRADE" = "1" ]; then if [ "$AUTO_UPGRADE" = "1" ]; then
export LE_WORKING_DIR export LE_WORKING_DIR
( (
...@@ -3895,6 +4974,7 @@ cron() { ...@@ -3895,6 +4974,7 @@ cron() {
renewAll renewAll
_ret="$?" _ret="$?"
IN_CRON="" IN_CRON=""
_info "$(__green "===End cron===")"
exit $_ret exit $_ret
} }
...@@ -3912,57 +4992,64 @@ Commands: ...@@ -3912,57 +4992,64 @@ Commands:
--version, -v Show version info. --version, -v Show version info.
--install Install $PROJECT_NAME to your system. --install Install $PROJECT_NAME to your system.
--uninstall Uninstall $PROJECT_NAME, and uninstall the cron job. --uninstall Uninstall $PROJECT_NAME, and uninstall the cron job.
--upgrade Upgrade $PROJECT_NAME to the latest code from $PROJECT . --upgrade Upgrade $PROJECT_NAME to the latest code from $PROJECT.
--issue Issue a cert. --issue Issue a cert.
--signcsr Issue a cert from an existing csr. --signcsr Issue a cert from an existing csr.
--deploy Deploy the cert to your server. --deploy Deploy the cert to your server.
--installcert Install the issued cert to apache/nginx or any other server. --install-cert Install the issued cert to apache/nginx or any other server.
--renew, -r Renew a cert. --renew, -r Renew a cert.
--renewAll Renew all the certs. --renew-all Renew all the certs.
--revoke Revoke a cert. --revoke Revoke a cert.
--remove Remove the cert from $PROJECT
--list List all the certs. --list List all the certs.
--showcsr Show the content of a csr. --showcsr Show the content of a csr.
--installcronjob Install the cron job to renew certs, you don't need to call this. The 'install' command can automatically install the cron job. --install-cronjob Install the cron job to renew certs, you don't need to call this. The 'install' command can automatically install the cron job.
--uninstallcronjob Uninstall the cron job. The 'uninstall' command can do this automatically. --uninstall-cronjob Uninstall the cron job. The 'uninstall' command can do this automatically.
--cron Run cron job to renew all the certs. --cron Run cron job to renew all the certs.
--toPkcs Export the certificate and key to a pfx file. --toPkcs Export the certificate and key to a pfx file.
--updateaccount Update account info. --toPkcs8 Convert to pkcs8 format.
--registeraccount Register account key. --update-account Update account info.
--createAccountKey, -cak Create an account private key, professional use. --register-account Register account key.
--createDomainKey, -cdk Create an domain private key, professional use. --deactivate-account Deactivate the account.
--create-account-key Create an account private key, professional use.
--create-domain-key Create an domain private key, professional use.
--createCSR, -ccsr Create CSR , professional use. --createCSR, -ccsr Create CSR , professional use.
--deactivate Deactivate the domain authz, professional use. --deactivate Deactivate the domain authz, professional use.
Parameters: Parameters:
--domain, -d domain.tld Specifies a domain, used to issue, renew or revoke etc. --domain, -d domain.tld Specifies a domain, used to issue, renew or revoke etc.
--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.
--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.
--stateless Use stateless mode, see: $_STATELESS_WIKI
--tls Use standalone tls mode. --tls Use standalone tls mode.
--apache Use apache mode. --apache Use apache mode.
--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.
--accountkeylength, -ak [2048] Specifies the account key length. --accountkeylength, -ak [2048] Specifies the account key length.
--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.
These parameters are to install the cert to nginx/apache or anyother server after issue/renew a cert: These parameters are to install the cert to nginx/apache or anyother server after issue/renew a cert:
--certpath /path/to/real/cert/file After issue/renew, the cert will be copied to this path. --cert-file After issue/renew, the cert will be copied to this path.
--keypath /path/to/real/key/file After issue/renew, the key will be copied to this path. --key-file After issue/renew, the key will be copied to this path.
--capath /path/to/real/ca/file After issue/renew, the intermediate cert will be copied to this path. --ca-file After issue/renew, the intermediate cert will be copied to this path.
--fullchainpath /path/to/fullchain/file After issue/renew, the fullchain cert will be copied to this path. --fullchain-file After issue/renew, the fullchain cert will be copied to this path.
--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)
--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 .
--certhome 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.
--config-home Specifies the home dir to save all the configurations.
--useragent Specifies the user agent string. it will be saved for future use too. --useragent Specifies the user agent string. it will be saved for future use too.
--accountemail Specifies the account email for registering, Only valid for the '--install' command. --accountemail Specifies the account email for registering, Only valid for the '--install' command.
--accountkey Specifies the account key path, Only valid for the '--install' command. --accountkey Specifies the account key path, Only valid for the '--install' command.
...@@ -3971,21 +5058,25 @@ Parameters: ...@@ -3971,21 +5058,25 @@ Parameters:
--tlsport Specifies the standalone tls listening port. Only valid if the server is behind a reverse proxy or load balancer. --tlsport Specifies the standalone tls listening port. Only valid if the server is behind a reverse proxy or load balancer.
--local-address Specifies the standalone/tls server listening address, in case you have multiple ip addresses. --local-address Specifies the standalone/tls server listening address, in case you have multiple ip addresses.
--listraw Only used for '--list' command, list the certs in raw format. --listraw Only used for '--list' command, list the certs in raw format.
--stopRenewOnError, -se Only valid for '--renewall' command. Stop if one cert has error in renewal. --stopRenewOnError, -se Only valid for '--renew-all' command. Stop if one cert has error in renewal.
--insecure Do not check the server certificate, in some devices, the api server's certificate may not be trusted. --insecure Do not check the server certificate, in some devices, the api server's certificate may not be trusted.
--ca-bundle Specifices the path to the CA certificate bundle to verify api server's certificate. --ca-bundle Specifies the path to the CA certificate bundle to verify api server's certificate.
--ca-path Specifies directory containing CA certificates in PEM format, used by wget or curl.
--nocron Only valid for '--install' command, which means: do not install the default cron job. In this case, the certs will not be renewed automatically. --nocron Only valid for '--install' command, which means: do not install the default cron job. In this case, the certs will not be renewed automatically.
--ecc Specifies to use the ECC cert. Valid for '--installcert', '--renew', '--revoke', '--toPkcs' and '--createCSR' --no-color Do not output color text.
--ecc Specifies to use the ECC cert. Valid for '--install-cert', '--renew', '--revoke', '--toPkcs' and '--createCSR'
--csr Specifies the input csr. --csr Specifies the input csr.
--pre-hook Command to be run before obtaining any certificates. --pre-hook Command to be run before obtaining any certificates.
--post-hook Command to be run after attempting to obtain/renew certificates. No matter the obain/renew is success or failed. --post-hook Command to be run after attempting to obtain/renew certificates. No matter the obtain/renew is success or failed.
--renew-hook Command to be run once for each successfully renewed certificate. --renew-hook Command to be run once for each successfully renewed certificate.
--deploy-hook The hook file to deploy cert --deploy-hook The hook file to deploy cert
--ocsp-must-staple, --ocsp Generate ocsp must Staple extension. --ocsp-must-staple, --ocsp Generate ocsp must Staple extension.
--always-force-new-domain-key Generate new domain key when renewal. Otherwise, the domain key is not changed by default.
--auto-upgrade [0|1] Valid for '--upgrade' command, indicating whether to upgrade automatically in future. --auto-upgrade [0|1] Valid for '--upgrade' command, indicating whether to upgrade automatically in future.
--listen-v4 Force standalone/tls server to listen at ipv4. --listen-v4 Force standalone/tls server to listen at ipv4.
--listen-v6 Force standalone/tls server to listen at ipv6. --listen-v6 Force standalone/tls server to listen at ipv6.
--openssl-bin Specifies a custom openssl bin location. --openssl-bin Specifies a custom openssl bin location.
--use-wget Force to use wget, if you have both curl and wget installed.
" "
} }
...@@ -4006,7 +5097,10 @@ _installOnline() { ...@@ -4006,7 +5097,10 @@ _installOnline() {
fi fi
( (
_info "Extracting $localname" _info "Extracting $localname"
tar xzf $localname if ! (tar xzf $localname || gtar xzf $localname); then
_err "Extraction error."
exit 1
fi
cd "$PROJECT_NAME-$BRANCH" cd "$PROJECT_NAME-$BRANCH"
chmod +x $PROJECT_ENTRY chmod +x $PROJECT_ENTRY
...@@ -4050,9 +5144,9 @@ _processAccountConf() { ...@@ -4050,9 +5144,9 @@ _processAccountConf() {
fi fi
if [ "$_openssl_bin" ]; then if [ "$_openssl_bin" ]; then
_saveaccountconf "OPENSSL_BIN" "$_openssl_bin" _saveaccountconf "ACME_OPENSSL_BIN" "$_openssl_bin"
elif [ "$OPENSSL_BIN" ] && [ "$OPENSSL_BIN" != "$DEFAULT_OPENSSL_BIN" ]; then elif [ "$ACME_OPENSSL_BIN" ] && [ "$ACME_OPENSSL_BIN" != "$DEFAULT_OPENSSL_BIN" ]; then
_saveaccountconf "OPENSSL_BIN" "$OPENSSL_BIN" _saveaccountconf "ACME_OPENSSL_BIN" "$ACME_OPENSSL_BIN"
fi fi
if [ "$_auto_upgrade" ]; then if [ "$_auto_upgrade" ]; then
...@@ -4061,6 +5155,12 @@ _processAccountConf() { ...@@ -4061,6 +5155,12 @@ _processAccountConf() {
_saveaccountconf "AUTO_UPGRADE" "$AUTO_UPGRADE" _saveaccountconf "AUTO_UPGRADE" "$AUTO_UPGRADE"
fi fi
if [ "$_use_wget" ]; then
_saveaccountconf "ACME_USE_WGET" "$_use_wget"
elif [ "$ACME_USE_WGET" ]; then
_saveaccountconf "ACME_USE_WGET" "$ACME_USE_WGET"
fi
} }
_process() { _process() {
...@@ -4070,10 +5170,10 @@ _process() { ...@@ -4070,10 +5170,10 @@ _process() {
_webroot="" _webroot=""
_keylength="" _keylength=""
_accountkeylength="" _accountkeylength=""
_certpath="" _cert_file=""
_keypath="" _key_file=""
_capath="" _ca_file=""
_fullchainpath="" _fullchain_file=""
_reloadcmd="" _reloadcmd=""
_password="" _password=""
_accountconf="" _accountconf=""
...@@ -4081,6 +5181,7 @@ _process() { ...@@ -4081,6 +5181,7 @@ _process() {
_accountemail="" _accountemail=""
_accountkey="" _accountkey=""
_certhome="" _certhome=""
_confighome=""
_httpport="" _httpport=""
_tlsport="" _tlsport=""
_dnssleep="" _dnssleep=""
...@@ -4088,6 +5189,7 @@ _process() { ...@@ -4088,6 +5189,7 @@ _process() {
_stopRenewOnError="" _stopRenewOnError=""
#_insecure="" #_insecure=""
_ca_bundle="" _ca_bundle=""
_ca_path=""
_nocron="" _nocron=""
_ecc="" _ecc=""
_csr="" _csr=""
...@@ -4103,6 +5205,9 @@ _process() { ...@@ -4103,6 +5205,9 @@ _process() {
_listen_v4="" _listen_v4=""
_listen_v6="" _listen_v6=""
_openssl_bin="" _openssl_bin=""
_syslog=""
_use_wget=""
_server=""
while [ ${#} -gt 0 ]; do while [ ${#} -gt 0 ]; do
case "${1}" in case "${1}" in
...@@ -4135,25 +5240,28 @@ _process() { ...@@ -4135,25 +5240,28 @@ _process() {
--showcsr) --showcsr)
_CMD="showcsr" _CMD="showcsr"
;; ;;
--installcert | -i) --installcert | -i | --install-cert)
_CMD="installcert" _CMD="installcert"
;; ;;
--renew | -r) --renew | -r)
_CMD="renew" _CMD="renew"
;; ;;
--renewAll | --renewall) --renewAll | --renewall | --renew-all)
_CMD="renewAll" _CMD="renewAll"
;; ;;
--revoke) --revoke)
_CMD="revoke" _CMD="revoke"
;; ;;
--remove)
_CMD="remove"
;;
--list) --list)
_CMD="list" _CMD="list"
;; ;;
--installcronjob) --installcronjob | --install-cronjob)
_CMD="installcronjob" _CMD="installcronjob"
;; ;;
--uninstallcronjob) --uninstallcronjob | --uninstall-cronjob)
_CMD="uninstallcronjob" _CMD="uninstallcronjob"
;; ;;
--cron) --cron)
...@@ -4162,10 +5270,13 @@ _process() { ...@@ -4162,10 +5270,13 @@ _process() {
--toPkcs) --toPkcs)
_CMD="toPkcs" _CMD="toPkcs"
;; ;;
--createAccountKey | --createaccountkey | -cak) --toPkcs8)
_CMD="toPkcs8"
;;
--createAccountKey | --createaccountkey | -cak | --create-account-key)
_CMD="createAccountKey" _CMD="createAccountKey"
;; ;;
--createDomainKey | --createdomainkey | -cdk) --createDomainKey | --createdomainkey | -cdk | --create-domain-key)
_CMD="createDomainKey" _CMD="createDomainKey"
;; ;;
--createCSR | --createcsr | -ccr) --createCSR | --createcsr | -ccr)
...@@ -4174,12 +5285,15 @@ _process() { ...@@ -4174,12 +5285,15 @@ _process() {
--deactivate) --deactivate)
_CMD="deactivate" _CMD="deactivate"
;; ;;
--updateaccount) --updateaccount | --update-account)
_CMD="updateaccount" _CMD="updateaccount"
;; ;;
--registeraccount) --registeraccount | --register-account)
_CMD="registeraccount" _CMD="registeraccount"
;; ;;
--deactivate-account)
_CMD="deactivateaccount"
;;
--domain | -d) --domain | -d)
_dvalue="$2" _dvalue="$2"
...@@ -4213,14 +5327,23 @@ _process() { ...@@ -4213,14 +5327,23 @@ _process() {
--staging | --test) --staging | --test)
STAGE="1" STAGE="1"
;; ;;
--server)
ACME_DIRECTORY="$2"
_server="$ACME_DIRECTORY"
export ACME_DIRECTORY
shift
;;
--debug) --debug)
if [ -z "$2" ] || _startswith "$2" "-"; then if [ -z "$2" ] || _startswith "$2" "-"; then
DEBUG="1" DEBUG="$DEBUG_LEVEL_DEFAULT"
else else
DEBUG="$2" DEBUG="$2"
shift shift
fi fi
;; ;;
--output-insecure)
export OUTPUT_INSECURE=1
;;
--webroot | -w) --webroot | -w)
wvalue="$2" wvalue="$2"
if [ -z "$_webroot" ]; then if [ -z "$_webroot" ]; then
...@@ -4238,6 +5361,14 @@ _process() { ...@@ -4238,6 +5361,14 @@ _process() {
_webroot="$_webroot,$wvalue" _webroot="$_webroot,$wvalue"
fi fi
;; ;;
--stateless)
wvalue="$MODE_STATELESS"
if [ -z "$_webroot" ]; then
_webroot="$wvalue"
else
_webroot="$_webroot,$wvalue"
fi
;;
--local-address) --local-address)
lvalue="$2" lvalue="$2"
_local_address="$_local_address$lvalue," _local_address="$_local_address$lvalue,"
...@@ -4251,6 +5382,14 @@ _process() { ...@@ -4251,6 +5382,14 @@ _process() {
_webroot="$_webroot,$wvalue" _webroot="$_webroot,$wvalue"
fi fi
;; ;;
--nginx)
wvalue="$NGINX"
if [ -z "$_webroot" ]; then
_webroot="$wvalue"
else
_webroot="$_webroot,$wvalue"
fi
;;
--tls) --tls)
wvalue="$W_TLS" wvalue="$W_TLS"
if [ -z "$_webroot" ]; then if [ -z "$_webroot" ]; then
...@@ -4261,7 +5400,7 @@ _process() { ...@@ -4261,7 +5400,7 @@ _process() {
;; ;;
--dns) --dns)
wvalue="dns" wvalue="dns"
if ! _startswith "$2" "-"; then if [ "$2" ] && ! _startswith "$2" "-"; then
wvalue="$2" wvalue="$2"
shift shift
fi fi
...@@ -4286,20 +5425,20 @@ _process() { ...@@ -4286,20 +5425,20 @@ _process() {
shift shift
;; ;;
--certpath) --cert-file | --certpath)
_certpath="$2" _cert_file="$2"
shift shift
;; ;;
--keypath) --key-file | --keypath)
_keypath="$2" _key_file="$2"
shift shift
;; ;;
--capath) --ca-file | --capath)
_capath="$2" _ca_file="$2"
shift shift
;; ;;
--fullchainpath) --fullchain-file | --fullchainpath)
_fullchainpath="$2" _fullchain_file="$2"
shift shift
;; ;;
--reloadcmd | --reloadCmd) --reloadcmd | --reloadCmd)
...@@ -4319,11 +5458,16 @@ _process() { ...@@ -4319,11 +5458,16 @@ _process() {
LE_WORKING_DIR="$2" LE_WORKING_DIR="$2"
shift shift
;; ;;
--certhome) --certhome | --cert-home)
_certhome="$2" _certhome="$2"
CERT_HOME="$_certhome" CERT_HOME="$_certhome"
shift shift
;; ;;
--config-home)
_confighome="$2"
LE_CONFIG_HOME="$_confighome"
shift
;;
--useragent) --useragent)
_useragent="$2" _useragent="$2"
USER_AGENT="$_useragent" USER_AGENT="$_useragent"
...@@ -4366,13 +5510,21 @@ _process() { ...@@ -4366,13 +5510,21 @@ _process() {
HTTPS_INSECURE="1" HTTPS_INSECURE="1"
;; ;;
--ca-bundle) --ca-bundle)
_ca_bundle="$(readlink -f "$2")" _ca_bundle="$(_readlink -f "$2")"
CA_BUNDLE="$_ca_bundle" CA_BUNDLE="$_ca_bundle"
shift shift
;; ;;
--ca-path)
_ca_path="$2"
CA_PATH="$_ca_path"
shift
;;
--nocron) --nocron)
_nocron="1" _nocron="1"
;; ;;
--no-color)
export ACME_NO_COLOR=1
;;
--ecc) --ecc)
_ecc="isEcc" _ecc="isEcc"
;; ;;
...@@ -4393,11 +5545,23 @@ _process() { ...@@ -4393,11 +5545,23 @@ _process() {
shift shift
;; ;;
--deploy-hook) --deploy-hook)
_deploy_hook="$2" if [ -z "$2" ] || _startswith "$2" "-"; then
_usage "Please specify a value for '--deploy-hook'"
return 1
fi
_deploy_hook="$_deploy_hook$2,"
shift shift
;; ;;
--ocsp-must-staple | --ocsp) --ocsp-must-staple | --ocsp)
Le_OCSP_Stable="1" Le_OCSP_Staple="1"
;;
--always-force-new-domain-key)
if [ -z "$2" ] || _startswith "$2" "-"; then
Le_ForceNewDomainKey=1
else
Le_ForceNewDomainKey="$2"
shift
fi
;; ;;
--log | --logfile) --log | --logfile)
_log="1" _log="1"
...@@ -4417,6 +5581,15 @@ _process() { ...@@ -4417,6 +5581,15 @@ _process() {
LOG_LEVEL="$_log_level" LOG_LEVEL="$_log_level"
shift shift
;; ;;
--syslog)
if ! _startswith "$2" '-'; then
_syslog="$2"
shift
fi
if [ -z "$_syslog" ]; then
_syslog="$SYSLOG_LEVEL_DEFAULT"
fi
;;
--auto-upgrade) --auto-upgrade)
_auto_upgrade="$2" _auto_upgrade="$2"
if [ -z "$_auto_upgrade" ] || _startswith "$_auto_upgrade" '-'; then if [ -z "$_auto_upgrade" ] || _startswith "$_auto_upgrade" '-'; then
...@@ -4436,7 +5609,12 @@ _process() { ...@@ -4436,7 +5609,12 @@ _process() {
;; ;;
--openssl-bin) --openssl-bin)
_openssl_bin="$2" _openssl_bin="$2"
OPENSSL_BIN="$_openssl_bin" ACME_OPENSSL_BIN="$_openssl_bin"
shift
;;
--use-wget)
_use_wget="1"
ACME_USE_WGET="1"
;; ;;
*) *)
_err "Unknown parameter : $1" _err "Unknown parameter : $1"
...@@ -4464,6 +5642,21 @@ _process() { ...@@ -4464,6 +5642,21 @@ _process() {
LOG_LEVEL="$_log_level" LOG_LEVEL="$_log_level"
fi fi
if [ "$_syslog" ]; then
if _exists logger; then
if [ "$_syslog" = "0" ]; then
_clearaccountconf "SYS_LOG"
else
_saveaccountconf "SYS_LOG" "$_syslog"
fi
SYS_LOG="$_syslog"
else
_err "The 'logger' command is not found, can not enable syslog."
_clearaccountconf "SYS_LOG"
SYS_LOG=""
fi
fi
_processAccountConf _processAccountConf
fi fi
...@@ -4471,14 +5664,17 @@ _process() { ...@@ -4471,14 +5664,17 @@ _process() {
if [ "$DEBUG" ]; then if [ "$DEBUG" ]; then
version version
if [ "$_server" ]; then
_debug "Using server: $_server"
fi
fi fi
case "${_CMD}" in case "${_CMD}" in
install) install "$_nocron" ;; install) install "$_nocron" "$_confighome" ;;
uninstall) uninstall "$_nocron" ;; uninstall) uninstall "$_nocron" ;;
upgrade) upgrade ;; upgrade) upgrade ;;
issue) issue)
issue "$_webroot" "$_domain" "$_altdomains" "$_keylength" "$_certpath" "$_keypath" "$_capath" "$_reloadcmd" "$_fullchainpath" "$_pre_hook" "$_post_hook" "$_renew_hook" "$_local_address" issue "$_webroot" "$_domain" "$_altdomains" "$_keylength" "$_cert_file" "$_key_file" "$_ca_file" "$_reloadcmd" "$_fullchain_file" "$_pre_hook" "$_post_hook" "$_renew_hook" "$_local_address"
;; ;;
deploy) deploy)
deploy "$_domain" "$_deploy_hook" "$_ecc" deploy "$_domain" "$_deploy_hook" "$_ecc"
...@@ -4490,7 +5686,7 @@ _process() { ...@@ -4490,7 +5686,7 @@ _process() {
showcsr "$_csr" "$_domain" showcsr "$_csr" "$_domain"
;; ;;
installcert) installcert)
installcert "$_domain" "$_certpath" "$_keypath" "$_capath" "$_reloadcmd" "$_fullchainpath" "$_ecc" installcert "$_domain" "$_cert_file" "$_key_file" "$_ca_file" "$_reloadcmd" "$_fullchain_file" "$_ecc"
;; ;;
renew) renew)
renew "$_domain" "$_ecc" renew "$_domain" "$_ecc"
...@@ -4501,6 +5697,9 @@ _process() { ...@@ -4501,6 +5697,9 @@ _process() {
revoke) revoke)
revoke "$_domain" "$_ecc" revoke "$_domain" "$_ecc"
;; ;;
remove)
remove "$_domain" "$_ecc"
;;
deactivate) deactivate)
deactivate "$_domain,$_altdomains" deactivate "$_domain,$_altdomains"
;; ;;
...@@ -4510,15 +5709,21 @@ _process() { ...@@ -4510,15 +5709,21 @@ _process() {
updateaccount) updateaccount)
updateaccount updateaccount
;; ;;
deactivateaccount)
deactivateaccount
;;
list) list)
list "$_listraw" list "$_listraw"
;; ;;
installcronjob) installcronjob ;; installcronjob) installcronjob "$_confighome" ;;
uninstallcronjob) uninstallcronjob ;; uninstallcronjob) uninstallcronjob ;;
cron) cron ;; cron) cron ;;
toPkcs) toPkcs)
toPkcs "$_domain" "$_password" "$_ecc" toPkcs "$_domain" "$_password" "$_ecc"
;; ;;
toPkcs8)
toPkcs8 "$_domain" "$_ecc"
;;
createAccountKey) createAccountKey)
createAccountKey "$_accountkeylength" createAccountKey "$_accountkeylength"
;; ;;
...@@ -4530,7 +5735,9 @@ _process() { ...@@ -4530,7 +5735,9 @@ _process() {
;; ;;
*) *)
_err "Invalid command: $_CMD" if [ "$_CMD" ]; then
_err "Invalid command: $_CMD"
fi
showhelp showhelp
return 1 return 1
;; ;;
...@@ -4551,6 +5758,21 @@ _process() { ...@@ -4551,6 +5758,21 @@ _process() {
if [ "$_log_level" ]; then if [ "$_log_level" ]; then
_saveaccountconf "LOG_LEVEL" "$_log_level" _saveaccountconf "LOG_LEVEL" "$_log_level"
fi fi
if [ "$_syslog" ]; then
if _exists logger; then
if [ "$_syslog" = "0" ]; then
_clearaccountconf "SYS_LOG"
else
_saveaccountconf "SYS_LOG" "$_syslog"
fi
else
_err "The 'logger' command is not found, can not enable syslog."
_clearaccountconf "SYS_LOG"
SYS_LOG=""
fi
fi
_processAccountConf _processAccountConf
fi fi
...@@ -4558,7 +5780,7 @@ _process() { ...@@ -4558,7 +5780,7 @@ _process() {
if [ "$INSTALLONLINE" ]; then if [ "$INSTALLONLINE" ]; then
INSTALLONLINE="" INSTALLONLINE=""
_installOnline $BRANCH _installOnline
exit exit
fi fi
......
# Using deploy api
Before you can deploy your cert, you must [issue the cert first](https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert).
Here are the scripts to deploy the certs/key to the server/services.
## 1. Deploy the certs to your cpanel host
If you want to deploy using cpanel UAPI see 7.
(cpanel deploy hook is not finished yet, this is just an example.)
Then you can deploy now:
```sh
export DEPLOY_CPANEL_USER=myusername
export DEPLOY_CPANEL_PASSWORD=PASSWORD
acme.sh --deploy -d example.com --deploy-hook cpanel
```
## 2. Deploy ssl cert on kong proxy engine based on api
Before you can deploy your cert, you must [issue the cert first](https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert).
Currently supports Kong-v0.10.x.
```sh
acme.sh --deploy -d ftp.example.com --deploy-hook kong
```
## 3. Deploy the cert to remote server through SSH access
(TODO)
## 4. Deploy the cert to local vsftpd server
```sh
acme.sh --deploy -d ftp.example.com --deploy-hook vsftpd
```
The default vsftpd conf file is `/etc/vsftpd.conf`, if your vsftpd conf is not in the default location, you can specify one:
```sh
export DEPLOY_VSFTPD_CONF="/etc/vsftpd.conf"
acme.sh --deploy -d ftp.example.com --deploy-hook vsftpd
```
The default command to restart vsftpd server is `service vsftpd restart`, if it doesn't work, you can specify one:
```sh
export DEPLOY_VSFTPD_RELOAD="/etc/init.d/vsftpd restart"
acme.sh --deploy -d ftp.example.com --deploy-hook vsftpd
```
## 5. Deploy the cert to local exim4 server
```sh
acme.sh --deploy -d ftp.example.com --deploy-hook exim4
```
The default exim4 conf file is `/etc/exim/exim.conf`, if your exim4 conf is not in the default location, you can specify one:
```sh
export DEPLOY_EXIM4_CONF="/etc/exim4/exim4.conf.template"
acme.sh --deploy -d ftp.example.com --deploy-hook exim4
```
The default command to restart exim4 server is `service exim4 restart`, if it doesn't work, you can specify one:
```sh
export DEPLOY_EXIM4_RELOAD="/etc/init.d/exim4 restart"
acme.sh --deploy -d ftp.example.com --deploy-hook exim4
```
## 6. Deploy the cert to OSX Keychain
```sh
acme.sh --deploy -d ftp.example.com --deploy-hook keychain
```
## 7. Deploy to cpanel host using UAPI
This hook is using UAPI and works in cPanel & WHM version 56 or newer.
```
acme.sh --deploy -d example.com --deploy-hook cpanel_uapi
```
DEPLOY_CPANEL_USER is required only if you run the script as root and it should contain cpanel username.
```sh
export DEPLOY_CPANEL_USER=username
acme.sh --deploy -d example.com --deploy-hook cpanel_uapi
```
Please note, that the cpanel_uapi hook will deploy only the first domain when your certificate will automatically renew. Therefore you should issue a separete certificate for each domain.
## 8. Deploy the cert to your FRITZ!Box router
You must specify the credentials that have administrative privileges on the FRITZ!Box in order to deploy the certificate, plus the URL of your FRITZ!Box, through the following environment variables:
```sh
$ export DEPLOY_FRITZBOX_USERNAME=my_username
$ export DEPLOY_FRITZBOX_PASSWORD=the_password
$ export DEPLOY_FRITZBOX_URL=https://fritzbox.example.com
```
After the first deployment, these values will be stored in your $HOME/.acme.sh/account.conf. You may now deploy the certificate like this:
```sh
acme.sh --deploy -d fritzbox.example.com --deploy-hook fritzbox
```
## 9. Deploy the cert to strongswan
```sh
acme.sh --deploy -d ftp.example.com --deploy-hook strongswan
```
#!/usr/bin/env sh
#Here is a script to deploy cert to apache server.
#returns 0 means success, otherwise error.
######## Public functions #####################
#domain keyfile certfile cafile fullchain
apache_deploy() {
_cdomain="$1"
_ckey="$2"
_ccert="$3"
_cca="$4"
_cfullchain="$5"
_debug _cdomain "$_cdomain"
_debug _ckey "$_ckey"
_debug _ccert "$_ccert"
_debug _cca "$_cca"
_debug _cfullchain "$_cfullchain"
_err "Deploy cert to apache server, Not implemented yet"
return 1
}
#!/usr/bin/env sh
# Here is the script to deploy the cert to your cpanel using the cpanel API.
# Uses command line uapi. --user option is needed only if run as root.
# Returns 0 when success.
# Written by Santeri Kannisto <santeri.kannisto@2globalnomads.info>
# Public domain, 2017
#export DEPLOY_CPANEL_USER=myusername
######## Public functions #####################
#domain keyfile certfile cafile fullchain
cpanel_uapi_deploy() {
_cdomain="$1"
_ckey="$2"
_ccert="$3"
_cca="$4"
_cfullchain="$5"
_debug _cdomain "$_cdomain"
_debug _ckey "$_ckey"
_debug _ccert "$_ccert"
_debug _cca "$_cca"
_debug _cfullchain "$_cfullchain"
if ! _exists uapi; then
_err "The command uapi is not found."
return 1
fi
if ! _exists php; then
_err "The command php is not found."
return 1
fi
# read cert and key files and urlencode both
_certstr=$(cat "$_ccert")
_keystr=$(cat "$_ckey")
_cert=$(php -r "echo urlencode(\"$_certstr\");")
_key=$(php -r "echo urlencode(\"$_keystr\");")
_debug _cert "$_cert"
_debug _key "$_key"
if [ "$(id -u)" = 0 ]; then
if [ -z "$DEPLOY_CPANEL_USER" ]; then
_err "It seems that you are root, please define the target user name: export DEPLOY_CPANEL_USER=username"
return 1
fi
_savedomainconf DEPLOY_CPANEL_USER "$DEPLOY_CPANEL_USER"
_response=$(uapi --user="$DEPLOY_CPANEL_USER" SSL install_ssl domain="$_cdomain" cert="$_cert" key="$_key")
else
_response=$(uapi SSL install_ssl domain="$_cdomain" cert="$_cert" key="$_key")
fi
error_response="status: 0"
if test "${_response#*$error_response}" != "$_response"; then
_err "Error in deploying certificate:"
_err "$_response"
return 1
fi
_debug response "$_response"
_info "Certificate successfully deployed"
return 0
}
#!/usr/bin/env sh
#Here is a script to deploy cert to dovecot server.
#returns 0 means success, otherwise error.
######## Public functions #####################
#domain keyfile certfile cafile fullchain
dovecot_deploy() {
_cdomain="$1"
_ckey="$2"
_ccert="$3"
_cca="$4"
_cfullchain="$5"
_debug _cdomain "$_cdomain"
_debug _ckey "$_ckey"
_debug _ccert "$_ccert"
_debug _cca "$_cca"
_debug _cfullchain "$_cfullchain"
_err "Not implemented yet"
return 1
}
#!/usr/bin/env sh
#Here is a script to deploy cert to exim4 server.
#returns 0 means success, otherwise error.
#DEPLOY_EXIM4_CONF="/etc/exim/exim.conf"
#DEPLOY_EXIM4_RELOAD="service exim4 restart"
######## Public functions #####################
#domain keyfile certfile cafile fullchain
exim4_deploy() {
_cdomain="$1"
_ckey="$2"
_ccert="$3"
_cca="$4"
_cfullchain="$5"
_debug _cdomain "$_cdomain"
_debug _ckey "$_ckey"
_debug _ccert "$_ccert"
_debug _cca "$_cca"
_debug _cfullchain "$_cfullchain"
_ssl_path="/etc/acme.sh/exim4"
if ! mkdir -p "$_ssl_path"; then
_err "Can not create folder:$_ssl_path"
return 1
fi
_info "Copying key and cert"
_real_key="$_ssl_path/exim4.key"
if ! cat "$_ckey" >"$_real_key"; then
_err "Error: write key file to: $_real_key"
return 1
fi
_real_fullchain="$_ssl_path/exim4.pem"
if ! cat "$_cfullchain" >"$_real_fullchain"; then
_err "Error: write key file to: $_real_fullchain"
return 1
fi
DEFAULT_EXIM4_RELOAD="service exim4 restart"
_reload="${DEPLOY_EXIM4_RELOAD:-$DEFAULT_EXIM4_RELOAD}"
if [ -z "$IS_RENEW" ]; then
DEFAULT_EXIM4_CONF="/etc/exim/exim.conf"
if [ ! -f "$DEFAULT_EXIM4_CONF" ]; then
DEFAULT_EXIM4_CONF="/etc/exim4/exim4.conf.template"
fi
_exim4_conf="${DEPLOY_EXIM4_CONF:-$DEFAULT_EXIM4_CONF}"
_debug _exim4_conf "$_exim4_conf"
if [ ! -f "$_exim4_conf" ]; then
if [ -z "$DEPLOY_EXIM4_CONF" ]; then
_err "exim4 conf is not found, please define DEPLOY_EXIM4_CONF"
return 1
else
_err "It seems that the specified exim4 conf is not valid, please check."
return 1
fi
fi
if [ ! -w "$_exim4_conf" ]; then
_err "The file $_exim4_conf is not writable, please change the permission."
return 1
fi
_backup_conf="$DOMAIN_BACKUP_PATH/exim4.conf.bak"
_info "Backup $_exim4_conf to $_backup_conf"
cp "$_exim4_conf" "$_backup_conf"
_info "Modify exim4 conf: $_exim4_conf"
if _setopt "$_exim4_conf" "tls_certificate" "=" "$_real_fullchain" \
&& _setopt "$_exim4_conf" "tls_privatekey" "=" "$_real_key"; then
_info "Set config success!"
else
_err "Config exim4 server error, please report bug to us."
_info "Restoring exim4 conf"
if cat "$_backup_conf" >"$_exim4_conf"; then
_info "Restore conf success"
eval "$_reload"
else
_err "Oops, error restore exim4 conf, please report bug to us."
fi
return 1
fi
fi
_info "Run reload: $_reload"
if eval "$_reload"; then
_info "Reload success!"
if [ "$DEPLOY_EXIM4_CONF" ]; then
_savedomainconf DEPLOY_EXIM4_CONF "$DEPLOY_EXIM4_CONF"
else
_cleardomainconf DEPLOY_EXIM4_CONF
fi
if [ "$DEPLOY_EXIM4_RELOAD" ]; then
_savedomainconf DEPLOY_EXIM4_RELOAD "$DEPLOY_EXIM4_RELOAD"
else
_cleardomainconf DEPLOY_EXIM4_RELOAD
fi
return 0
else
_err "Reload error, restoring"
if cat "$_backup_conf" >"$_exim4_conf"; then
_info "Restore conf success"
eval "$_reload"
else
_err "Oops, error restore exim4 conf, please report bug to us."
fi
return 1
fi
return 0
}
#!/usr/bin/env sh
#Here is a script to deploy cert to an AVM FRITZ!Box router.
#returns 0 means success, otherwise error.
#DEPLOY_FRITZBOX_USERNAME="username"
#DEPLOY_FRITZBOX_PASSWORD="password"
#DEPLOY_FRITZBOX_URL="https://fritz.box"
# Kudos to wikrie at Github for his FRITZ!Box update script:
# https://gist.github.com/wikrie/f1d5747a714e0a34d0582981f7cb4cfb
######## Public functions #####################
#domain keyfile certfile cafile fullchain
fritzbox_deploy() {
_cdomain="$1"
_ckey="$2"
_ccert="$3"
_cca="$4"
_cfullchain="$5"
_debug _cdomain "$_cdomain"
_debug _ckey "$_ckey"
_debug _ccert "$_ccert"
_debug _cca "$_cca"
_debug _cfullchain "$_cfullchain"
if ! _exists iconv; then
_err "iconv not found"
return 1
fi
_fritzbox_username="${DEPLOY_FRITZBOX_USERNAME}"
_fritzbox_password="${DEPLOY_FRITZBOX_PASSWORD}"
_fritzbox_url="${DEPLOY_FRITZBOX_URL}"
_debug _fritzbox_url "$_fritzbox_url"
_debug _fritzbox_username "$_fritzbox_username"
_secure_debug _fritzbox_password "$_fritzbox_password"
if [ -z "$_fritzbox_username" ]; then
_err "FRITZ!Box username is not found, please define DEPLOY_FRITZBOX_USERNAME."
return 1
fi
if [ -z "$_fritzbox_password" ]; then
_err "FRITZ!Box password is not found, please define DEPLOY_FRITZBOX_PASSWORD."
return 1
fi
if [ -z "$_fritzbox_url" ]; then
_err "FRITZ!Box url is not found, please define DEPLOY_FRITZBOX_URL."
return 1
fi
_saveaccountconf DEPLOY_FRITZBOX_USERNAME "${_fritzbox_username}"
_saveaccountconf DEPLOY_FRITZBOX_PASSWORD "${_fritzbox_password}"
_saveaccountconf DEPLOY_FRITZBOX_URL "${_fritzbox_url}"
# Do not check for a valid SSL certificate, because initially the cert is not valid, so it could not install the LE generated certificate
export HTTPS_INSECURE=1
_info "Log in to the FRITZ!Box"
_fritzbox_challenge="$(_get "${_fritzbox_url}/login_sid.lua" | sed -e 's/^.*<Challenge>//' -e 's/<\/Challenge>.*$//')"
_fritzbox_hash="$(printf "%s-%s" "${_fritzbox_challenge}" "${_fritzbox_password}" | iconv -f ASCII -t UTF16LE | md5sum | awk '{print $1}')"
_fritzbox_sid="$(_get "${_fritzbox_url}/login_sid.lua?sid=0000000000000000&username=${_fritzbox_username}&response=${_fritzbox_challenge}-${_fritzbox_hash}" | sed -e 's/^.*<SID>//' -e 's/<\/SID>.*$//')"
if [ -z "${_fritzbox_sid}" ] || [ "${_fritzbox_sid}" = "0000000000000000" ]; then
_err "Logging in to the FRITZ!Box failed. Please check username, password and URL."
return 1
fi
_info "Generate form POST request"
_post_request="$(_mktemp)"
_post_boundary="---------------------------$(date +%Y%m%d%H%M%S)"
# _CERTPASSWORD_ is unset because Let's Encrypt certificates don't have a password. But if they ever do, here's the place to use it!
_CERTPASSWORD_=
{
printf -- "--"
printf -- "%s\r\n" "${_post_boundary}"
printf "Content-Disposition: form-data; name=\"sid\"\r\n\r\n%s\r\n" "${_fritzbox_sid}"
printf -- "--"
printf -- "%s\r\n" "${_post_boundary}"
printf "Content-Disposition: form-data; name=\"BoxCertPassword\"\r\n\r\n%s\r\n" "${_CERTPASSWORD_}"
printf -- "--"
printf -- "%s\r\n" "${_post_boundary}"
printf "Content-Disposition: form-data; name=\"BoxCertImportFile\"; filename=\"BoxCert.pem\"\r\n"
printf "Content-Type: application/octet-stream\r\n\r\n"
cat "${_ckey}" "${_cfullchain}"
printf "\r\n"
printf -- "--"
printf -- "%s--" "${_post_boundary}"
} >>"${_post_request}"
_info "Upload certificate to the FRITZ!Box"
export _H1="Content-type: multipart/form-data boundary=${_post_boundary}"
_post "$(cat "${_post_request}")" "${_fritzbox_url}/cgi-bin/firmwarecfg" | grep SSL
retval=$?
if [ $retval = 0 ]; then
_info "Upload successful"
else
_err "Upload failed"
fi
rm "${_post_request}"
return $retval
}
#!/usr/bin/env sh
#Here is a script to deploy cert to haproxy server.
#returns 0 means success, otherwise error.
######## Public functions #####################
#domain keyfile certfile cafile fullchain
haproxy_deploy() {
_cdomain="$1"
_ckey="$2"
_ccert="$3"
_cca="$4"
_cfullchain="$5"
_debug _cdomain "$_cdomain"
_debug _ckey "$_ckey"
_debug _ccert "$_ccert"
_debug _cca "$_cca"
_debug _cfullchain "$_cfullchain"
_err "deploy cert to haproxy server, Not implemented yet"
return 1
}
#!/usr/bin/env sh
#Here is a sample custom api script.
#This file name is "myapi.sh"
#So, here must be a method myapi_deploy()
#Which will be called by acme.sh to deploy the cert
#returns 0 means success, otherwise error.
######## Public functions #####################
#domain keyfile certfile cafile fullchain
keychain_deploy() {
_cdomain="$1"
_ckey="$2"
_ccert="$3"
_cca="$4"
_cfullchain="$5"
_debug _cdomain "$_cdomain"
_debug _ckey "$_ckey"
_debug _ccert "$_ccert"
_debug _cca "$_cca"
_debug _cfullchain "$_cfullchain"
/usr/bin/security import "$_ckey" -k "/Library/Keychains/System.keychain"
/usr/bin/security import "$_ccert" -k "/Library/Keychains/System.keychain"
/usr/bin/security import "$_cca" -k "/Library/Keychains/System.keychain"
/usr/bin/security import "$_cfullchain" -k "/Library/Keychains/System.keychain"
return 0
}
#!/usr/bin/env sh
# If certificate already exist it will update only cert and key not touching other parameter
# If certificate doesn't exist it will only upload cert and key and not set other parameter
# Note that we deploy full chain
# Written by Geoffroi Genot <ggenot@voxbone.com>
######## Public functions #####################
#domain keyfile certfile cafile fullchain
kong_deploy() {
_cdomain="$1"
_ckey="$2"
_ccert="$3"
_cca="$4"
_cfullchain="$5"
_info "Deploying certificate on Kong instance"
if [ -z "$KONG_URL" ]; then
_debug "KONG_URL Not set, using default http://localhost:8001"
KONG_URL="http://localhost:8001"
fi
_debug _cdomain "$_cdomain"
_debug _ckey "$_ckey"
_debug _ccert "$_ccert"
_debug _cca "$_cca"
_debug _cfullchain "$_cfullchain"
#Get ssl_uuid linked to the domain
ssl_uuid=$(_get "$KONG_URL/certificates/$_cdomain" | _normalizeJson | _egrep_o '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}')
if [ -z "$ssl_uuid" ]; then
_debug "Unable to get Kong ssl_uuid for domain $_cdomain"
_debug "Make sure that KONG_URL is correctly configured"
_debug "Make sure that a Kong certificate match the sni"
_debug "Kong url: $KONG_URL"
_info "No existing certificate, creating..."
#return 1
fi
#Save kong url if it's succesful (First run case)
_saveaccountconf KONG_URL "$KONG_URL"
#Generate DEIM
delim="-----MultipartDelimiter$(date "+%s%N")"
nl="\015\012"
#Set Header
_H1="Content-Type: multipart/form-data; boundary=$delim"
#Generate data for request (Multipart/form-data with mixed content)
if [ -z "$ssl_uuid" ]; then
#set sni to domain
content="--$delim${nl}Content-Disposition: form-data; name=\"snis\"${nl}${nl}$_cdomain"
fi
#add key
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"key\"; filename=\"$(basename "$_ckey")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_ckey")"
#Add cert
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"cert\"; filename=\"$(basename "$_cfullchain")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_cfullchain")"
#Close multipart
content="$content${nl}--$delim--${nl}"
#Convert CRLF
content=$(printf %b "$content")
#DEBUG
_debug header "$_H1"
_debug content "$content"
#Check if sslcreated (if not => POST else => PATCH)
if [ -z "$ssl_uuid" ]; then
#Post certificate to Kong
response=$(_post "$content" "$KONG_URL/certificates" "" "POST")
else
#patch
response=$(_post "$content" "$KONG_URL/certificates/$ssl_uuid" "" "PATCH")
fi
if ! [ "$(echo "$response" | _egrep_o "created_at")" = "created_at" ]; then
_err "An error occurred with cert upload. Check response:"
_err "$response"
return 1
fi
_debug response "$response"
_info "Certificate successfully deployed"
}
File mode changed from 100644 to 100755
#!/usr/bin/env sh
#Here is a script to deploy cert to mysqld server.
#returns 0 means success, otherwise error.
######## Public functions #####################
#domain keyfile certfile cafile fullchain
mysqld_deploy() {
_cdomain="$1"
_ckey="$2"
_ccert="$3"
_cca="$4"
_cfullchain="$5"
_debug _cdomain "$_cdomain"
_debug _ckey "$_ckey"
_debug _ccert "$_ccert"
_debug _cca "$_cca"
_debug _cfullchain "$_cfullchain"
_err "deploy cert to mysqld server, Not implemented yet"
return 1
}
#!/usr/bin/env sh
#Here is a script to deploy cert to nginx server.
#returns 0 means success, otherwise error.
######## Public functions #####################
#domain keyfile certfile cafile fullchain
nginx_deploy() {
_cdomain="$1"
_ckey="$2"
_ccert="$3"
_cca="$4"
_cfullchain="$5"
_debug _cdomain "$_cdomain"
_debug _ckey "$_ckey"
_debug _ccert "$_ccert"
_debug _cca "$_cca"
_debug _cfullchain "$_cfullchain"
_err "deploy cert to nginx server, Not implemented yet"
return 1
}
#!/usr/bin/env sh
#Here is a script to deploy cert to opensshd server.
#returns 0 means success, otherwise error.
######## Public functions #####################
#domain keyfile certfile cafile fullchain
opensshd_deploy() {
_cdomain="$1"
_ckey="$2"
_ccert="$3"
_cca="$4"
_cfullchain="$5"
_debug _cdomain "$_cdomain"
_debug _ckey "$_ckey"
_debug _ccert "$_ccert"
_debug _cca "$_cca"
_debug _cfullchain "$_cfullchain"
_err "deploy cert to opensshd server, Not implemented yet"
return 1
}
#!/usr/bin/env sh
#Here is a script to deploy cert to pureftpd server.
#returns 0 means success, otherwise error.
######## Public functions #####################
#domain keyfile certfile cafile fullchain
pureftpd_deploy() {
_cdomain="$1"
_ckey="$2"
_ccert="$3"
_cca="$4"
_cfullchain="$5"
_debug _cdomain "$_cdomain"
_debug _ckey "$_ckey"
_debug _ccert "$_ccert"
_debug _cca "$_cca"
_debug _cfullchain "$_cfullchain"
_err "deploy cert to pureftpd server, Not implemented yet"
return 1
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment