committed by
GitHub
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
117 changed files with 11251 additions and 2497 deletions
-
40.github/auto-comment.yml
-
254.github/workflows/DNS.yml
-
63.github/workflows/FreeBSD.yml
-
41.github/workflows/Linux.yml
-
55.github/workflows/MacOS.yml
-
62.github/workflows/PebbleStrict.yml
-
61.github/workflows/Solaris.yml
-
86.github/workflows/Ubuntu.yml
-
73.github/workflows/Windows.yml
-
59.github/workflows/dockerhub.yml
-
33.github/workflows/shellcheck.yml
-
37.travis.yml
-
18Dockerfile
-
93README.md
-
1635acme.sh
-
92deploy/cleverreach.sh
-
98deploy/consul.sh
-
1deploy/docker.sh
-
4deploy/exim4.sh
-
48deploy/fritzbox.sh
-
13deploy/gcore_cdn.sh
-
25deploy/haproxy.sh
-
2deploy/kong.sh
-
280deploy/lighttpd.sh
-
23deploy/mailcow.sh
-
156deploy/openmediavault.sh
-
262deploy/openstack.sh
-
123deploy/peplink.sh
-
62deploy/routeros.sh
-
37deploy/ssh.sh
-
79deploy/synology_dsm.sh
-
180deploy/truenas.sh
-
194deploy/unifi.sh
-
78deploy/vault.sh
-
12deploy/vault_cli.sh
-
6deploy/vsftpd.sh
-
171dnsapi/dns_1984hosting.sh
-
63dnsapi/dns_acmedns.sh
-
150dnsapi/dns_anx.sh
-
41dnsapi/dns_arvan.sh
-
171dnsapi/dns_aurora.sh
-
14dnsapi/dns_aws.sh
-
204dnsapi/dns_azion.sh
-
64dnsapi/dns_azure.sh
-
6dnsapi/dns_cf.sh
-
30dnsapi/dns_cloudns.sh
-
6dnsapi/dns_conoha.sh
-
43dnsapi/dns_constellix.sh
-
159dnsapi/dns_cpanel.sh
-
159dnsapi/dns_curanet.sh
-
32dnsapi/dns_cyon.sh
-
4dnsapi/dns_ddnss.sh
-
29dnsapi/dns_desec.sh
-
6dnsapi/dns_dgon.sh
-
87dnsapi/dns_dnshome.sh
-
24dnsapi/dns_do.sh
-
2dnsapi/dns_dp.sh
-
16dnsapi/dns_dpi.sh
-
13dnsapi/dns_duckdns.sh
-
238dnsapi/dns_dynv6.sh
-
466dnsapi/dns_edgedns.sh
-
18dnsapi/dns_freedns.sh
-
16dnsapi/dns_gandi_livedns.sh
-
17dnsapi/dns_gcloud.sh
-
221dnsapi/dns_geoscaling.sh
-
6dnsapi/dns_he.sh
-
8dnsapi/dns_hetzner.sh
-
289dnsapi/dns_huaweicloud.sh
-
25dnsapi/dns_infoblox.sh
-
199dnsapi/dns_infomaniak.sh
-
163dnsapi/dns_ionos.sh
-
24dnsapi/dns_ispconfig.sh
-
150dnsapi/dns_kappernet.sh
-
6dnsapi/dns_knot.sh
-
3dnsapi/dns_linode_v4.sh
-
48dnsapi/dns_loopia.sh
-
4dnsapi/dns_misaka.sh
-
261dnsapi/dns_mythic_beasts.sh
-
10dnsapi/dns_namecheap.sh
-
8dnsapi/dns_netcup.sh
-
162dnsapi/dns_netlify.sh
-
2dnsapi/dns_nic.sh
-
2dnsapi/dns_nsd.sh
-
324dnsapi/dns_oci.sh
-
56dnsapi/dns_one.sh
-
348dnsapi/dns_openstack.sh
-
3dnsapi/dns_opnsense.sh
-
6dnsapi/dns_ovh.sh
-
11dnsapi/dns_pdns.sh
-
39dnsapi/dns_pleskxml.sh
-
157dnsapi/dns_porkbun.sh
-
156dnsapi/dns_rackcorp.sh
-
5dnsapi/dns_rackspace.sh
-
15dnsapi/dns_regru.sh
-
176dnsapi/dns_scaleway.sh
-
26dnsapi/dns_servercow.sh
-
263dnsapi/dns_simply.sh
-
160dnsapi/dns_udr.sh
-
158dnsapi/dns_veesp.sh
-
6dnsapi/dns_vultr.sh
@ -1,40 +0,0 @@ |
|||||
# Comment to a new issue. |
|
||||
issuesOpened: > |
|
||||
If this is a bug report, please upgrade to the latest code and try again: |
|
||||
|
|
||||
如果有 bug, 请先更新到最新版试试: |
|
||||
|
|
||||
``` |
|
||||
acme.sh --upgrade |
|
||||
``` |
|
||||
|
|
||||
please also provide the log with `--debug 2`. |
|
||||
|
|
||||
同时请提供调试输出 `--debug 2` |
|
||||
|
|
||||
see: https://github.com/acmesh-official/acme.sh/wiki/How-to-debug-acme.sh |
|
||||
|
|
||||
Without `--debug 2` log, your issue will NEVER get replied. |
|
||||
|
|
||||
没有调试输出, 你的 issue 不会得到任何解答. |
|
||||
|
|
||||
|
|
||||
pullRequestOpened: > |
|
||||
First, NEVER send a PR to `master` branch, it will NEVER be accepted. Please send to the `dev` branch instead. |
|
||||
|
|
||||
If this is a PR to support new DNS API or new notification API, please read this guide first: |
|
||||
https://github.com/acmesh-official/acme.sh/wiki/DNS-API-Dev-Guide |
|
||||
|
|
||||
Please check the guide items one by one. |
|
||||
|
|
||||
Then add your usage here: |
|
||||
https://github.com/acmesh-official/acme.sh/wiki/dnsapi |
|
||||
|
|
||||
Or some other wiki pages: |
|
||||
|
|
||||
https://github.com/acmesh-official/acme.sh/wiki/deployhooks |
|
||||
|
|
||||
https://github.com/acmesh-official/acme.sh/wiki/notify |
|
||||
|
|
||||
|
|
||||
|
|
||||
@ -0,0 +1,254 @@ |
|||||
|
name: DNS |
||||
|
on: |
||||
|
push: |
||||
|
paths: |
||||
|
- 'dnsapi/*.sh' |
||||
|
- '.github/workflows/DNS.yml' |
||||
|
pull_request: |
||||
|
branches: |
||||
|
- 'dev' |
||||
|
paths: |
||||
|
- 'dnsapi/*.sh' |
||||
|
- '.github/workflows/DNS.yml' |
||||
|
|
||||
|
|
||||
|
jobs: |
||||
|
CheckToken: |
||||
|
runs-on: ubuntu-latest |
||||
|
outputs: |
||||
|
hasToken: ${{ steps.step_one.outputs.hasToken }} |
||||
|
steps: |
||||
|
- name: Set the value |
||||
|
id: step_one |
||||
|
run: | |
||||
|
if [ "${{secrets.TokenName1}}" ] ; then |
||||
|
echo "::set-output name=hasToken::true" |
||||
|
else |
||||
|
echo "::set-output name=hasToken::false" |
||||
|
fi |
||||
|
- name: Check the value |
||||
|
run: echo ${{ steps.step_one.outputs.hasToken }} |
||||
|
|
||||
|
Fail: |
||||
|
runs-on: ubuntu-latest |
||||
|
needs: CheckToken |
||||
|
if: "contains(needs.CheckToken.outputs.hasToken, 'false')" |
||||
|
steps: |
||||
|
- name: "Read this: https://github.com/acmesh-official/acme.sh/wiki/DNS-API-Test" |
||||
|
run: | |
||||
|
echo "Read this: https://github.com/acmesh-official/acme.sh/wiki/DNS-API-Test" |
||||
|
if [ "${{github.repository_owner}}" != "acmesh-official" ]; then |
||||
|
false |
||||
|
fi |
||||
|
|
||||
|
Docker: |
||||
|
runs-on: ubuntu-latest |
||||
|
needs: CheckToken |
||||
|
if: "contains(needs.CheckToken.outputs.hasToken, 'true')" |
||||
|
env: |
||||
|
TEST_DNS : ${{ secrets.TEST_DNS }} |
||||
|
TestingDomain: ${{ secrets.TestingDomain }} |
||||
|
TEST_DNS_NO_WILDCARD: ${{ secrets.TEST_DNS_NO_WILDCARD }} |
||||
|
TEST_DNS_NO_SUBDOMAIN: ${{ secrets.TEST_DNS_NO_SUBDOMAIN }} |
||||
|
TEST_DNS_SLEEP: ${{ secrets.TEST_DNS_SLEEP }} |
||||
|
CASE: le_test_dnsapi |
||||
|
TEST_LOCAL: 1 |
||||
|
DEBUG: 1 |
||||
|
steps: |
||||
|
- uses: actions/checkout@v2 |
||||
|
- name: Clone acmetest |
||||
|
run: cd .. && git clone https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/ |
||||
|
- name: Set env file |
||||
|
run: | |
||||
|
cd ../acmetest |
||||
|
if [ "${{ secrets.TokenName1}}" ] ; then |
||||
|
echo "${{ secrets.TokenName1}}=${{ secrets.TokenValue1}}" >> docker.env |
||||
|
fi |
||||
|
if [ "${{ secrets.TokenName2}}" ] ; then |
||||
|
echo "${{ secrets.TokenName2}}=${{ secrets.TokenValue2}}" >> docker.env |
||||
|
fi |
||||
|
if [ "${{ secrets.TokenName3}}" ] ; then |
||||
|
echo "${{ secrets.TokenName3}}=${{ secrets.TokenValue3}}" >> docker.env |
||||
|
fi |
||||
|
if [ "${{ secrets.TokenName4}}" ] ; then |
||||
|
echo "${{ secrets.TokenName4}}=${{ secrets.TokenValue4}}" >> docker.env |
||||
|
fi |
||||
|
if [ "${{ secrets.TokenName5}}" ] ; then |
||||
|
echo "${{ secrets.TokenName5}}=${{ secrets.TokenValue5}}" >> docker.env |
||||
|
fi |
||||
|
echo "TEST_DNS_NO_WILDCARD" >> docker.env |
||||
|
echo "TEST_DNS_SLEEP" >> docker.env |
||||
|
- name: Run acmetest |
||||
|
run: cd ../acmetest && ./rundocker.sh testall |
||||
|
|
||||
|
MacOS: |
||||
|
runs-on: macos-latest |
||||
|
needs: Docker |
||||
|
env: |
||||
|
TEST_DNS : ${{ secrets.TEST_DNS }} |
||||
|
TestingDomain: ${{ secrets.TestingDomain }} |
||||
|
TEST_DNS_NO_WILDCARD: ${{ secrets.TEST_DNS_NO_WILDCARD }} |
||||
|
TEST_DNS_NO_SUBDOMAIN: ${{ secrets.TEST_DNS_NO_SUBDOMAIN }} |
||||
|
TEST_DNS_SLEEP: ${{ secrets.TEST_DNS_SLEEP }} |
||||
|
CASE: le_test_dnsapi |
||||
|
TEST_LOCAL: 1 |
||||
|
DEBUG: 1 |
||||
|
steps: |
||||
|
- uses: actions/checkout@v2 |
||||
|
- name: Install tools |
||||
|
run: brew install socat |
||||
|
- name: Clone acmetest |
||||
|
run: cd .. && git clone https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/ |
||||
|
- name: Run acmetest |
||||
|
run: | |
||||
|
if [ "${{ secrets.TokenName1}}" ] ; then |
||||
|
export ${{ secrets.TokenName1}}=${{ secrets.TokenValue1}} |
||||
|
fi |
||||
|
if [ "${{ secrets.TokenName2}}" ] ; then |
||||
|
export ${{ secrets.TokenName2}}=${{ secrets.TokenValue2}} |
||||
|
fi |
||||
|
if [ "${{ secrets.TokenName3}}" ] ; then |
||||
|
export ${{ secrets.TokenName3}}=${{ secrets.TokenValue3}} |
||||
|
fi |
||||
|
if [ "${{ secrets.TokenName4}}" ] ; then |
||||
|
export ${{ secrets.TokenName4}}=${{ secrets.TokenValue4}} |
||||
|
fi |
||||
|
if [ "${{ secrets.TokenName5}}" ] ; then |
||||
|
export ${{ secrets.TokenName5}}=${{ secrets.TokenValue5}} |
||||
|
fi |
||||
|
cd ../acmetest |
||||
|
./letest.sh |
||||
|
|
||||
|
Windows: |
||||
|
runs-on: windows-latest |
||||
|
needs: MacOS |
||||
|
env: |
||||
|
TEST_DNS : ${{ secrets.TEST_DNS }} |
||||
|
TestingDomain: ${{ secrets.TestingDomain }} |
||||
|
TEST_DNS_NO_WILDCARD: ${{ secrets.TEST_DNS_NO_WILDCARD }} |
||||
|
TEST_DNS_NO_SUBDOMAIN: ${{ secrets.TEST_DNS_NO_SUBDOMAIN }} |
||||
|
TEST_DNS_SLEEP: ${{ secrets.TEST_DNS_SLEEP }} |
||||
|
CASE: le_test_dnsapi |
||||
|
TEST_LOCAL: 1 |
||||
|
DEBUG: 1 |
||||
|
steps: |
||||
|
- name: Set git to use LF |
||||
|
run: | |
||||
|
git config --global core.autocrlf false |
||||
|
- uses: actions/checkout@v2 |
||||
|
- name: Install cygwin base packages with chocolatey |
||||
|
run: | |
||||
|
choco config get cacheLocation |
||||
|
choco install --no-progress cygwin |
||||
|
shell: cmd |
||||
|
- name: Install cygwin additional packages |
||||
|
run: | |
||||
|
C:\tools\cygwin\cygwinsetup.exe -qgnNdO -R C:/tools/cygwin -s http://mirrors.kernel.org/sourceware/cygwin/ -P socat,curl,cron,unzip,git |
||||
|
shell: cmd |
||||
|
- name: Set ENV |
||||
|
shell: cmd |
||||
|
run: | |
||||
|
echo PATH=C:\tools\cygwin\bin;C:\tools\cygwin\usr\bin >> %GITHUB_ENV% |
||||
|
- name: Clone acmetest |
||||
|
run: cd .. && git clone https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/ |
||||
|
- name: Run acmetest |
||||
|
shell: bash |
||||
|
run: | |
||||
|
if [ "${{ secrets.TokenName1}}" ] ; then |
||||
|
export ${{ secrets.TokenName1}}=${{ secrets.TokenValue1}} |
||||
|
fi |
||||
|
if [ "${{ secrets.TokenName2}}" ] ; then |
||||
|
export ${{ secrets.TokenName2}}=${{ secrets.TokenValue2}} |
||||
|
fi |
||||
|
if [ "${{ secrets.TokenName3}}" ] ; then |
||||
|
export ${{ secrets.TokenName3}}=${{ secrets.TokenValue3}} |
||||
|
fi |
||||
|
if [ "${{ secrets.TokenName4}}" ] ; then |
||||
|
export ${{ secrets.TokenName4}}=${{ secrets.TokenValue4}} |
||||
|
fi |
||||
|
if [ "${{ secrets.TokenName5}}" ] ; then |
||||
|
export ${{ secrets.TokenName5}}=${{ secrets.TokenValue5}} |
||||
|
fi |
||||
|
cd ../acmetest |
||||
|
./letest.sh |
||||
|
|
||||
|
FreeBSD: |
||||
|
runs-on: macos-10.15 |
||||
|
needs: Windows |
||||
|
env: |
||||
|
TEST_DNS : ${{ secrets.TEST_DNS }} |
||||
|
TestingDomain: ${{ secrets.TestingDomain }} |
||||
|
TEST_DNS_NO_WILDCARD: ${{ secrets.TEST_DNS_NO_WILDCARD }} |
||||
|
TEST_DNS_NO_SUBDOMAIN: ${{ secrets.TEST_DNS_NO_SUBDOMAIN }} |
||||
|
TEST_DNS_SLEEP: ${{ secrets.TEST_DNS_SLEEP }} |
||||
|
CASE: le_test_dnsapi |
||||
|
TEST_LOCAL: 1 |
||||
|
DEBUG: 1 |
||||
|
steps: |
||||
|
- uses: actions/checkout@v2 |
||||
|
- name: Clone acmetest |
||||
|
run: cd .. && git clone https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/ |
||||
|
- uses: vmactions/freebsd-vm@v0.1.4 |
||||
|
with: |
||||
|
envs: 'TEST_DNS TestingDomain TEST_DNS_NO_WILDCARD TEST_DNS_NO_SUBDOMAIN TEST_DNS_SLEEP CASE TEST_LOCAL DEBUG ${{ secrets.TokenName1}} ${{ secrets.TokenName2}} ${{ secrets.TokenName3}} ${{ secrets.TokenName4}} ${{ secrets.TokenName5}}' |
||||
|
prepare: pkg install -y socat curl |
||||
|
usesh: true |
||||
|
run: | |
||||
|
if [ "${{ secrets.TokenName1}}" ] ; then |
||||
|
export ${{ secrets.TokenName1}}=${{ secrets.TokenValue1}} |
||||
|
fi |
||||
|
if [ "${{ secrets.TokenName2}}" ] ; then |
||||
|
export ${{ secrets.TokenName2}}=${{ secrets.TokenValue2}} |
||||
|
fi |
||||
|
if [ "${{ secrets.TokenName3}}" ] ; then |
||||
|
export ${{ secrets.TokenName3}}=${{ secrets.TokenValue3}} |
||||
|
fi |
||||
|
if [ "${{ secrets.TokenName4}}" ] ; then |
||||
|
export ${{ secrets.TokenName4}}=${{ secrets.TokenValue4}} |
||||
|
fi |
||||
|
if [ "${{ secrets.TokenName5}}" ] ; then |
||||
|
export ${{ secrets.TokenName5}}=${{ secrets.TokenValue5}} |
||||
|
fi |
||||
|
cd ../acmetest |
||||
|
./letest.sh |
||||
|
|
||||
|
Solaris: |
||||
|
runs-on: macos-10.15 |
||||
|
needs: FreeBSD |
||||
|
env: |
||||
|
TEST_DNS : ${{ secrets.TEST_DNS }} |
||||
|
TestingDomain: ${{ secrets.TestingDomain }} |
||||
|
TEST_DNS_NO_WILDCARD: ${{ secrets.TEST_DNS_NO_WILDCARD }} |
||||
|
TEST_DNS_NO_SUBDOMAIN: ${{ secrets.TEST_DNS_NO_SUBDOMAIN }} |
||||
|
TEST_DNS_SLEEP: ${{ secrets.TEST_DNS_SLEEP }} |
||||
|
CASE: le_test_dnsapi |
||||
|
TEST_LOCAL: 1 |
||||
|
DEBUG: 1 |
||||
|
steps: |
||||
|
- uses: actions/checkout@v2 |
||||
|
- name: Clone acmetest |
||||
|
run: cd .. && git clone https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/ |
||||
|
- uses: vmactions/solaris-vm@v0.0.5 |
||||
|
with: |
||||
|
envs: 'TEST_DNS TestingDomain TEST_DNS_NO_WILDCARD TEST_DNS_NO_SUBDOMAIN TEST_DNS_SLEEP CASE TEST_LOCAL DEBUG ${{ secrets.TokenName1}} ${{ secrets.TokenName2}} ${{ secrets.TokenName3}} ${{ secrets.TokenName4}} ${{ secrets.TokenName5}}' |
||||
|
prepare: pkgutil -y -i socat |
||||
|
run: | |
||||
|
pkg set-mediator -v -I default@1.1 openssl |
||||
|
export PATH=/usr/gnu/bin:$PATH |
||||
|
if [ "${{ secrets.TokenName1}}" ] ; then |
||||
|
export ${{ secrets.TokenName1}}=${{ secrets.TokenValue1}} |
||||
|
fi |
||||
|
if [ "${{ secrets.TokenName2}}" ] ; then |
||||
|
export ${{ secrets.TokenName2}}=${{ secrets.TokenValue2}} |
||||
|
fi |
||||
|
if [ "${{ secrets.TokenName3}}" ] ; then |
||||
|
export ${{ secrets.TokenName3}}=${{ secrets.TokenValue3}} |
||||
|
fi |
||||
|
if [ "${{ secrets.TokenName4}}" ] ; then |
||||
|
export ${{ secrets.TokenName4}}=${{ secrets.TokenValue4}} |
||||
|
fi |
||||
|
if [ "${{ secrets.TokenName5}}" ] ; then |
||||
|
export ${{ secrets.TokenName5}}=${{ secrets.TokenValue5}} |
||||
|
fi |
||||
|
cd ../acmetest |
||||
|
./letest.sh |
||||
@ -0,0 +1,63 @@ |
|||||
|
name: FreeBSD |
||||
|
on: |
||||
|
push: |
||||
|
branches: |
||||
|
- '*' |
||||
|
paths: |
||||
|
- '*.sh' |
||||
|
- '.github/workflows/FreeBSD.yml' |
||||
|
|
||||
|
pull_request: |
||||
|
branches: |
||||
|
- dev |
||||
|
paths: |
||||
|
- '*.sh' |
||||
|
- '.github/workflows/FreeBSD.yml' |
||||
|
|
||||
|
|
||||
|
jobs: |
||||
|
FreeBSD: |
||||
|
strategy: |
||||
|
matrix: |
||||
|
include: |
||||
|
- TEST_ACME_Server: "LetsEncrypt.org_test" |
||||
|
CA_ECDSA: "" |
||||
|
CA: "" |
||||
|
CA_EMAIL: "" |
||||
|
TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1 |
||||
|
- TEST_ACME_Server: "ZeroSSL.com" |
||||
|
CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA" |
||||
|
CA: "ZeroSSL RSA Domain Secure Site CA" |
||||
|
CA_EMAIL: "githubtest@acme.sh" |
||||
|
TEST_PREFERRED_CHAIN: "" |
||||
|
runs-on: macos-10.15 |
||||
|
env: |
||||
|
TEST_LOCAL: 1 |
||||
|
TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }} |
||||
|
CA_ECDSA: ${{ matrix.CA_ECDSA }} |
||||
|
CA: ${{ matrix.CA }} |
||||
|
CA_EMAIL: ${{ matrix.CA_EMAIL }} |
||||
|
TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }} |
||||
|
steps: |
||||
|
- uses: actions/checkout@v2 |
||||
|
- uses: vmactions/cf-tunnel@v0.0.3 |
||||
|
id: tunnel |
||||
|
with: |
||||
|
protocol: http |
||||
|
port: 8080 |
||||
|
- name: Set envs |
||||
|
run: echo "TestingDomain=${{steps.tunnel.outputs.server}}" >> $GITHUB_ENV |
||||
|
- name: Clone acmetest |
||||
|
run: cd .. && git clone https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/ |
||||
|
- uses: vmactions/freebsd-vm@v0.1.5 |
||||
|
with: |
||||
|
envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN' |
||||
|
nat: | |
||||
|
"8080": "80" |
||||
|
prepare: pkg install -y socat curl |
||||
|
usesh: true |
||||
|
run: | |
||||
|
cd ../acmetest \ |
||||
|
&& ./letest.sh |
||||
|
|
||||
|
|
||||
@ -0,0 +1,41 @@ |
|||||
|
name: Linux |
||||
|
on: |
||||
|
push: |
||||
|
branches: |
||||
|
- '*' |
||||
|
paths: |
||||
|
- '*.sh' |
||||
|
- '.github/workflows/Linux.yml' |
||||
|
|
||||
|
pull_request: |
||||
|
branches: |
||||
|
- dev |
||||
|
paths: |
||||
|
- '*.sh' |
||||
|
- '.github/workflows/Linux.yml' |
||||
|
|
||||
|
|
||||
|
|
||||
|
jobs: |
||||
|
Linux: |
||||
|
strategy: |
||||
|
matrix: |
||||
|
os: ["ubuntu:latest", "debian:latest", "almalinux:latest", "fedora:latest", "centos:7", "opensuse/leap:latest", "alpine:latest", "oraclelinux:8", "kalilinux/kali", "archlinux:latest", "mageia", "gentoo/stage3"] |
||||
|
runs-on: ubuntu-latest |
||||
|
env: |
||||
|
TEST_LOCAL: 1 |
||||
|
TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1 |
||||
|
steps: |
||||
|
- uses: actions/checkout@v2 |
||||
|
- name: Clone acmetest |
||||
|
run: | |
||||
|
cd .. \ |
||||
|
&& git clone https://github.com/acmesh-official/acmetest.git \ |
||||
|
&& cp -r acme.sh acmetest/ |
||||
|
- name: Run acmetest |
||||
|
run: | |
||||
|
cd ../acmetest \ |
||||
|
&& ./rundocker.sh testplat ${{ matrix.os }} |
||||
|
|
||||
|
|
||||
|
|
||||
@ -0,0 +1,55 @@ |
|||||
|
name: MacOS |
||||
|
on: |
||||
|
push: |
||||
|
branches: |
||||
|
- '*' |
||||
|
paths: |
||||
|
- '*.sh' |
||||
|
- '.github/workflows/MacOS.yml' |
||||
|
|
||||
|
pull_request: |
||||
|
branches: |
||||
|
- dev |
||||
|
paths: |
||||
|
- '*.sh' |
||||
|
- '.github/workflows/MacOS.yml' |
||||
|
|
||||
|
|
||||
|
jobs: |
||||
|
MacOS: |
||||
|
strategy: |
||||
|
matrix: |
||||
|
include: |
||||
|
- TEST_ACME_Server: "LetsEncrypt.org_test" |
||||
|
CA_ECDSA: "" |
||||
|
CA: "" |
||||
|
CA_EMAIL: "" |
||||
|
TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1 |
||||
|
- TEST_ACME_Server: "ZeroSSL.com" |
||||
|
CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA" |
||||
|
CA: "ZeroSSL RSA Domain Secure Site CA" |
||||
|
CA_EMAIL: "githubtest@acme.sh" |
||||
|
TEST_PREFERRED_CHAIN: "" |
||||
|
runs-on: macos-latest |
||||
|
env: |
||||
|
TEST_LOCAL: 1 |
||||
|
TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }} |
||||
|
CA_ECDSA: ${{ matrix.CA_ECDSA }} |
||||
|
CA: ${{ matrix.CA }} |
||||
|
CA_EMAIL: ${{ matrix.CA_EMAIL }} |
||||
|
TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }} |
||||
|
steps: |
||||
|
- uses: actions/checkout@v2 |
||||
|
- name: Install tools |
||||
|
run: brew install socat |
||||
|
- name: Clone acmetest |
||||
|
run: | |
||||
|
cd .. \ |
||||
|
&& git clone https://github.com/acmesh-official/acmetest.git \ |
||||
|
&& cp -r acme.sh acmetest/ |
||||
|
- name: Run acmetest |
||||
|
run: | |
||||
|
cd ../acmetest \ |
||||
|
&& sudo --preserve-env ./letest.sh |
||||
|
|
||||
|
|
||||
@ -0,0 +1,62 @@ |
|||||
|
name: PebbleStrict |
||||
|
on: |
||||
|
push: |
||||
|
branches: |
||||
|
- '*' |
||||
|
paths: |
||||
|
- '*.sh' |
||||
|
- '.github/workflows/PebbleStrict.yml' |
||||
|
pull_request: |
||||
|
branches: |
||||
|
- dev |
||||
|
paths: |
||||
|
- '*.sh' |
||||
|
- '.github/workflows/PebbleStrict.yml' |
||||
|
|
||||
|
jobs: |
||||
|
PebbleStrict: |
||||
|
runs-on: ubuntu-latest |
||||
|
env: |
||||
|
TestingDomain: example.com |
||||
|
TestingAltDomains: www.example.com |
||||
|
TEST_ACME_Server: https://localhost:14000/dir |
||||
|
HTTPS_INSECURE: 1 |
||||
|
Le_HTTPPort: 5002 |
||||
|
TEST_LOCAL: 1 |
||||
|
TEST_CA: "Pebble Intermediate CA" |
||||
|
|
||||
|
steps: |
||||
|
- uses: actions/checkout@v2 |
||||
|
- name: Install tools |
||||
|
run: sudo apt-get install -y socat |
||||
|
- name: Run Pebble |
||||
|
run: cd .. && curl https://raw.githubusercontent.com/letsencrypt/pebble/master/docker-compose.yml >docker-compose.yml && docker-compose up -d |
||||
|
- name: Set up Pebble |
||||
|
run: curl --request POST --data '{"ip":"10.30.50.1"}' http://localhost:8055/set-default-ipv4 |
||||
|
- name: Clone acmetest |
||||
|
run: cd .. && git clone https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/ |
||||
|
- name: Run acmetest |
||||
|
run: cd ../acmetest && ./letest.sh |
||||
|
|
||||
|
PebbleStrict_IPCert: |
||||
|
runs-on: ubuntu-latest |
||||
|
env: |
||||
|
TestingDomain: 10.30.50.1 |
||||
|
ACME_DIRECTORY: https://localhost:14000/dir |
||||
|
HTTPS_INSECURE: 1 |
||||
|
Le_HTTPPort: 5002 |
||||
|
Le_TLSPort: 5001 |
||||
|
TEST_LOCAL: 1 |
||||
|
TEST_CA: "Pebble Intermediate CA" |
||||
|
TEST_IPCERT: 1 |
||||
|
|
||||
|
steps: |
||||
|
- uses: actions/checkout@v2 |
||||
|
- name: Install tools |
||||
|
run: sudo apt-get install -y socat |
||||
|
- name: Run Pebble |
||||
|
run: cd .. && curl https://raw.githubusercontent.com/letsencrypt/pebble/master/docker-compose.yml >docker-compose.yml && docker-compose up -d |
||||
|
- name: Clone acmetest |
||||
|
run: cd .. && git clone https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/ |
||||
|
- name: Run acmetest |
||||
|
run: cd ../acmetest && ./letest.sh |
||||
@ -0,0 +1,61 @@ |
|||||
|
name: Solaris |
||||
|
on: |
||||
|
push: |
||||
|
branches: |
||||
|
- '*' |
||||
|
paths: |
||||
|
- '*.sh' |
||||
|
- '.github/workflows/Solaris.yml' |
||||
|
|
||||
|
pull_request: |
||||
|
branches: |
||||
|
- dev |
||||
|
paths: |
||||
|
- '*.sh' |
||||
|
- '.github/workflows/Solaris.yml' |
||||
|
|
||||
|
|
||||
|
jobs: |
||||
|
Solaris: |
||||
|
strategy: |
||||
|
matrix: |
||||
|
include: |
||||
|
- TEST_ACME_Server: "LetsEncrypt.org_test" |
||||
|
CA_ECDSA: "" |
||||
|
CA: "" |
||||
|
CA_EMAIL: "" |
||||
|
TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1 |
||||
|
- TEST_ACME_Server: "ZeroSSL.com" |
||||
|
CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA" |
||||
|
CA: "ZeroSSL RSA Domain Secure Site CA" |
||||
|
CA_EMAIL: "githubtest@acme.sh" |
||||
|
TEST_PREFERRED_CHAIN: "" |
||||
|
runs-on: macos-10.15 |
||||
|
env: |
||||
|
TEST_LOCAL: 1 |
||||
|
TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }} |
||||
|
CA_ECDSA: ${{ matrix.CA_ECDSA }} |
||||
|
CA: ${{ matrix.CA }} |
||||
|
CA_EMAIL: ${{ matrix.CA_EMAIL }} |
||||
|
TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }} |
||||
|
steps: |
||||
|
- uses: actions/checkout@v2 |
||||
|
- uses: vmactions/cf-tunnel@v0.0.3 |
||||
|
id: tunnel |
||||
|
with: |
||||
|
protocol: http |
||||
|
port: 8080 |
||||
|
- name: Set envs |
||||
|
run: echo "TestingDomain=${{steps.tunnel.outputs.server}}" >> $GITHUB_ENV |
||||
|
- name: Clone acmetest |
||||
|
run: cd .. && git clone https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/ |
||||
|
- uses: vmactions/solaris-vm@v0.0.5 |
||||
|
with: |
||||
|
envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN' |
||||
|
nat: | |
||||
|
"8080": "80" |
||||
|
prepare: pkgutil -y -i socat curl |
||||
|
run: | |
||||
|
cd ../acmetest \ |
||||
|
&& ./letest.sh |
||||
|
|
||||
@ -0,0 +1,86 @@ |
|||||
|
name: Ubuntu |
||||
|
on: |
||||
|
push: |
||||
|
branches: |
||||
|
- '*' |
||||
|
paths: |
||||
|
- '*.sh' |
||||
|
- '.github/workflows/Ubuntu.yml' |
||||
|
|
||||
|
pull_request: |
||||
|
branches: |
||||
|
- dev |
||||
|
paths: |
||||
|
- '*.sh' |
||||
|
- '.github/workflows/Ubuntu.yml' |
||||
|
|
||||
|
|
||||
|
jobs: |
||||
|
Ubuntu: |
||||
|
strategy: |
||||
|
matrix: |
||||
|
include: |
||||
|
- TEST_ACME_Server: "LetsEncrypt.org_test" |
||||
|
CA_ECDSA: "" |
||||
|
CA: "" |
||||
|
CA_EMAIL: "" |
||||
|
TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1 |
||||
|
- TEST_ACME_Server: "ZeroSSL.com" |
||||
|
CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA" |
||||
|
CA: "ZeroSSL RSA Domain Secure Site CA" |
||||
|
CA_EMAIL: "githubtest@acme.sh" |
||||
|
TEST_PREFERRED_CHAIN: "" |
||||
|
- TEST_ACME_Server: "https://localhost:9000/acme/acme/directory" |
||||
|
CA_ECDSA: "Smallstep Intermediate CA" |
||||
|
CA: "Smallstep Intermediate CA" |
||||
|
CA_EMAIL: "" |
||||
|
TEST_PREFERRED_CHAIN: "" |
||||
|
NO_REVOKE: 1 |
||||
|
- TEST_ACME_Server: "https://localhost:9000/acme/acme/directory" |
||||
|
CA_ECDSA: "Smallstep Intermediate CA" |
||||
|
CA: "Smallstep Intermediate CA" |
||||
|
CA_EMAIL: "" |
||||
|
TEST_PREFERRED_CHAIN: "" |
||||
|
NO_REVOKE: 1 |
||||
|
TEST_IPCERT: 1 |
||||
|
TestingDomain: "172.17.0.1" |
||||
|
|
||||
|
runs-on: ubuntu-latest |
||||
|
env: |
||||
|
TEST_LOCAL: 1 |
||||
|
TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }} |
||||
|
CA_ECDSA: ${{ matrix.CA_ECDSA }} |
||||
|
CA: ${{ matrix.CA }} |
||||
|
CA_EMAIL: ${{ matrix.CA_EMAIL }} |
||||
|
NO_ECC_384: ${{ matrix.NO_ECC_384 }} |
||||
|
TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }} |
||||
|
NO_REVOKE: ${{ matrix.NO_REVOKE }} |
||||
|
TEST_IPCERT: ${{ matrix.TEST_IPCERT }} |
||||
|
TestingDomain: ${{ matrix.TestingDomain }} |
||||
|
steps: |
||||
|
- uses: actions/checkout@v2 |
||||
|
- name: Install tools |
||||
|
run: sudo apt-get install -y socat |
||||
|
- name: Start StepCA |
||||
|
if: ${{ matrix.TEST_ACME_Server=='https://localhost:9000/acme/acme/directory' }} |
||||
|
run: | |
||||
|
docker run --rm -d \ |
||||
|
-p 9000:9000 \ |
||||
|
-e "DOCKER_STEPCA_INIT_NAME=Smallstep" \ |
||||
|
-e "DOCKER_STEPCA_INIT_DNS_NAMES=localhost,$(hostname -f)" \ |
||||
|
--name stepca \ |
||||
|
smallstep/step-ca \ |
||||
|
&& sleep 5 && docker exec stepca step ca provisioner add acme --type ACME \ |
||||
|
&& docker exec stepca kill -1 1 \ |
||||
|
&& docker exec stepca cat /home/step/certs/root_ca.crt | sudo bash -c "cat - >>/etc/ssl/certs/ca-certificates.crt" |
||||
|
- name: Clone acmetest |
||||
|
run: | |
||||
|
cd .. \ |
||||
|
&& git clone https://github.com/acmesh-official/acmetest.git \ |
||||
|
&& cp -r acme.sh acmetest/ |
||||
|
- name: Run acmetest |
||||
|
run: | |
||||
|
cd ../acmetest \ |
||||
|
&& sudo --preserve-env ./letest.sh |
||||
|
|
||||
|
|
||||
@ -0,0 +1,73 @@ |
|||||
|
name: Windows |
||||
|
on: |
||||
|
push: |
||||
|
branches: |
||||
|
- '*' |
||||
|
paths: |
||||
|
- '*.sh' |
||||
|
- '.github/workflows/Windows.yml' |
||||
|
|
||||
|
pull_request: |
||||
|
branches: |
||||
|
- dev |
||||
|
paths: |
||||
|
- '*.sh' |
||||
|
- '.github/workflows/Windows.yml' |
||||
|
|
||||
|
|
||||
|
jobs: |
||||
|
Windows: |
||||
|
strategy: |
||||
|
matrix: |
||||
|
include: |
||||
|
- TEST_ACME_Server: "LetsEncrypt.org_test" |
||||
|
CA_ECDSA: "" |
||||
|
CA: "" |
||||
|
CA_EMAIL: "" |
||||
|
TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1 |
||||
|
- TEST_ACME_Server: "ZeroSSL.com" |
||||
|
CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA" |
||||
|
CA: "ZeroSSL RSA Domain Secure Site CA" |
||||
|
CA_EMAIL: "githubtest@acme.sh" |
||||
|
TEST_PREFERRED_CHAIN: "" |
||||
|
runs-on: windows-latest |
||||
|
env: |
||||
|
TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }} |
||||
|
CA_ECDSA: ${{ matrix.CA_ECDSA }} |
||||
|
CA: ${{ matrix.CA }} |
||||
|
CA_EMAIL: ${{ matrix.CA_EMAIL }} |
||||
|
TEST_LOCAL: 1 |
||||
|
#The 80 port is used by Windows server, we have to use a custom port, tunnel will also use this port. |
||||
|
Le_HTTPPort: 8888 |
||||
|
TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }} |
||||
|
steps: |
||||
|
- name: Set git to use LF |
||||
|
run: | |
||||
|
git config --global core.autocrlf false |
||||
|
- uses: actions/checkout@v2 |
||||
|
- name: Install cygwin base packages with chocolatey |
||||
|
run: | |
||||
|
choco config get cacheLocation |
||||
|
choco install --no-progress cygwin |
||||
|
shell: cmd |
||||
|
- name: Install cygwin additional packages |
||||
|
run: | |
||||
|
C:\tools\cygwin\cygwinsetup.exe -qgnNdO -R C:/tools/cygwin -s http://mirrors.kernel.org/sourceware/cygwin/ -P socat,curl,cron,unzip,git,xxd |
||||
|
shell: cmd |
||||
|
- name: Set ENV |
||||
|
shell: cmd |
||||
|
run: | |
||||
|
echo PATH=C:\tools\cygwin\bin;C:\tools\cygwin\usr\bin;%PATH% >> %GITHUB_ENV% |
||||
|
- name: Check ENV |
||||
|
shell: cmd |
||||
|
run: | |
||||
|
echo "PATH=%PATH%" |
||||
|
- name: Clone acmetest |
||||
|
shell: cmd |
||||
|
run: cd .. && git clone https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/ |
||||
|
- name: Run acmetest |
||||
|
shell: cmd |
||||
|
run: cd ../acmetest && bash.exe -c ./letest.sh |
||||
|
|
||||
|
|
||||
|
|
||||
@ -0,0 +1,33 @@ |
|||||
|
name: Shellcheck |
||||
|
on: |
||||
|
push: |
||||
|
branches: |
||||
|
- '*' |
||||
|
paths: |
||||
|
- '**.sh' |
||||
|
- '.github/workflows/shellcheck.yml' |
||||
|
pull_request: |
||||
|
branches: |
||||
|
- dev |
||||
|
paths: |
||||
|
- '**.sh' |
||||
|
- '.github/workflows/shellcheck.yml' |
||||
|
|
||||
|
jobs: |
||||
|
ShellCheck: |
||||
|
runs-on: ubuntu-latest |
||||
|
steps: |
||||
|
- uses: actions/checkout@v2 |
||||
|
- name: Install Shellcheck |
||||
|
run: sudo apt-get install -y shellcheck |
||||
|
- name: DoShellcheck |
||||
|
run: shellcheck -V && shellcheck -e SC2181 **/*.sh && echo "shellcheck OK" |
||||
|
|
||||
|
shfmt: |
||||
|
runs-on: ubuntu-latest |
||||
|
steps: |
||||
|
- uses: actions/checkout@v2 |
||||
|
- name: Install shfmt |
||||
|
run: curl -sSL https://github.com/mvdan/sh/releases/download/v3.1.2/shfmt_v3.1.2_linux_amd64 -o ~/shfmt && chmod +x ~/shfmt |
||||
|
- name: shfmt |
||||
|
run: ~/shfmt -l -w -i 2 . ; git diff --exit-code && echo "shfmt OK" |
||||
@ -1,37 +0,0 @@ |
|||||
language: shell |
|
||||
dist: trusty |
|
||||
|
|
||||
os: |
|
||||
- linux |
|
||||
- osx |
|
||||
|
|
||||
services: |
|
||||
- docker |
|
||||
|
|
||||
env: |
|
||||
global: |
|
||||
- SHFMT_URL=https://github.com/mvdan/sh/releases/download/v0.4.0/shfmt_v0.4.0_linux_amd64 |
|
||||
|
|
||||
|
|
||||
install: |
|
||||
- if [ "$TRAVIS_OS_NAME" = 'osx' ]; then |
|
||||
brew update && brew install socat; |
|
||||
export PATH="/usr/local/opt/openssl@1.1/bin:$PATH" ; |
|
||||
fi |
|
||||
|
|
||||
script: |
|
||||
- echo "NGROK_TOKEN=$(echo "$NGROK_TOKEN" | wc -c)" |
|
||||
- command -V openssl && openssl version |
|
||||
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then curl -sSL $SHFMT_URL -o ~/shfmt && chmod +x ~/shfmt && ~/shfmt -l -w -i 2 . ; fi |
|
||||
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then git diff --exit-code && echo "shfmt OK" ; fi |
|
||||
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then shellcheck -V ; fi |
|
||||
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then shellcheck -e SC2181 **/*.sh && echo "shellcheck OK" ; fi |
|
||||
- cd .. |
|
||||
- git clone --depth 1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/ && cd acmetest |
|
||||
- if [ "$TRAVIS_OS_NAME" = "linux" -a "$NGROK_TOKEN" ]; then sudo TEST_LOCAL="$TEST_LOCAL" NGROK_TOKEN="$NGROK_TOKEN" ./rundocker.sh testplat ubuntu:latest ; fi |
|
||||
- if [ "$TRAVIS_OS_NAME" = "osx" -a "$NGROK_TOKEN" ]; then sudo TEST_LOCAL="$TEST_LOCAL" NGROK_TOKEN="$NGROK_TOKEN" ACME_OPENSSL_BIN="$ACME_OPENSSL_BIN" ./letest.sh ; fi |
|
||||
|
|
||||
matrix: |
|
||||
fast_finish: true |
|
||||
|
|
||||
|
|
||||
1635
acme.sh
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,92 @@ |
|||||
|
#!/usr/bin/env sh |
||||
|
# Here is the script to deploy the cert to your CleverReach Account using the CleverReach REST API. |
||||
|
# Your OAuth needs the right scope, please contact CleverReach support for that. |
||||
|
# |
||||
|
# Written by Jan-Philipp Benecke <github@bnck.me> |
||||
|
# Public domain, 2020 |
||||
|
# |
||||
|
# Following environment variables must be set: |
||||
|
# |
||||
|
#export DEPLOY_CLEVERREACH_CLIENT_ID=myid |
||||
|
#export DEPLOY_CLEVERREACH_CLIENT_SECRET=mysecret |
||||
|
|
||||
|
cleverreach_deploy() { |
||||
|
_cdomain="$1" |
||||
|
_ckey="$2" |
||||
|
_ccert="$3" |
||||
|
_cca="$4" |
||||
|
_cfullchain="$5" |
||||
|
|
||||
|
_rest_endpoint="https://rest.cleverreach.com" |
||||
|
|
||||
|
_debug _cdomain "$_cdomain" |
||||
|
_debug _ckey "$_ckey" |
||||
|
_debug _ccert "$_ccert" |
||||
|
_debug _cca "$_cca" |
||||
|
_debug _cfullchain "$_cfullchain" |
||||
|
|
||||
|
_getdeployconf DEPLOY_CLEVERREACH_CLIENT_ID |
||||
|
_getdeployconf DEPLOY_CLEVERREACH_CLIENT_SECRET |
||||
|
_getdeployconf DEPLOY_CLEVERREACH_SUBCLIENT_ID |
||||
|
|
||||
|
if [ -z "${DEPLOY_CLEVERREACH_CLIENT_ID}" ]; then |
||||
|
_err "CleverReach Client ID is not found, please define DEPLOY_CLEVERREACH_CLIENT_ID." |
||||
|
return 1 |
||||
|
fi |
||||
|
if [ -z "${DEPLOY_CLEVERREACH_CLIENT_SECRET}" ]; then |
||||
|
_err "CleverReach client secret is not found, please define DEPLOY_CLEVERREACH_CLIENT_SECRET." |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_savedeployconf DEPLOY_CLEVERREACH_CLIENT_ID "${DEPLOY_CLEVERREACH_CLIENT_ID}" |
||||
|
_savedeployconf DEPLOY_CLEVERREACH_CLIENT_SECRET "${DEPLOY_CLEVERREACH_CLIENT_SECRET}" |
||||
|
_savedeployconf DEPLOY_CLEVERREACH_SUBCLIENT_ID "${DEPLOY_CLEVERREACH_SUBCLIENT_ID}" |
||||
|
|
||||
|
_info "Obtaining a CleverReach access token" |
||||
|
|
||||
|
_data="{\"grant_type\": \"client_credentials\", \"client_id\": \"${DEPLOY_CLEVERREACH_CLIENT_ID}\", \"client_secret\": \"${DEPLOY_CLEVERREACH_CLIENT_SECRET}\"}" |
||||
|
_auth_result="$(_post "$_data" "$_rest_endpoint/oauth/token.php" "" "POST" "application/json")" |
||||
|
|
||||
|
_debug _data "$_data" |
||||
|
_debug _auth_result "$_auth_result" |
||||
|
|
||||
|
_regex=".*\"access_token\":\"\([-._0-9A-Za-z]*\)\".*$" |
||||
|
_debug _regex "$_regex" |
||||
|
_access_token=$(echo "$_auth_result" | _json_decode | sed -n "s/$_regex/\1/p") |
||||
|
|
||||
|
_debug _subclient "${DEPLOY_CLEVERREACH_SUBCLIENT_ID}" |
||||
|
|
||||
|
if [ -n "${DEPLOY_CLEVERREACH_SUBCLIENT_ID}" ]; then |
||||
|
_info "Obtaining token for sub-client ${DEPLOY_CLEVERREACH_SUBCLIENT_ID}" |
||||
|
export _H1="Authorization: Bearer ${_access_token}" |
||||
|
_subclient_token_result="$(_get "$_rest_endpoint/v3/clients/$DEPLOY_CLEVERREACH_SUBCLIENT_ID/token")" |
||||
|
_access_token=$(echo "$_subclient_token_result" | sed -n "s/\"//p") |
||||
|
|
||||
|
_debug _subclient_token_result "$_access_token" |
||||
|
|
||||
|
_info "Destroying parent token at CleverReach, as it not needed anymore" |
||||
|
_destroy_result="$(_post "" "$_rest_endpoint/v3/oauth/token.json" "" "DELETE" "application/json")" |
||||
|
_debug _destroy_result "$_destroy_result" |
||||
|
fi |
||||
|
|
||||
|
_info "Uploading certificate and key to CleverReach" |
||||
|
|
||||
|
_certData="{\"cert\":\"$(_json_encode <"$_cfullchain")\", \"key\":\"$(_json_encode <"$_ckey")\"}" |
||||
|
export _H1="Authorization: Bearer ${_access_token}" |
||||
|
_add_cert_result="$(_post "$_certData" "$_rest_endpoint/v3/ssl" "" "POST" "application/json")" |
||||
|
|
||||
|
if [ -z "${DEPLOY_CLEVERREACH_SUBCLIENT_ID}" ]; then |
||||
|
_info "Destroying token at CleverReach, as it not needed anymore" |
||||
|
_destroy_result="$(_post "" "$_rest_endpoint/v3/oauth/token.json" "" "DELETE" "application/json")" |
||||
|
_debug _destroy_result "$_destroy_result" |
||||
|
fi |
||||
|
|
||||
|
if ! echo "$_add_cert_result" | grep '"error":' >/dev/null; then |
||||
|
_info "Uploaded certificate successfully" |
||||
|
return 0 |
||||
|
else |
||||
|
_debug _add_cert_result "$_add_cert_result" |
||||
|
_err "Unable to update certificate" |
||||
|
return 1 |
||||
|
fi |
||||
|
} |
||||
@ -0,0 +1,98 @@ |
|||||
|
#!/usr/bin/env sh |
||||
|
|
||||
|
# Here is a script to deploy cert to hashicorp consul using curl |
||||
|
# (https://www.consul.io/) |
||||
|
# |
||||
|
# it requires following environment variables: |
||||
|
# |
||||
|
# CONSUL_PREFIX - this contains the prefix path in consul |
||||
|
# CONSUL_HTTP_ADDR - consul requires this to find your consul server |
||||
|
# |
||||
|
# additionally, you need to ensure that CONSUL_HTTP_TOKEN is available |
||||
|
# to access the consul server |
||||
|
|
||||
|
#returns 0 means success, otherwise error. |
||||
|
|
||||
|
######## Public functions ##################### |
||||
|
|
||||
|
#domain keyfile certfile cafile fullchain |
||||
|
consul_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" |
||||
|
|
||||
|
# validate required env vars |
||||
|
_getdeployconf CONSUL_PREFIX |
||||
|
if [ -z "$CONSUL_PREFIX" ]; then |
||||
|
_err "CONSUL_PREFIX needs to be defined (contains prefix path in vault)" |
||||
|
return 1 |
||||
|
fi |
||||
|
_savedeployconf CONSUL_PREFIX "$CONSUL_PREFIX" |
||||
|
|
||||
|
_getdeployconf CONSUL_HTTP_ADDR |
||||
|
if [ -z "$CONSUL_HTTP_ADDR" ]; then |
||||
|
_err "CONSUL_HTTP_ADDR needs to be defined (contains consul connection address)" |
||||
|
return 1 |
||||
|
fi |
||||
|
_savedeployconf CONSUL_HTTP_ADDR "$CONSUL_HTTP_ADDR" |
||||
|
|
||||
|
CONSUL_CMD=$(command -v consul) |
||||
|
|
||||
|
# force CLI, but the binary does not exist => error |
||||
|
if [ -n "$USE_CLI" ] && [ -z "$CONSUL_CMD" ]; then |
||||
|
_err "Cannot find the consul binary!" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
# use the CLI first |
||||
|
if [ -n "$USE_CLI" ] || [ -n "$CONSUL_CMD" ]; then |
||||
|
_info "Found consul binary, deploying with CLI" |
||||
|
consul_deploy_cli "$CONSUL_CMD" "$CONSUL_PREFIX" |
||||
|
else |
||||
|
_info "Did not find consul binary, deploying with API" |
||||
|
consul_deploy_api "$CONSUL_HTTP_ADDR" "$CONSUL_PREFIX" "$CONSUL_HTTP_TOKEN" |
||||
|
fi |
||||
|
} |
||||
|
|
||||
|
consul_deploy_api() { |
||||
|
CONSUL_HTTP_ADDR="$1" |
||||
|
CONSUL_PREFIX="$2" |
||||
|
CONSUL_HTTP_TOKEN="$3" |
||||
|
|
||||
|
URL="$CONSUL_HTTP_ADDR/v1/kv/$CONSUL_PREFIX" |
||||
|
export _H1="X-Consul-Token: $CONSUL_HTTP_TOKEN" |
||||
|
|
||||
|
if [ -n "$FABIO" ]; then |
||||
|
_post "$(cat "$_cfullchain")" "$URL/${_cdomain}-cert.pem" '' "PUT" || return 1 |
||||
|
_post "$(cat "$_ckey")" "$URL/${_cdomain}-key.pem" '' "PUT" || return 1 |
||||
|
else |
||||
|
_post "$(cat "$_ccert")" "$URL/${_cdomain}/cert.pem" '' "PUT" || return 1 |
||||
|
_post "$(cat "$_ckey")" "$URL/${_cdomain}/cert.key" '' "PUT" || return 1 |
||||
|
_post "$(cat "$_cca")" "$URL/${_cdomain}/chain.pem" '' "PUT" || return 1 |
||||
|
_post "$(cat "$_cfullchain")" "$URL/${_cdomain}/fullchain.pem" '' "PUT" || return 1 |
||||
|
fi |
||||
|
} |
||||
|
|
||||
|
consul_deploy_cli() { |
||||
|
CONSUL_CMD="$1" |
||||
|
CONSUL_PREFIX="$2" |
||||
|
|
||||
|
if [ -n "$FABIO" ]; then |
||||
|
$CONSUL_CMD kv put "${CONSUL_PREFIX}/${_cdomain}-cert.pem" @"$_cfullchain" || return 1 |
||||
|
$CONSUL_CMD kv put "${CONSUL_PREFIX}/${_cdomain}-key.pem" @"$_ckey" || return 1 |
||||
|
else |
||||
|
$CONSUL_CMD kv put "${CONSUL_PREFIX}/${_cdomain}/cert.pem" value=@"$_ccert" || return 1 |
||||
|
$CONSUL_CMD kv put "${CONSUL_PREFIX}/${_cdomain}/cert.key" value=@"$_ckey" || return 1 |
||||
|
$CONSUL_CMD kv put "${CONSUL_PREFIX}/${_cdomain}/chain.pem" value=@"$_cca" || return 1 |
||||
|
$CONSUL_CMD kv put "${CONSUL_PREFIX}/${_cdomain}/fullchain.pem" value=@"$_cfullchain" || return 1 |
||||
|
fi |
||||
|
} |
||||
@ -0,0 +1,280 @@ |
|||||
|
#!/usr/bin/env sh |
||||
|
|
||||
|
# Script for acme.sh to deploy certificates to lighttpd |
||||
|
# |
||||
|
# The following variables can be exported: |
||||
|
# |
||||
|
# export DEPLOY_LIGHTTPD_PEM_NAME="${domain}.pem" |
||||
|
# |
||||
|
# Defines the name of the PEM file. |
||||
|
# Defaults to "<domain>.pem" |
||||
|
# |
||||
|
# export DEPLOY_LIGHTTPD_PEM_PATH="/etc/lighttpd" |
||||
|
# |
||||
|
# Defines location of PEM file for Lighttpd. |
||||
|
# Defaults to /etc/lighttpd |
||||
|
# |
||||
|
# export DEPLOY_LIGHTTPD_RELOAD="systemctl reload lighttpd" |
||||
|
# |
||||
|
# OPTIONAL: Reload command used post deploy |
||||
|
# This defaults to be a no-op (ie "true"). |
||||
|
# It is strongly recommended to set this something that makes sense |
||||
|
# for your distro. |
||||
|
# |
||||
|
# export DEPLOY_LIGHTTPD_ISSUER="yes" |
||||
|
# |
||||
|
# OPTIONAL: Places CA file as "${DEPLOY_LIGHTTPD_PEM}.issuer" |
||||
|
# Note: Required for OCSP stapling to work |
||||
|
# |
||||
|
# export DEPLOY_LIGHTTPD_BUNDLE="no" |
||||
|
# |
||||
|
# OPTIONAL: Deploy this certificate as part of a multi-cert bundle |
||||
|
# This adds a suffix to the certificate based on the certificate type |
||||
|
# eg RSA certificates will have .rsa as a suffix to the file name |
||||
|
# Lighttpd will load all certificates and provide one or the other |
||||
|
# depending on client capabilities |
||||
|
# Note: This functionality requires Lighttpd was compiled against |
||||
|
# a version of OpenSSL that supports this. |
||||
|
# |
||||
|
|
||||
|
######## Public functions ##################### |
||||
|
|
||||
|
#domain keyfile certfile cafile fullchain |
||||
|
lighttpd_deploy() { |
||||
|
_cdomain="$1" |
||||
|
_ckey="$2" |
||||
|
_ccert="$3" |
||||
|
_cca="$4" |
||||
|
_cfullchain="$5" |
||||
|
|
||||
|
# Some defaults |
||||
|
DEPLOY_LIGHTTPD_PEM_PATH_DEFAULT="/etc/lighttpd" |
||||
|
DEPLOY_LIGHTTPD_PEM_NAME_DEFAULT="${_cdomain}.pem" |
||||
|
DEPLOY_LIGHTTPD_BUNDLE_DEFAULT="no" |
||||
|
DEPLOY_LIGHTTPD_ISSUER_DEFAULT="yes" |
||||
|
DEPLOY_LIGHTTPD_RELOAD_DEFAULT="true" |
||||
|
|
||||
|
_debug _cdomain "${_cdomain}" |
||||
|
_debug _ckey "${_ckey}" |
||||
|
_debug _ccert "${_ccert}" |
||||
|
_debug _cca "${_cca}" |
||||
|
_debug _cfullchain "${_cfullchain}" |
||||
|
|
||||
|
# PEM_PATH is optional. If not provided then assume "${DEPLOY_LIGHTTPD_PEM_PATH_DEFAULT}" |
||||
|
_getdeployconf DEPLOY_LIGHTTPD_PEM_PATH |
||||
|
_debug2 DEPLOY_LIGHTTPD_PEM_PATH "${DEPLOY_LIGHTTPD_PEM_PATH}" |
||||
|
if [ -n "${DEPLOY_LIGHTTPD_PEM_PATH}" ]; then |
||||
|
Le_Deploy_lighttpd_pem_path="${DEPLOY_LIGHTTPD_PEM_PATH}" |
||||
|
_savedomainconf Le_Deploy_lighttpd_pem_path "${Le_Deploy_lighttpd_pem_path}" |
||||
|
elif [ -z "${Le_Deploy_lighttpd_pem_path}" ]; then |
||||
|
Le_Deploy_lighttpd_pem_path="${DEPLOY_LIGHTTPD_PEM_PATH_DEFAULT}" |
||||
|
fi |
||||
|
|
||||
|
# Ensure PEM_PATH exists |
||||
|
if [ -d "${Le_Deploy_lighttpd_pem_path}" ]; then |
||||
|
_debug "PEM_PATH ${Le_Deploy_lighttpd_pem_path} exists" |
||||
|
else |
||||
|
_err "PEM_PATH ${Le_Deploy_lighttpd_pem_path} does not exist" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
# PEM_NAME is optional. If not provided then assume "${DEPLOY_LIGHTTPD_PEM_NAME_DEFAULT}" |
||||
|
_getdeployconf DEPLOY_LIGHTTPD_PEM_NAME |
||||
|
_debug2 DEPLOY_LIGHTTPD_PEM_NAME "${DEPLOY_LIGHTTPD_PEM_NAME}" |
||||
|
if [ -n "${DEPLOY_LIGHTTPD_PEM_NAME}" ]; then |
||||
|
Le_Deploy_lighttpd_pem_name="${DEPLOY_LIGHTTPD_PEM_NAME}" |
||||
|
_savedomainconf Le_Deploy_lighttpd_pem_name "${Le_Deploy_lighttpd_pem_name}" |
||||
|
elif [ -z "${Le_Deploy_lighttpd_pem_name}" ]; then |
||||
|
Le_Deploy_lighttpd_pem_name="${DEPLOY_LIGHTTPD_PEM_NAME_DEFAULT}" |
||||
|
fi |
||||
|
|
||||
|
# BUNDLE is optional. If not provided then assume "${DEPLOY_LIGHTTPD_BUNDLE_DEFAULT}" |
||||
|
_getdeployconf DEPLOY_LIGHTTPD_BUNDLE |
||||
|
_debug2 DEPLOY_LIGHTTPD_BUNDLE "${DEPLOY_LIGHTTPD_BUNDLE}" |
||||
|
if [ -n "${DEPLOY_LIGHTTPD_BUNDLE}" ]; then |
||||
|
Le_Deploy_lighttpd_bundle="${DEPLOY_LIGHTTPD_BUNDLE}" |
||||
|
_savedomainconf Le_Deploy_lighttpd_bundle "${Le_Deploy_lighttpd_bundle}" |
||||
|
elif [ -z "${Le_Deploy_lighttpd_bundle}" ]; then |
||||
|
Le_Deploy_lighttpd_bundle="${DEPLOY_LIGHTTPD_BUNDLE_DEFAULT}" |
||||
|
fi |
||||
|
|
||||
|
# ISSUER is optional. If not provided then assume "${DEPLOY_LIGHTTPD_ISSUER_DEFAULT}" |
||||
|
_getdeployconf DEPLOY_LIGHTTPD_ISSUER |
||||
|
_debug2 DEPLOY_LIGHTTPD_ISSUER "${DEPLOY_LIGHTTPD_ISSUER}" |
||||
|
if [ -n "${DEPLOY_LIGHTTPD_ISSUER}" ]; then |
||||
|
Le_Deploy_lighttpd_issuer="${DEPLOY_LIGHTTPD_ISSUER}" |
||||
|
_savedomainconf Le_Deploy_lighttpd_issuer "${Le_Deploy_lighttpd_issuer}" |
||||
|
elif [ -z "${Le_Deploy_lighttpd_issuer}" ]; then |
||||
|
Le_Deploy_lighttpd_issuer="${DEPLOY_LIGHTTPD_ISSUER_DEFAULT}" |
||||
|
fi |
||||
|
|
||||
|
# RELOAD is optional. If not provided then assume "${DEPLOY_LIGHTTPD_RELOAD_DEFAULT}" |
||||
|
_getdeployconf DEPLOY_LIGHTTPD_RELOAD |
||||
|
_debug2 DEPLOY_LIGHTTPD_RELOAD "${DEPLOY_LIGHTTPD_RELOAD}" |
||||
|
if [ -n "${DEPLOY_LIGHTTPD_RELOAD}" ]; then |
||||
|
Le_Deploy_lighttpd_reload="${DEPLOY_LIGHTTPD_RELOAD}" |
||||
|
_savedomainconf Le_Deploy_lighttpd_reload "${Le_Deploy_lighttpd_reload}" |
||||
|
elif [ -z "${Le_Deploy_lighttpd_reload}" ]; then |
||||
|
Le_Deploy_lighttpd_reload="${DEPLOY_LIGHTTPD_RELOAD_DEFAULT}" |
||||
|
fi |
||||
|
|
||||
|
# Set the suffix depending if we are creating a bundle or not |
||||
|
if [ "${Le_Deploy_lighttpd_bundle}" = "yes" ]; then |
||||
|
_info "Bundle creation requested" |
||||
|
# Initialise $Le_Keylength if its not already set |
||||
|
if [ -z "${Le_Keylength}" ]; then |
||||
|
Le_Keylength="" |
||||
|
fi |
||||
|
if _isEccKey "${Le_Keylength}"; then |
||||
|
_info "ECC key type detected" |
||||
|
_suffix=".ecdsa" |
||||
|
else |
||||
|
_info "RSA key type detected" |
||||
|
_suffix=".rsa" |
||||
|
fi |
||||
|
else |
||||
|
_suffix="" |
||||
|
fi |
||||
|
_debug _suffix "${_suffix}" |
||||
|
|
||||
|
# Set variables for later |
||||
|
_pem="${Le_Deploy_lighttpd_pem_path}/${Le_Deploy_lighttpd_pem_name}${_suffix}" |
||||
|
_issuer="${_pem}.issuer" |
||||
|
_ocsp="${_pem}.ocsp" |
||||
|
_reload="${Le_Deploy_lighttpd_reload}" |
||||
|
|
||||
|
_info "Deploying PEM file" |
||||
|
# Create a temporary PEM file |
||||
|
_temppem="$(_mktemp)" |
||||
|
_debug _temppem "${_temppem}" |
||||
|
cat "${_ckey}" "${_ccert}" "${_cca}" >"${_temppem}" |
||||
|
_ret="$?" |
||||
|
|
||||
|
# Check that we could create the temporary file |
||||
|
if [ "${_ret}" != "0" ]; then |
||||
|
_err "Error code ${_ret} returned during PEM file creation" |
||||
|
[ -f "${_temppem}" ] && rm -f "${_temppem}" |
||||
|
return ${_ret} |
||||
|
fi |
||||
|
|
||||
|
# Move PEM file into place |
||||
|
_info "Moving new certificate into place" |
||||
|
_debug _pem "${_pem}" |
||||
|
cat "${_temppem}" >"${_pem}" |
||||
|
_ret=$? |
||||
|
|
||||
|
# Clean up temp file |
||||
|
[ -f "${_temppem}" ] && rm -f "${_temppem}" |
||||
|
|
||||
|
# Deal with any failure of moving PEM file into place |
||||
|
if [ "${_ret}" != "0" ]; then |
||||
|
_err "Error code ${_ret} returned while moving new certificate into place" |
||||
|
return ${_ret} |
||||
|
fi |
||||
|
|
||||
|
# Update .issuer file if requested |
||||
|
if [ "${Le_Deploy_lighttpd_issuer}" = "yes" ]; then |
||||
|
_info "Updating .issuer file" |
||||
|
_debug _issuer "${_issuer}" |
||||
|
cat "${_cca}" >"${_issuer}" |
||||
|
_ret="$?" |
||||
|
|
||||
|
if [ "${_ret}" != "0" ]; then |
||||
|
_err "Error code ${_ret} returned while copying issuer/CA certificate into place" |
||||
|
return ${_ret} |
||||
|
fi |
||||
|
else |
||||
|
[ -f "${_issuer}" ] && _err "Issuer file update not requested but .issuer file exists" |
||||
|
fi |
||||
|
|
||||
|
# Update .ocsp file if certificate was requested with --ocsp/--ocsp-must-staple option |
||||
|
if [ -z "${Le_OCSP_Staple}" ]; then |
||||
|
Le_OCSP_Staple="0" |
||||
|
fi |
||||
|
if [ "${Le_OCSP_Staple}" = "1" ]; then |
||||
|
_info "Updating OCSP stapling info" |
||||
|
_debug _ocsp "${_ocsp}" |
||||
|
_info "Extracting OCSP URL" |
||||
|
_ocsp_url=$(${ACME_OPENSSL_BIN:-openssl} x509 -noout -ocsp_uri -in "${_pem}") |
||||
|
_debug _ocsp_url "${_ocsp_url}" |
||||
|
|
||||
|
# Only process OCSP if URL was present |
||||
|
if [ "${_ocsp_url}" != "" ]; then |
||||
|
# Extract the hostname from the OCSP URL |
||||
|
_info "Extracting OCSP URL" |
||||
|
_ocsp_host=$(echo "${_ocsp_url}" | cut -d/ -f3) |
||||
|
_debug _ocsp_host "${_ocsp_host}" |
||||
|
|
||||
|
# Only process the certificate if we have a .issuer file |
||||
|
if [ -r "${_issuer}" ]; then |
||||
|
# Check if issuer cert is also a root CA cert |
||||
|
_subjectdn=$(${ACME_OPENSSL_BIN:-openssl} x509 -in "${_issuer}" -subject -noout | cut -d'/' -f2,3,4,5,6,7,8,9,10) |
||||
|
_debug _subjectdn "${_subjectdn}" |
||||
|
_issuerdn=$(${ACME_OPENSSL_BIN:-openssl} x509 -in "${_issuer}" -issuer -noout | cut -d'/' -f2,3,4,5,6,7,8,9,10) |
||||
|
_debug _issuerdn "${_issuerdn}" |
||||
|
_info "Requesting OCSP response" |
||||
|
# If the issuer is a CA cert then our command line has "-CAfile" added |
||||
|
if [ "${_subjectdn}" = "${_issuerdn}" ]; then |
||||
|
_cafile_argument="-CAfile \"${_issuer}\"" |
||||
|
else |
||||
|
_cafile_argument="" |
||||
|
fi |
||||
|
_debug _cafile_argument "${_cafile_argument}" |
||||
|
# if OpenSSL/LibreSSL is v1.1 or above, the format for the -header option has changed |
||||
|
_openssl_version=$(${ACME_OPENSSL_BIN:-openssl} version | cut -d' ' -f2) |
||||
|
_debug _openssl_version "${_openssl_version}" |
||||
|
_openssl_major=$(echo "${_openssl_version}" | cut -d '.' -f1) |
||||
|
_openssl_minor=$(echo "${_openssl_version}" | cut -d '.' -f2) |
||||
|
if [ "${_openssl_major}" -eq "1" ] && [ "${_openssl_minor}" -ge "1" ] || [ "${_openssl_major}" -ge "2" ]; then |
||||
|
_header_sep="=" |
||||
|
else |
||||
|
_header_sep=" " |
||||
|
fi |
||||
|
# Request the OCSP response from the issuer and store it |
||||
|
_openssl_ocsp_cmd="${ACME_OPENSSL_BIN:-openssl} ocsp \ |
||||
|
-issuer \"${_issuer}\" \ |
||||
|
-cert \"${_pem}\" \ |
||||
|
-url \"${_ocsp_url}\" \ |
||||
|
-header Host${_header_sep}\"${_ocsp_host}\" \ |
||||
|
-respout \"${_ocsp}\" \ |
||||
|
-verify_other \"${_issuer}\" \ |
||||
|
${_cafile_argument} \ |
||||
|
| grep -q \"${_pem}: good\"" |
||||
|
_debug _openssl_ocsp_cmd "${_openssl_ocsp_cmd}" |
||||
|
eval "${_openssl_ocsp_cmd}" |
||||
|
_ret=$? |
||||
|
else |
||||
|
# Non fatal: No issuer file was present so no OCSP stapling file created |
||||
|
_err "OCSP stapling in use but no .issuer file was present" |
||||
|
fi |
||||
|
else |
||||
|
# Non fatal: No OCSP url was found int the certificate |
||||
|
_err "OCSP update requested but no OCSP URL was found in certificate" |
||||
|
fi |
||||
|
|
||||
|
# Non fatal: Check return code of openssl command |
||||
|
if [ "${_ret}" != "0" ]; then |
||||
|
_err "Updating OCSP stapling failed with return code ${_ret}" |
||||
|
fi |
||||
|
else |
||||
|
# An OCSP file was already present but certificate did not have OCSP extension |
||||
|
if [ -f "${_ocsp}" ]; then |
||||
|
_err "OCSP was not requested but .ocsp file exists." |
||||
|
# Could remove the file at this step, although Lighttpd just ignores it in this case |
||||
|
# rm -f "${_ocsp}" || _err "Problem removing stale .ocsp file" |
||||
|
fi |
||||
|
fi |
||||
|
|
||||
|
# Reload Lighttpd |
||||
|
_debug _reload "${_reload}" |
||||
|
eval "${_reload}" |
||||
|
_ret=$? |
||||
|
if [ "${_ret}" != "0" ]; then |
||||
|
_err "Error code ${_ret} during reload" |
||||
|
return ${_ret} |
||||
|
else |
||||
|
_info "Reload successful" |
||||
|
fi |
||||
|
|
||||
|
return 0 |
||||
|
} |
||||
@ -0,0 +1,156 @@ |
|||||
|
#!/usr/bin/env sh |
||||
|
|
||||
|
# This deploy hook is tested on OpenMediaVault 5.x. It supports both local and remote deployment. |
||||
|
# The way it works is that if a cert with the matching domain name is not found, it will firstly create a dummy cert to get its uuid, and then replace it with your cert. |
||||
|
# |
||||
|
# DEPLOY_OMV_WEBUI_ADMIN - This is OMV web gui admin account. Default value is admin. It's required as the user parameter (-u) for the omv-rpc command. |
||||
|
# DEPLOY_OMV_HOST and DEPLOY_OMV_SSH_USER are optional. They are used for remote deployment through ssh (support public key authentication only). Per design, OMV web gui admin doesn't have ssh permission, so another account is needed for ssh. |
||||
|
# |
||||
|
# returns 0 means success, otherwise error. |
||||
|
|
||||
|
######## Public functions ##################### |
||||
|
|
||||
|
#domain keyfile certfile cafile fullchain |
||||
|
openmediavault_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" |
||||
|
|
||||
|
_getdeployconf DEPLOY_OMV_WEBUI_ADMIN |
||||
|
|
||||
|
if [ -z "$DEPLOY_OMV_WEBUI_ADMIN" ]; then |
||||
|
DEPLOY_OMV_WEBUI_ADMIN="admin" |
||||
|
fi |
||||
|
|
||||
|
_savedeployconf DEPLOY_OMV_WEBUI_ADMIN "$DEPLOY_OMV_WEBUI_ADMIN" |
||||
|
|
||||
|
_getdeployconf DEPLOY_OMV_HOST |
||||
|
_getdeployconf DEPLOY_OMV_SSH_USER |
||||
|
|
||||
|
if [ -n "$DEPLOY_OMV_HOST" ] && [ -n "$DEPLOY_OMV_SSH_USER" ]; then |
||||
|
_info "[OMV deploy-hook] Deploy certificate remotely through ssh." |
||||
|
_savedeployconf DEPLOY_OMV_HOST "$DEPLOY_OMV_HOST" |
||||
|
_savedeployconf DEPLOY_OMV_SSH_USER "$DEPLOY_OMV_SSH_USER" |
||||
|
else |
||||
|
_info "[OMV deploy-hook] Deploy certificate locally." |
||||
|
fi |
||||
|
|
||||
|
if [ -n "$DEPLOY_OMV_HOST" ] && [ -n "$DEPLOY_OMV_SSH_USER" ]; then |
||||
|
|
||||
|
_command="omv-rpc -u $DEPLOY_OMV_WEBUI_ADMIN 'CertificateMgmt' 'getList' '{\"start\": 0, \"limit\": -1}' | jq -r '.data[] | select(.name==\"/CN='$_cdomain'\") | .uuid'" |
||||
|
# shellcheck disable=SC2029 |
||||
|
_uuid=$(ssh "$DEPLOY_OMV_SSH_USER@$DEPLOY_OMV_HOST" "$_command") |
||||
|
_debug _command "$_command" |
||||
|
|
||||
|
if [ -z "$_uuid" ]; then |
||||
|
_info "[OMV deploy-hook] Domain $_cdomain has no certificate in openmediavault, creating it!" |
||||
|
_command="omv-rpc -u $DEPLOY_OMV_WEBUI_ADMIN 'CertificateMgmt' 'create' '{\"cn\": \"test.example.com\", \"size\": 4096, \"days\": 3650, \"c\": \"\", \"st\": \"\", \"l\": \"\", \"o\": \"\", \"ou\": \"\", \"email\": \"\"}' | jq -r '.uuid'" |
||||
|
# shellcheck disable=SC2029 |
||||
|
_uuid=$(ssh "$DEPLOY_OMV_SSH_USER@$DEPLOY_OMV_HOST" "$_command") |
||||
|
_debug _command "$_command" |
||||
|
|
||||
|
if [ -z "$_uuid" ]; then |
||||
|
_err "[OMV deploy-hook] An error occured while creating the certificate" |
||||
|
return 1 |
||||
|
fi |
||||
|
fi |
||||
|
|
||||
|
_info "[OMV deploy-hook] Domain $_cdomain has uuid: $_uuid" |
||||
|
_fullchain=$(jq <"$_cfullchain" -aRs .) |
||||
|
_key=$(jq <"$_ckey" -aRs .) |
||||
|
|
||||
|
_debug _fullchain "$_fullchain" |
||||
|
_debug _key "$_key" |
||||
|
|
||||
|
_info "[OMV deploy-hook] Updating key and certificate in openmediavault" |
||||
|
_command="omv-rpc -u $DEPLOY_OMV_WEBUI_ADMIN 'CertificateMgmt' 'set' '{\"uuid\":\"$_uuid\", \"certificate\":$_fullchain, \"privatekey\":$_key, \"comment\":\"acme.sh deployed $(date)\"}'" |
||||
|
# shellcheck disable=SC2029 |
||||
|
_result=$(ssh "$DEPLOY_OMV_SSH_USER@$DEPLOY_OMV_HOST" "$_command") |
||||
|
|
||||
|
_debug _command "$_command" |
||||
|
_debug _result "$_result" |
||||
|
|
||||
|
_command="omv-rpc -u $DEPLOY_OMV_WEBUI_ADMIN 'WebGui' 'setSettings' \$(omv-rpc -u $DEPLOY_OMV_WEBUI_ADMIN 'WebGui' 'getSettings' | jq -c '.sslcertificateref=\"$_uuid\"')" |
||||
|
# shellcheck disable=SC2029 |
||||
|
_result=$(ssh "$DEPLOY_OMV_SSH_USER@$DEPLOY_OMV_HOST" "$_command") |
||||
|
|
||||
|
_debug _command "$_command" |
||||
|
_debug _result "$_result" |
||||
|
|
||||
|
_info "[OMV deploy-hook] Asking openmediavault to apply changes... (this could take some time, hang in there)" |
||||
|
_command="omv-rpc -u $DEPLOY_OMV_WEBUI_ADMIN 'Config' 'applyChanges' '{\"modules\":[], \"force\": false}'" |
||||
|
# shellcheck disable=SC2029 |
||||
|
_result=$(ssh "$DEPLOY_OMV_SSH_USER@$DEPLOY_OMV_HOST" "$_command") |
||||
|
|
||||
|
_debug _command "$_command" |
||||
|
_debug _result "$_result" |
||||
|
|
||||
|
_info "[OMV deploy-hook] Asking nginx to reload" |
||||
|
_command="nginx -s reload" |
||||
|
# shellcheck disable=SC2029 |
||||
|
_result=$(ssh "$DEPLOY_OMV_SSH_USER@$DEPLOY_OMV_HOST" "$_command") |
||||
|
|
||||
|
_debug _command "$_command" |
||||
|
_debug _result "$_result" |
||||
|
|
||||
|
else |
||||
|
|
||||
|
# shellcheck disable=SC2086 |
||||
|
_uuid=$(omv-rpc -u $DEPLOY_OMV_WEBUI_ADMIN 'CertificateMgmt' 'getList' '{"start": 0, "limit": -1}' | jq -r '.data[] | select(.name=="/CN='$_cdomain'") | .uuid') |
||||
|
if [ -z "$_uuid" ]; then |
||||
|
_info "[OMV deploy-hook] Domain $_cdomain has no certificate in openmediavault, creating it!" |
||||
|
# shellcheck disable=SC2086 |
||||
|
_uuid=$(omv-rpc -u $DEPLOY_OMV_WEBUI_ADMIN 'CertificateMgmt' 'create' '{"cn": "test.example.com", "size": 4096, "days": 3650, "c": "", "st": "", "l": "", "o": "", "ou": "", "email": ""}' | jq -r '.uuid') |
||||
|
|
||||
|
if [ -z "$_uuid" ]; then |
||||
|
_err "[OMB deploy-hook] An error occured while creating the certificate" |
||||
|
return 1 |
||||
|
fi |
||||
|
fi |
||||
|
|
||||
|
_info "[OMV deploy-hook] Domain $_cdomain has uuid: $_uuid" |
||||
|
_fullchain=$(jq <"$_cfullchain" -aRs .) |
||||
|
_key=$(jq <"$_ckey" -aRs .) |
||||
|
|
||||
|
_debug _fullchain "$_fullchain" |
||||
|
_debug _key "$_key" |
||||
|
|
||||
|
_info "[OMV deploy-hook] Updating key and certificate in openmediavault" |
||||
|
_command="omv-rpc -u $DEPLOY_OMV_WEBUI_ADMIN 'CertificateMgmt' 'set' '{\"uuid\":\"$_uuid\", \"certificate\":$_fullchain, \"privatekey\":$_key, \"comment\":\"acme.sh deployed $(date)\"}'" |
||||
|
_result=$(eval "$_command") |
||||
|
|
||||
|
_debug _command "$_command" |
||||
|
_debug _result "$_result" |
||||
|
|
||||
|
_command="omv-rpc -u $DEPLOY_OMV_WEBUI_ADMIN 'WebGui' 'setSettings' \$(omv-rpc -u $DEPLOY_OMV_WEBUI_ADMIN 'WebGui' 'getSettings' | jq -c '.sslcertificateref=\"$_uuid\"')" |
||||
|
_result=$(eval "$_command") |
||||
|
|
||||
|
_debug _command "$_command" |
||||
|
_debug _result "$_result" |
||||
|
|
||||
|
_info "[OMV deploy-hook] Asking openmediavault to apply changes... (this could take some time, hang in there)" |
||||
|
_command="omv-rpc -u $DEPLOY_OMV_WEBUI_ADMIN 'Config' 'applyChanges' '{\"modules\":[], \"force\": false}'" |
||||
|
_result=$(eval "$_command") |
||||
|
|
||||
|
_debug _command "$_command" |
||||
|
_debug _result "$_result" |
||||
|
|
||||
|
_info "[OMV deploy-hook] Asking nginx to reload" |
||||
|
_command="nginx -s reload" |
||||
|
_result=$(eval "$_command") |
||||
|
|
||||
|
_debug _command "$_command" |
||||
|
_debug _result "$_result" |
||||
|
|
||||
|
fi |
||||
|
|
||||
|
return 0 |
||||
|
} |
||||
@ -0,0 +1,262 @@ |
|||||
|
#!/usr/bin/env sh |
||||
|
|
||||
|
# OpenStack Barbican deploy hook |
||||
|
# |
||||
|
# This requires you to have OpenStackClient and python-barbicanclient |
||||
|
# installed. |
||||
|
# |
||||
|
# You will require Keystone V3 credentials loaded into your environment, which |
||||
|
# could be either password or v3applicationcredential type. |
||||
|
# |
||||
|
# Author: Andy Botting <andy@andybotting.com> |
||||
|
|
||||
|
openstack_deploy() { |
||||
|
_cdomain="$1" |
||||
|
_ckey="$2" |
||||
|
_ccert="$3" |
||||
|
_cca="$4" |
||||
|
_cfullchain="$5" |
||||
|
|
||||
|
_debug _cdomain "$_cdomain" |
||||
|
_debug _ckey "$_ckey" |
||||
|
_debug _ccert "$_ccert" |
||||
|
_debug _cca "$_cca" |
||||
|
_debug _cfullchain "$_cfullchain" |
||||
|
|
||||
|
if ! _exists openstack; then |
||||
|
_err "OpenStack client not found" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_openstack_credentials || return $? |
||||
|
|
||||
|
_info "Generate import pkcs12" |
||||
|
_import_pkcs12="$(_mktemp)" |
||||
|
if ! _openstack_to_pkcs "$_import_pkcs12" "$_ckey" "$_ccert" "$_cca"; then |
||||
|
_err "Error creating pkcs12 certificate" |
||||
|
return 1 |
||||
|
fi |
||||
|
_debug _import_pkcs12 "$_import_pkcs12" |
||||
|
_base64_pkcs12=$(_base64 "multiline" <"$_import_pkcs12") |
||||
|
|
||||
|
secretHrefs=$(_openstack_get_secrets) |
||||
|
_debug secretHrefs "$secretHrefs" |
||||
|
_openstack_store_secret || return $? |
||||
|
|
||||
|
if [ -n "$secretHrefs" ]; then |
||||
|
_info "Cleaning up existing secret" |
||||
|
_openstack_delete_secrets || return $? |
||||
|
fi |
||||
|
|
||||
|
_info "Certificate successfully deployed" |
||||
|
return 0 |
||||
|
} |
||||
|
|
||||
|
_openstack_store_secret() { |
||||
|
if ! openstack secret store --name "$_cdomain." -t 'application/octet-stream' -e base64 --payload "$_base64_pkcs12"; then |
||||
|
_err "Failed to create OpenStack secret" |
||||
|
return 1 |
||||
|
fi |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
_openstack_delete_secrets() { |
||||
|
echo "$secretHrefs" | while read -r secretHref; do |
||||
|
_info "Deleting old secret $secretHref" |
||||
|
if ! openstack secret delete "$secretHref"; then |
||||
|
_err "Failed to delete OpenStack secret" |
||||
|
return 1 |
||||
|
fi |
||||
|
done |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
_openstack_get_secrets() { |
||||
|
if ! secretHrefs=$(openstack secret list -f value --name "$_cdomain." | cut -d' ' -f1); then |
||||
|
_err "Failed to list secrets" |
||||
|
return 1 |
||||
|
fi |
||||
|
echo "$secretHrefs" |
||||
|
} |
||||
|
|
||||
|
_openstack_to_pkcs() { |
||||
|
# The existing _toPkcs command can't allow an empty password, due to sh |
||||
|
# -z test, so copied here and forcing the empty password. |
||||
|
_cpfx="$1" |
||||
|
_ckey="$2" |
||||
|
_ccert="$3" |
||||
|
_cca="$4" |
||||
|
|
||||
|
${ACME_OPENSSL_BIN:-openssl} pkcs12 -export -out "$_cpfx" -inkey "$_ckey" -in "$_ccert" -certfile "$_cca" -password "pass:" |
||||
|
} |
||||
|
|
||||
|
_openstack_credentials() { |
||||
|
_debug "Check OpenStack credentials" |
||||
|
|
||||
|
# If we have OS_AUTH_URL already set in the environment, then assume we want |
||||
|
# to use those, otherwise use stored credentials |
||||
|
if [ -n "$OS_AUTH_URL" ]; then |
||||
|
_debug "OS_AUTH_URL env var found, using environment" |
||||
|
else |
||||
|
_debug "OS_AUTH_URL not found, loading stored credentials" |
||||
|
OS_AUTH_URL="${OS_AUTH_URL:-$(_readaccountconf_mutable OS_AUTH_URL)}" |
||||
|
OS_IDENTITY_API_VERSION="${OS_IDENTITY_API_VERSION:-$(_readaccountconf_mutable OS_IDENTITY_API_VERSION)}" |
||||
|
OS_AUTH_TYPE="${OS_AUTH_TYPE:-$(_readaccountconf_mutable OS_AUTH_TYPE)}" |
||||
|
OS_APPLICATION_CREDENTIAL_ID="${OS_APPLICATION_CREDENTIAL_ID:-$(_readaccountconf_mutable OS_APPLICATION_CREDENTIAL_ID)}" |
||||
|
OS_APPLICATION_CREDENTIAL_SECRET="${OS_APPLICATION_CREDENTIAL_SECRET:-$(_readaccountconf_mutable OS_APPLICATION_CREDENTIAL_SECRET)}" |
||||
|
OS_USERNAME="${OS_USERNAME:-$(_readaccountconf_mutable OS_USERNAME)}" |
||||
|
OS_PASSWORD="${OS_PASSWORD:-$(_readaccountconf_mutable OS_PASSWORD)}" |
||||
|
OS_PROJECT_NAME="${OS_PROJECT_NAME:-$(_readaccountconf_mutable OS_PROJECT_NAME)}" |
||||
|
OS_PROJECT_ID="${OS_PROJECT_ID:-$(_readaccountconf_mutable OS_PROJECT_ID)}" |
||||
|
OS_USER_DOMAIN_NAME="${OS_USER_DOMAIN_NAME:-$(_readaccountconf_mutable OS_USER_DOMAIN_NAME)}" |
||||
|
OS_USER_DOMAIN_ID="${OS_USER_DOMAIN_ID:-$(_readaccountconf_mutable OS_USER_DOMAIN_ID)}" |
||||
|
OS_PROJECT_DOMAIN_NAME="${OS_PROJECT_DOMAIN_NAME:-$(_readaccountconf_mutable OS_PROJECT_DOMAIN_NAME)}" |
||||
|
OS_PROJECT_DOMAIN_ID="${OS_PROJECT_DOMAIN_ID:-$(_readaccountconf_mutable OS_PROJECT_DOMAIN_ID)}" |
||||
|
fi |
||||
|
|
||||
|
# Check each var and either save or clear it depending on whether its set. |
||||
|
# The helps us clear out old vars in the case where a user may want |
||||
|
# to switch between password and app creds |
||||
|
_debug "OS_AUTH_URL" "$OS_AUTH_URL" |
||||
|
if [ -n "$OS_AUTH_URL" ]; then |
||||
|
export OS_AUTH_URL |
||||
|
_saveaccountconf_mutable OS_AUTH_URL "$OS_AUTH_URL" |
||||
|
else |
||||
|
unset OS_AUTH_URL |
||||
|
_clearaccountconf SAVED_OS_AUTH_URL |
||||
|
fi |
||||
|
|
||||
|
_debug "OS_IDENTITY_API_VERSION" "$OS_IDENTITY_API_VERSION" |
||||
|
if [ -n "$OS_IDENTITY_API_VERSION" ]; then |
||||
|
export OS_IDENTITY_API_VERSION |
||||
|
_saveaccountconf_mutable OS_IDENTITY_API_VERSION "$OS_IDENTITY_API_VERSION" |
||||
|
else |
||||
|
unset OS_IDENTITY_API_VERSION |
||||
|
_clearaccountconf SAVED_OS_IDENTITY_API_VERSION |
||||
|
fi |
||||
|
|
||||
|
_debug "OS_AUTH_TYPE" "$OS_AUTH_TYPE" |
||||
|
if [ -n "$OS_AUTH_TYPE" ]; then |
||||
|
export OS_AUTH_TYPE |
||||
|
_saveaccountconf_mutable OS_AUTH_TYPE "$OS_AUTH_TYPE" |
||||
|
else |
||||
|
unset OS_AUTH_TYPE |
||||
|
_clearaccountconf SAVED_OS_AUTH_TYPE |
||||
|
fi |
||||
|
|
||||
|
_debug "OS_APPLICATION_CREDENTIAL_ID" "$OS_APPLICATION_CREDENTIAL_ID" |
||||
|
if [ -n "$OS_APPLICATION_CREDENTIAL_ID" ]; then |
||||
|
export OS_APPLICATION_CREDENTIAL_ID |
||||
|
_saveaccountconf_mutable OS_APPLICATION_CREDENTIAL_ID "$OS_APPLICATION_CREDENTIAL_ID" |
||||
|
else |
||||
|
unset OS_APPLICATION_CREDENTIAL_ID |
||||
|
_clearaccountconf SAVED_OS_APPLICATION_CREDENTIAL_ID |
||||
|
fi |
||||
|
|
||||
|
_secure_debug "OS_APPLICATION_CREDENTIAL_SECRET" "$OS_APPLICATION_CREDENTIAL_SECRET" |
||||
|
if [ -n "$OS_APPLICATION_CREDENTIAL_SECRET" ]; then |
||||
|
export OS_APPLICATION_CREDENTIAL_SECRET |
||||
|
_saveaccountconf_mutable OS_APPLICATION_CREDENTIAL_SECRET "$OS_APPLICATION_CREDENTIAL_SECRET" |
||||
|
else |
||||
|
unset OS_APPLICATION_CREDENTIAL_SECRET |
||||
|
_clearaccountconf SAVED_OS_APPLICATION_CREDENTIAL_SECRET |
||||
|
fi |
||||
|
|
||||
|
_debug "OS_USERNAME" "$OS_USERNAME" |
||||
|
if [ -n "$OS_USERNAME" ]; then |
||||
|
export OS_USERNAME |
||||
|
_saveaccountconf_mutable OS_USERNAME "$OS_USERNAME" |
||||
|
else |
||||
|
unset OS_USERNAME |
||||
|
_clearaccountconf SAVED_OS_USERNAME |
||||
|
fi |
||||
|
|
||||
|
_secure_debug "OS_PASSWORD" "$OS_PASSWORD" |
||||
|
if [ -n "$OS_PASSWORD" ]; then |
||||
|
export OS_PASSWORD |
||||
|
_saveaccountconf_mutable OS_PASSWORD "$OS_PASSWORD" |
||||
|
else |
||||
|
unset OS_PASSWORD |
||||
|
_clearaccountconf SAVED_OS_PASSWORD |
||||
|
fi |
||||
|
|
||||
|
_debug "OS_PROJECT_NAME" "$OS_PROJECT_NAME" |
||||
|
if [ -n "$OS_PROJECT_NAME" ]; then |
||||
|
export OS_PROJECT_NAME |
||||
|
_saveaccountconf_mutable OS_PROJECT_NAME "$OS_PROJECT_NAME" |
||||
|
else |
||||
|
unset OS_PROJECT_NAME |
||||
|
_clearaccountconf SAVED_OS_PROJECT_NAME |
||||
|
fi |
||||
|
|
||||
|
_debug "OS_PROJECT_ID" "$OS_PROJECT_ID" |
||||
|
if [ -n "$OS_PROJECT_ID" ]; then |
||||
|
export OS_PROJECT_ID |
||||
|
_saveaccountconf_mutable OS_PROJECT_ID "$OS_PROJECT_ID" |
||||
|
else |
||||
|
unset OS_PROJECT_ID |
||||
|
_clearaccountconf SAVED_OS_PROJECT_ID |
||||
|
fi |
||||
|
|
||||
|
_debug "OS_USER_DOMAIN_NAME" "$OS_USER_DOMAIN_NAME" |
||||
|
if [ -n "$OS_USER_DOMAIN_NAME" ]; then |
||||
|
export OS_USER_DOMAIN_NAME |
||||
|
_saveaccountconf_mutable OS_USER_DOMAIN_NAME "$OS_USER_DOMAIN_NAME" |
||||
|
else |
||||
|
unset OS_USER_DOMAIN_NAME |
||||
|
_clearaccountconf SAVED_OS_USER_DOMAIN_NAME |
||||
|
fi |
||||
|
|
||||
|
_debug "OS_USER_DOMAIN_ID" "$OS_USER_DOMAIN_ID" |
||||
|
if [ -n "$OS_USER_DOMAIN_ID" ]; then |
||||
|
export OS_USER_DOMAIN_ID |
||||
|
_saveaccountconf_mutable OS_USER_DOMAIN_ID "$OS_USER_DOMAIN_ID" |
||||
|
else |
||||
|
unset OS_USER_DOMAIN_ID |
||||
|
_clearaccountconf SAVED_OS_USER_DOMAIN_ID |
||||
|
fi |
||||
|
|
||||
|
_debug "OS_PROJECT_DOMAIN_NAME" "$OS_PROJECT_DOMAIN_NAME" |
||||
|
if [ -n "$OS_PROJECT_DOMAIN_NAME" ]; then |
||||
|
export OS_PROJECT_DOMAIN_NAME |
||||
|
_saveaccountconf_mutable OS_PROJECT_DOMAIN_NAME "$OS_PROJECT_DOMAIN_NAME" |
||||
|
else |
||||
|
unset OS_PROJECT_DOMAIN_NAME |
||||
|
_clearaccountconf SAVED_OS_PROJECT_DOMAIN_NAME |
||||
|
fi |
||||
|
|
||||
|
_debug "OS_PROJECT_DOMAIN_ID" "$OS_PROJECT_DOMAIN_ID" |
||||
|
if [ -n "$OS_PROJECT_DOMAIN_ID" ]; then |
||||
|
export OS_PROJECT_DOMAIN_ID |
||||
|
_saveaccountconf_mutable OS_PROJECT_DOMAIN_ID "$OS_PROJECT_DOMAIN_ID" |
||||
|
else |
||||
|
unset OS_PROJECT_DOMAIN_ID |
||||
|
_clearaccountconf SAVED_OS_PROJECT_DOMAIN_ID |
||||
|
fi |
||||
|
|
||||
|
if [ "$OS_AUTH_TYPE" = "v3applicationcredential" ]; then |
||||
|
# Application Credential auth |
||||
|
if [ -z "$OS_APPLICATION_CREDENTIAL_ID" ] || [ -z "$OS_APPLICATION_CREDENTIAL_SECRET" ]; then |
||||
|
_err "When using OpenStack application credentials, OS_APPLICATION_CREDENTIAL_ID" |
||||
|
_err "and OS_APPLICATION_CREDENTIAL_SECRET must be set." |
||||
|
_err "Please check your credentials and try again." |
||||
|
return 1 |
||||
|
fi |
||||
|
else |
||||
|
# Password auth |
||||
|
if [ -z "$OS_USERNAME" ] || [ -z "$OS_PASSWORD" ]; then |
||||
|
_err "OpenStack username or password not found." |
||||
|
_err "Please check your credentials and try again." |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
if [ -z "$OS_PROJECT_NAME" ] && [ -z "$OS_PROJECT_ID" ]; then |
||||
|
_err "When using password authentication, OS_PROJECT_NAME or" |
||||
|
_err "OS_PROJECT_ID must be set." |
||||
|
_err "Please check your credentials and try again." |
||||
|
return 1 |
||||
|
fi |
||||
|
fi |
||||
|
|
||||
|
return 0 |
||||
|
} |
||||
@ -0,0 +1,123 @@ |
|||||
|
#!/usr/bin/env sh |
||||
|
|
||||
|
# Script to deploy cert to Peplink Routers |
||||
|
# |
||||
|
# The following environment variables must be set: |
||||
|
# |
||||
|
# PEPLINK_Hostname - Peplink hostname |
||||
|
# PEPLINK_Username - Peplink username to login |
||||
|
# PEPLINK_Password - Peplink password to login |
||||
|
# |
||||
|
# The following environmental variables may be set if you don't like their |
||||
|
# default values: |
||||
|
# |
||||
|
# PEPLINK_Certtype - Certificate type to target for replacement |
||||
|
# defaults to "webadmin", can be one of: |
||||
|
# * "chub" (ContentHub) |
||||
|
# * "openvpn" (OpenVPN CA) |
||||
|
# * "portal" (Captive Portal SSL) |
||||
|
# * "webadmin" (Web Admin SSL) |
||||
|
# * "webproxy" (Proxy Root CA) |
||||
|
# * "wwan_ca" (Wi-Fi WAN CA) |
||||
|
# * "wwan_client" (Wi-Fi WAN Client) |
||||
|
# PEPLINK_Scheme - defaults to "https" |
||||
|
# PEPLINK_Port - defaults to "443" |
||||
|
# |
||||
|
#returns 0 means success, otherwise error. |
||||
|
|
||||
|
######## Public functions ##################### |
||||
|
|
||||
|
_peplink_get_cookie_data() { |
||||
|
grep -i "\W$1=" | grep -i "^Set-Cookie:" | _tail_n 1 | _egrep_o "$1=[^;]*;" | tr -d ';' |
||||
|
} |
||||
|
|
||||
|
#domain keyfile certfile cafile fullchain |
||||
|
peplink_deploy() { |
||||
|
|
||||
|
_cdomain="$1" |
||||
|
_ckey="$2" |
||||
|
_cfullchain="$5" |
||||
|
|
||||
|
_debug _cdomain "$_cdomain" |
||||
|
_debug _cfullchain "$_cfullchain" |
||||
|
_debug _ckey "$_ckey" |
||||
|
|
||||
|
# Get Hostname, Username and Password, but don't save until we successfully authenticate |
||||
|
_getdeployconf PEPLINK_Hostname |
||||
|
_getdeployconf PEPLINK_Username |
||||
|
_getdeployconf PEPLINK_Password |
||||
|
if [ -z "${PEPLINK_Hostname:-}" ] || [ -z "${PEPLINK_Username:-}" ] || [ -z "${PEPLINK_Password:-}" ]; then |
||||
|
_err "PEPLINK_Hostname & PEPLINK_Username & PEPLINK_Password must be set" |
||||
|
return 1 |
||||
|
fi |
||||
|
_debug2 PEPLINK_Hostname "$PEPLINK_Hostname" |
||||
|
_debug2 PEPLINK_Username "$PEPLINK_Username" |
||||
|
_secure_debug2 PEPLINK_Password "$PEPLINK_Password" |
||||
|
|
||||
|
# Optional certificate type, scheme, and port for Peplink |
||||
|
_getdeployconf PEPLINK_Certtype |
||||
|
_getdeployconf PEPLINK_Scheme |
||||
|
_getdeployconf PEPLINK_Port |
||||
|
|
||||
|
# Don't save the certificate type until we verify it exists and is supported |
||||
|
_savedeployconf PEPLINK_Scheme "$PEPLINK_Scheme" |
||||
|
_savedeployconf PEPLINK_Port "$PEPLINK_Port" |
||||
|
|
||||
|
# Default vaules for certificate type, scheme, and port |
||||
|
[ -n "${PEPLINK_Certtype}" ] || PEPLINK_Certtype="webadmin" |
||||
|
[ -n "${PEPLINK_Scheme}" ] || PEPLINK_Scheme="https" |
||||
|
[ -n "${PEPLINK_Port}" ] || PEPLINK_Port="443" |
||||
|
|
||||
|
_debug2 PEPLINK_Certtype "$PEPLINK_Certtype" |
||||
|
_debug2 PEPLINK_Scheme "$PEPLINK_Scheme" |
||||
|
_debug2 PEPLINK_Port "$PEPLINK_Port" |
||||
|
|
||||
|
_base_url="$PEPLINK_Scheme://$PEPLINK_Hostname:$PEPLINK_Port" |
||||
|
_debug _base_url "$_base_url" |
||||
|
|
||||
|
# Login, get the auth token from the cookie |
||||
|
_info "Logging into $PEPLINK_Hostname:$PEPLINK_Port" |
||||
|
encoded_username="$(printf "%s" "$PEPLINK_Username" | _url_encode)" |
||||
|
encoded_password="$(printf "%s" "$PEPLINK_Password" | _url_encode)" |
||||
|
response=$(_post "func=login&username=$encoded_username&password=$encoded_password" "$_base_url/cgi-bin/MANGA/api.cgi") |
||||
|
auth_token=$(_peplink_get_cookie_data "bauth" <"$HTTP_HEADER") |
||||
|
_debug3 response "$response" |
||||
|
_debug auth_token "$auth_token" |
||||
|
|
||||
|
if [ -z "$auth_token" ]; then |
||||
|
_err "Unable to authenticate to $PEPLINK_Hostname:$PEPLINK_Port using $PEPLINK_Scheme." |
||||
|
_err "Check your username and password." |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_H1="Cookie: $auth_token" |
||||
|
export _H1 |
||||
|
_debug2 H1 "${_H1}" |
||||
|
|
||||
|
# Now that we know the hostnameusername and password are good, save them |
||||
|
_savedeployconf PEPLINK_Hostname "$PEPLINK_Hostname" |
||||
|
_savedeployconf PEPLINK_Username "$PEPLINK_Username" |
||||
|
_savedeployconf PEPLINK_Password "$PEPLINK_Password" |
||||
|
|
||||
|
_info "Generate form POST request" |
||||
|
|
||||
|
encoded_key="$(_url_encode <"$_ckey")" |
||||
|
encoded_fullchain="$(_url_encode <"$_cfullchain")" |
||||
|
body="cert_type=$PEPLINK_Certtype&cert_uid=§ion=CERT_modify&key_pem=$encoded_key&key_pem_passphrase=&key_pem_passphrase_confirm=&cert_pem=$encoded_fullchain" |
||||
|
_debug3 body "$body" |
||||
|
|
||||
|
_info "Upload $PEPLINK_Certtype certificate to the Peplink" |
||||
|
|
||||
|
response=$(_post "$body" "$_base_url/cgi-bin/MANGA/admin.cgi") |
||||
|
_debug3 response "$response" |
||||
|
|
||||
|
if echo "$response" | grep 'Success' >/dev/null; then |
||||
|
# We've verified this certificate type is valid, so save it |
||||
|
_savedeployconf PEPLINK_Certtype "$PEPLINK_Certtype" |
||||
|
_info "Certificate was updated" |
||||
|
return 0 |
||||
|
else |
||||
|
_err "Unable to update certificate, error code $response" |
||||
|
return 1 |
||||
|
fi |
||||
|
} |
||||
@ -0,0 +1,180 @@ |
|||||
|
#!/usr/bin/env sh |
||||
|
|
||||
|
# Here is a scipt to deploy the cert to your TrueNAS using the REST API. |
||||
|
# https://www.truenas.com/docs/hub/additional-topics/api/rest_api.html |
||||
|
# |
||||
|
# Written by Frank Plass github@f-plass.de |
||||
|
# https://github.com/danb35/deploy-freenas/blob/master/deploy_freenas.py |
||||
|
# Thanks to danb35 for your template! |
||||
|
# |
||||
|
# Following environment variables must be set: |
||||
|
# |
||||
|
# export DEPLOY_TRUENAS_APIKEY="<API_KEY_GENERATED_IN_THE_WEB_UI" |
||||
|
# |
||||
|
# The following environmental variables may be set if you don't like their |
||||
|
# default values: |
||||
|
# |
||||
|
# DEPLOY_TRUENAS_HOSTNAME - defaults to localhost |
||||
|
# DEPLOY_TRUENAS_SCHEME - defaults to http, set alternatively to https |
||||
|
# |
||||
|
#returns 0 means success, otherwise error. |
||||
|
|
||||
|
######## Public functions ##################### |
||||
|
|
||||
|
#domain keyfile certfile cafile fullchain |
||||
|
truenas_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" |
||||
|
|
||||
|
_getdeployconf DEPLOY_TRUENAS_APIKEY |
||||
|
|
||||
|
if [ -z "$DEPLOY_TRUENAS_APIKEY" ]; then |
||||
|
_err "TrueNAS Api Key is not found, please define DEPLOY_TRUENAS_APIKEY." |
||||
|
return 1 |
||||
|
fi |
||||
|
_secure_debug2 DEPLOY_TRUENAS_APIKEY "$DEPLOY_TRUENAS_APIKEY" |
||||
|
|
||||
|
# Optional hostname, scheme for TrueNAS |
||||
|
_getdeployconf DEPLOY_TRUENAS_HOSTNAME |
||||
|
_getdeployconf DEPLOY_TRUENAS_SCHEME |
||||
|
|
||||
|
# default values for hostname and scheme |
||||
|
[ -n "${DEPLOY_TRUENAS_HOSTNAME}" ] || DEPLOY_TRUENAS_HOSTNAME="localhost" |
||||
|
[ -n "${DEPLOY_TRUENAS_SCHEME}" ] || DEPLOY_TRUENAS_SCHEME="http" |
||||
|
|
||||
|
_debug2 DEPLOY_TRUENAS_HOSTNAME "$DEPLOY_TRUENAS_HOSTNAME" |
||||
|
_debug2 DEPLOY_TRUENAS_SCHEME "$DEPLOY_TRUENAS_SCHEME" |
||||
|
|
||||
|
_api_url="$DEPLOY_TRUENAS_SCHEME://$DEPLOY_TRUENAS_HOSTNAME/api/v2.0" |
||||
|
_debug _api_url "$_api_url" |
||||
|
|
||||
|
_H1="Authorization: Bearer $DEPLOY_TRUENAS_APIKEY" |
||||
|
_secure_debug3 _H1 "$_H1" |
||||
|
|
||||
|
_info "Testing Connection TrueNAS" |
||||
|
_response=$(_get "$_api_url/system/state") |
||||
|
_info "TrueNAS System State: $_response." |
||||
|
|
||||
|
if [ -z "$_response" ]; then |
||||
|
_err "Unable to authenticate to $_api_url." |
||||
|
_err 'Check your Connection and set DEPLOY_TRUENAS_HOSTNAME="192.168.178.x".' |
||||
|
_err 'or' |
||||
|
_err 'set DEPLOY_TRUENAS_HOSTNAME="<truenas_dnsname>".' |
||||
|
_err 'Check your Connection and set DEPLOY_TRUENAS_SCHEME="https".' |
||||
|
_err "Check your Api Key." |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_savedeployconf DEPLOY_TRUENAS_APIKEY "$DEPLOY_TRUENAS_APIKEY" |
||||
|
_savedeployconf DEPLOY_TRUENAS_HOSTNAME "$DEPLOY_TRUENAS_HOSTNAME" |
||||
|
_savedeployconf DEPLOY_TRUENAS_SCHEME "$DEPLOY_TRUENAS_SCHEME" |
||||
|
|
||||
|
_info "Getting active certificate from TrueNAS" |
||||
|
_response=$(_get "$_api_url/system/general") |
||||
|
_active_cert_id=$(echo "$_response" | grep -B2 '"name":' | grep 'id' | tr -d -- '"id: ,') |
||||
|
_active_cert_name=$(echo "$_response" | grep '"name":' | sed -n 's/.*: "\(.\{1,\}\)",$/\1/p') |
||||
|
_param_httpsredirect=$(echo "$_response" | grep '"ui_httpsredirect":' | sed -n 's/.*": \(.\{1,\}\),$/\1/p') |
||||
|
_debug Active_UI_Certificate_ID "$_active_cert_id" |
||||
|
_debug Active_UI_Certificate_Name "$_active_cert_name" |
||||
|
_debug Active_UI_http_redirect "$_param_httpsredirect" |
||||
|
|
||||
|
if [ "$DEPLOY_TRUENAS_SCHEME" = "http" ] && [ "$_param_httpsredirect" = "true" ]; then |
||||
|
_info "http Redirect active" |
||||
|
_info "Setting DEPLOY_TRUENAS_SCHEME to 'https'" |
||||
|
DEPLOY_TRUENAS_SCHEME="https" |
||||
|
_api_url="$DEPLOY_TRUENAS_SCHEME://$DEPLOY_TRUENAS_HOSTNAME/api/v2.0" |
||||
|
_savedeployconf DEPLOY_TRUENAS_SCHEME "$DEPLOY_TRUENAS_SCHEME" |
||||
|
fi |
||||
|
|
||||
|
_info "Upload new certifikate to TrueNAS" |
||||
|
_certname="Letsencrypt_$(_utc_date | tr ' ' '_' | tr -d -- ':')" |
||||
|
_debug3 _certname "$_certname" |
||||
|
|
||||
|
_certData="{\"create_type\": \"CERTIFICATE_CREATE_IMPORTED\", \"name\": \"${_certname}\", \"certificate\": \"$(_json_encode <"$_cfullchain")\", \"privatekey\": \"$(_json_encode <"$_ckey")\"}" |
||||
|
_add_cert_result="$(_post "$_certData" "$_api_url/certificate" "" "POST" "application/json")" |
||||
|
|
||||
|
_debug3 _add_cert_result "$_add_cert_result" |
||||
|
|
||||
|
_info "Getting Certificate list to get new Cert ID" |
||||
|
_cert_list=$(_get "$_api_url/system/general/ui_certificate_choices") |
||||
|
_cert_id=$(echo "$_cert_list" | grep "$_certname" | sed -n 's/.*"\([0-9]\{1,\}\)".*$/\1/p') |
||||
|
|
||||
|
_debug3 _cert_id "$_cert_id" |
||||
|
|
||||
|
_info "Activate Certificate ID: $_cert_id" |
||||
|
_activateData="{\"ui_certificate\": \"${_cert_id}\"}" |
||||
|
_activate_result="$(_post "$_activateData" "$_api_url/system/general" "" "PUT" "application/json")" |
||||
|
|
||||
|
_debug3 _activate_result "$_activate_result" |
||||
|
|
||||
|
_info "Check if WebDAV certificate is the same as the WEB UI" |
||||
|
_webdav_list=$(_get "$_api_url/webdav") |
||||
|
_webdav_cert_id=$(echo "$_webdav_list" | grep '"certssl":' | tr -d -- '"certsl: ,') |
||||
|
|
||||
|
if [ "$_webdav_cert_id" = "$_active_cert_id" ]; then |
||||
|
_info "Update the WebDAV Certificate" |
||||
|
_debug _webdav_cert_id "$_webdav_cert_id" |
||||
|
_webdav_data="{\"certssl\": \"${_cert_id}\"}" |
||||
|
_activate_webdav_cert="$(_post "$_webdav_data" "$_api_url/webdav" "" "PUT" "application/json")" |
||||
|
_webdav_new_cert_id=$(echo "$_activate_webdav_cert" | _json_decode | sed -n 's/.*: \([0-9]\{1,\}\) }$/\1/p') |
||||
|
if [ "$_webdav_new_cert_id" -eq "$_cert_id" ]; then |
||||
|
_info "WebDAV Certificate update successfully" |
||||
|
else |
||||
|
_err "Unable to set WebDAV certificate" |
||||
|
_debug3 _activate_webdav_cert "$_activate_webdav_cert" |
||||
|
_debug3 _webdav_new_cert_id "$_webdav_new_cert_id" |
||||
|
return 1 |
||||
|
fi |
||||
|
_debug3 _webdav_new_cert_id "$_webdav_new_cert_id" |
||||
|
else |
||||
|
_info "WebDAV certificate not set or not the same as Web UI" |
||||
|
fi |
||||
|
|
||||
|
_info "Check if FTP certificate is the same as the WEB UI" |
||||
|
_ftp_list=$(_get "$_api_url/ftp") |
||||
|
_ftp_cert_id=$(echo "$_ftp_list" | grep '"ssltls_certificate":' | tr -d -- '"certislfa:_ ,') |
||||
|
|
||||
|
if [ "$_ftp_cert_id" = "$_active_cert_id" ]; then |
||||
|
_info "Update the FTP Certificate" |
||||
|
_debug _ftp_cert_id "$_ftp_cert_id" |
||||
|
_ftp_data="{\"ssltls_certificate\": \"${_cert_id}\"}" |
||||
|
_activate_ftp_cert="$(_post "$_ftp_data" "$_api_url/ftp" "" "PUT" "application/json")" |
||||
|
_ftp_new_cert_id=$(echo "$_activate_ftp_cert" | _json_decode | sed -n 's/.*: \([0-9]\{1,\}\) }$/\1/p') |
||||
|
if [ "$_ftp_new_cert_id" -eq "$_cert_id" ]; then |
||||
|
_info "FTP Certificate update successfully" |
||||
|
else |
||||
|
_err "Unable to set FTP certificate" |
||||
|
_debug3 _activate_ftp_cert "$_activate_ftp_cert" |
||||
|
_debug3 _ftp_new_cert_id "$_ftp_new_cert_id" |
||||
|
return 1 |
||||
|
fi |
||||
|
_debug3 _activate_ftp_cert "$_activate_ftp_cert" |
||||
|
else |
||||
|
_info "FTP certificate not set or not the same as Web UI" |
||||
|
fi |
||||
|
|
||||
|
_info "Delete old Certificate" |
||||
|
_delete_result="$(_post "" "$_api_url/certificate/id/$_active_cert_id" "" "DELETE" "application/json")" |
||||
|
|
||||
|
_debug3 _delete_result "$_delete_result" |
||||
|
|
||||
|
_info "Reload WebUI from TrueNAS" |
||||
|
_restart_UI=$(_get "$_api_url/system/general/ui_restart") |
||||
|
_debug2 _restart_UI "$_restart_UI" |
||||
|
|
||||
|
if [ -n "$_add_cert_result" ] && [ -n "$_activate_result" ]; then |
||||
|
return 0 |
||||
|
else |
||||
|
_err "Certupdate was not succesfull, please use --debug" |
||||
|
return 1 |
||||
|
fi |
||||
|
} |
||||
@ -0,0 +1,78 @@ |
|||||
|
#!/usr/bin/env sh |
||||
|
|
||||
|
# Here is a script to deploy cert to hashicorp vault using curl |
||||
|
# (https://www.vaultproject.io/) |
||||
|
# |
||||
|
# it requires following environment variables: |
||||
|
# |
||||
|
# VAULT_PREFIX - this contains the prefix path in vault |
||||
|
# VAULT_ADDR - vault requires this to find your vault server |
||||
|
# |
||||
|
# additionally, you need to ensure that VAULT_TOKEN is avialable |
||||
|
# to access the vault server |
||||
|
|
||||
|
#returns 0 means success, otherwise error. |
||||
|
|
||||
|
######## Public functions ##################### |
||||
|
|
||||
|
#domain keyfile certfile cafile fullchain |
||||
|
vault_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" |
||||
|
|
||||
|
# validate required env vars |
||||
|
_getdeployconf VAULT_PREFIX |
||||
|
if [ -z "$VAULT_PREFIX" ]; then |
||||
|
_err "VAULT_PREFIX needs to be defined (contains prefix path in vault)" |
||||
|
return 1 |
||||
|
fi |
||||
|
_savedeployconf VAULT_PREFIX "$VAULT_PREFIX" |
||||
|
|
||||
|
_getdeployconf VAULT_ADDR |
||||
|
if [ -z "$VAULT_ADDR" ]; then |
||||
|
_err "VAULT_ADDR needs to be defined (contains vault connection address)" |
||||
|
return 1 |
||||
|
fi |
||||
|
_savedeployconf VAULT_ADDR "$VAULT_ADDR" |
||||
|
|
||||
|
# JSON does not allow multiline strings. |
||||
|
# So replacing new-lines with "\n" here |
||||
|
_ckey=$(sed -z 's/\n/\\n/g' <"$2") |
||||
|
_ccert=$(sed -z 's/\n/\\n/g' <"$3") |
||||
|
_cca=$(sed -z 's/\n/\\n/g' <"$4") |
||||
|
_cfullchain=$(sed -z 's/\n/\\n/g' <"$5") |
||||
|
|
||||
|
URL="$VAULT_ADDR/v1/$VAULT_PREFIX/$_cdomain" |
||||
|
export _H1="X-Vault-Token: $VAULT_TOKEN" |
||||
|
|
||||
|
if [ -n "$FABIO" ]; then |
||||
|
if [ -n "$VAULT_KV_V2" ]; then |
||||
|
_post "{ \"data\": {\"cert\": \"$_cfullchain\", \"key\": \"$_ckey\"} }" "$URL" |
||||
|
else |
||||
|
_post "{\"cert\": \"$_cfullchain\", \"key\": \"$_ckey\"}" "$URL" |
||||
|
fi |
||||
|
else |
||||
|
if [ -n "$VAULT_KV_V2" ]; then |
||||
|
_post "{\"data\": {\"value\": \"$_ccert\"}}" "$URL/cert.pem" |
||||
|
_post "{\"data\": {\"value\": \"$_ckey\"}}" "$URL/cert.key" |
||||
|
_post "{\"data\": {\"value\": \"$_cca\"}}" "$URL/chain.pem" |
||||
|
_post "{\"data\": {\"value\": \"$_cfullchain\"}}" "$URL/fullchain.pem" |
||||
|
else |
||||
|
_post "{\"value\": \"$_ccert\"}" "$URL/cert.pem" |
||||
|
_post "{\"value\": \"$_ckey\"}" "$URL/cert.key" |
||||
|
_post "{\"value\": \"$_cca\"}" "$URL/chain.pem" |
||||
|
_post "{\"value\": \"$_cfullchain\"}" "$URL/fullchain.pem" |
||||
|
fi |
||||
|
fi |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,150 @@ |
|||||
|
#!/usr/bin/env sh |
||||
|
|
||||
|
# Anexia CloudDNS acme.sh hook |
||||
|
# Author: MA |
||||
|
|
||||
|
#ANX_Token="xxxx" |
||||
|
|
||||
|
ANX_API='https://engine.anexia-it.com/api/clouddns/v1' |
||||
|
|
||||
|
######## Public functions ##################### |
||||
|
|
||||
|
dns_anx_add() { |
||||
|
fulldomain=$1 |
||||
|
txtvalue=$2 |
||||
|
|
||||
|
_info "Using ANX CDNS API" |
||||
|
|
||||
|
ANX_Token="${ANX_Token:-$(_readaccountconf_mutable ANX_Token)}" |
||||
|
_debug fulldomain "$fulldomain" |
||||
|
_debug txtvalue "$txtvalue" |
||||
|
|
||||
|
if [ "$ANX_Token" ]; then |
||||
|
_saveaccountconf_mutable ANX_Token "$ANX_Token" |
||||
|
else |
||||
|
_err "You didn't specify a ANEXIA Engine API token." |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_debug "First detect the root zone" |
||||
|
if ! _get_root "$fulldomain"; then |
||||
|
_err "invalid domain" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
# Always add records, wildcard need two records with the same name |
||||
|
_anx_rest POST "zone.json/${_domain}/records" "{\"name\":\"$_sub_domain\",\"type\":\"TXT\",\"rdata\":\"$txtvalue\"}" |
||||
|
if _contains "$response" "$txtvalue"; then |
||||
|
return 0 |
||||
|
else |
||||
|
return 1 |
||||
|
fi |
||||
|
} |
||||
|
|
||||
|
dns_anx_rm() { |
||||
|
fulldomain=$1 |
||||
|
txtvalue=$2 |
||||
|
|
||||
|
_info "Using ANX CDNS API" |
||||
|
|
||||
|
ANX_Token="${ANX_Token:-$(_readaccountconf_mutable ANX_Token)}" |
||||
|
|
||||
|
_debug fulldomain "$fulldomain" |
||||
|
_debug txtvalue "$txtvalue" |
||||
|
|
||||
|
_debug "First detect the root zone" |
||||
|
if ! _get_root "$fulldomain"; then |
||||
|
_err "invalid domain" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_get_record_id |
||||
|
|
||||
|
if _is_uuid "$_record_id"; then |
||||
|
if ! _anx_rest DELETE "zone.json/${_domain}/records/$_record_id"; then |
||||
|
_err "Delete record" |
||||
|
return 1 |
||||
|
fi |
||||
|
else |
||||
|
_info "No record found." |
||||
|
fi |
||||
|
echo "$response" | tr -d " " | grep \"status\":\"OK\" >/dev/null |
||||
|
} |
||||
|
|
||||
|
#################### Private functions below ################################## |
||||
|
|
||||
|
_is_uuid() { |
||||
|
pattern='^\{?[A-Z0-9a-z]{8}-[A-Z0-9a-z]{4}-[A-Z0-9a-z]{4}-[A-Z0-9a-z]{4}-[A-Z0-9a-z]{12}\}?$' |
||||
|
if echo "$1" | _egrep_o "$pattern" >/dev/null; then |
||||
|
return 0 |
||||
|
fi |
||||
|
return 1 |
||||
|
} |
||||
|
|
||||
|
_get_record_id() { |
||||
|
_debug subdomain "$_sub_domain" |
||||
|
_debug domain "$_domain" |
||||
|
|
||||
|
if _anx_rest GET "zone.json/${_domain}/records?name=$_sub_domain&type=TXT"; then |
||||
|
_debug response "$response" |
||||
|
if _contains "$response" "\"name\":\"$_sub_domain\"" >/dev/null; then |
||||
|
_record_id=$(printf "%s\n" "$response" | _egrep_o "\[.\"identifier\":\"[^\"]*\"" | head -n 1 | cut -d : -f 2 | tr -d \") |
||||
|
else |
||||
|
_record_id='' |
||||
|
fi |
||||
|
else |
||||
|
_err "Search existing record" |
||||
|
fi |
||||
|
} |
||||
|
|
||||
|
_anx_rest() { |
||||
|
m=$1 |
||||
|
ep="$2" |
||||
|
data="$3" |
||||
|
_debug "$ep" |
||||
|
|
||||
|
export _H1="Content-Type: application/json" |
||||
|
export _H2="Authorization: Token $ANX_Token" |
||||
|
|
||||
|
if [ "$m" != "GET" ]; then |
||||
|
_debug data "$data" |
||||
|
response="$(_post "$data" "${ANX_API}/$ep" "" "$m")" |
||||
|
else |
||||
|
response="$(_get "${ANX_API}/$ep")" |
||||
|
fi |
||||
|
|
||||
|
# shellcheck disable=SC2181 |
||||
|
if [ "$?" != "0" ]; then |
||||
|
_err "error $ep" |
||||
|
return 1 |
||||
|
fi |
||||
|
_debug response "$response" |
||||
|
return 0 |
||||
|
} |
||||
|
|
||||
|
_get_root() { |
||||
|
domain=$1 |
||||
|
i=1 |
||||
|
p=1 |
||||
|
|
||||
|
_anx_rest GET "zone.json" |
||||
|
|
||||
|
while true; do |
||||
|
h=$(printf "%s" "$domain" | cut -d . -f $i-100) |
||||
|
_debug h "$h" |
||||
|
if [ -z "$h" ]; then |
||||
|
#not valid |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
if _contains "$response" "\"name\":\"$h\""; then |
||||
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) |
||||
|
_domain=$h |
||||
|
return 0 |
||||
|
fi |
||||
|
|
||||
|
p=$i |
||||
|
i=$(_math "$i" + 1) |
||||
|
done |
||||
|
return 1 |
||||
|
} |
||||
@ -0,0 +1,171 @@ |
|||||
|
#!/usr/bin/env sh |
||||
|
|
||||
|
# |
||||
|
#AURORA_Key="sdfsdfsdfljlbjkljlkjsdfoiwje" |
||||
|
# |
||||
|
#AURORA_Secret="sdfsdfsdfljlbjkljlkjsdfoiwje" |
||||
|
|
||||
|
AURORA_Api="https://api.auroradns.eu" |
||||
|
|
||||
|
######## Public functions ##################### |
||||
|
|
||||
|
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" |
||||
|
dns_aurora_add() { |
||||
|
fulldomain=$1 |
||||
|
txtvalue=$2 |
||||
|
|
||||
|
AURORA_Key="${AURORA_Key:-$(_readaccountconf_mutable AURORA_Key)}" |
||||
|
AURORA_Secret="${AURORA_Secret:-$(_readaccountconf_mutable AURORA_Secret)}" |
||||
|
|
||||
|
if [ -z "$AURORA_Key" ] || [ -z "$AURORA_Secret" ]; then |
||||
|
AURORA_Key="" |
||||
|
AURORA_Secret="" |
||||
|
_err "You didn't specify an Aurora api key and secret yet." |
||||
|
_err "You can get yours from here https://cp.pcextreme.nl/auroradns/users." |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
#save the api key and secret to the account conf file. |
||||
|
_saveaccountconf_mutable AURORA_Key "$AURORA_Key" |
||||
|
_saveaccountconf_mutable AURORA_Secret "$AURORA_Secret" |
||||
|
|
||||
|
_debug "First detect the root zone" |
||||
|
if ! _get_root "$fulldomain"; then |
||||
|
_err "invalid domain" |
||||
|
return 1 |
||||
|
fi |
||||
|
_debug _domain_id "$_domain_id" |
||||
|
_debug _sub_domain "$_sub_domain" |
||||
|
_debug _domain "$_domain" |
||||
|
|
||||
|
_info "Adding record" |
||||
|
if _aurora_rest POST "zones/$_domain_id/records" "{\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"content\":\"$txtvalue\",\"ttl\":300}"; then |
||||
|
if _contains "$response" "$txtvalue"; then |
||||
|
_info "Added, OK" |
||||
|
return 0 |
||||
|
elif _contains "$response" "RecordExistsError"; then |
||||
|
_info "Already exists, OK" |
||||
|
return 0 |
||||
|
else |
||||
|
_err "Add txt record error." |
||||
|
return 1 |
||||
|
fi |
||||
|
fi |
||||
|
_err "Add txt record error." |
||||
|
return 1 |
||||
|
|
||||
|
} |
||||
|
|
||||
|
#fulldomain txtvalue |
||||
|
dns_aurora_rm() { |
||||
|
fulldomain=$1 |
||||
|
txtvalue=$2 |
||||
|
|
||||
|
AURORA_Key="${AURORA_Key:-$(_readaccountconf_mutable AURORA_Key)}" |
||||
|
AURORA_Secret="${AURORA_Secret:-$(_readaccountconf_mutable AURORA_Secret)}" |
||||
|
|
||||
|
_debug "First detect the root zone" |
||||
|
if ! _get_root "$fulldomain"; then |
||||
|
_err "invalid domain" |
||||
|
return 1 |
||||
|
fi |
||||
|
_debug _domain_id "$_domain_id" |
||||
|
_debug _sub_domain "$_sub_domain" |
||||
|
_debug _domain "$_domain" |
||||
|
|
||||
|
_debug "Getting records" |
||||
|
_aurora_rest GET "zones/${_domain_id}/records" |
||||
|
|
||||
|
if ! _contains "$response" "$txtvalue"; then |
||||
|
_info "Don't need to remove." |
||||
|
else |
||||
|
records=$(echo "$response" | _normalizeJson | tr -d "[]" | sed "s/},{/}|{/g" | tr "|" "\n") |
||||
|
if [ "$(echo "$records" | wc -l)" -le 2 ]; then |
||||
|
_err "Can not parse records." |
||||
|
return 1 |
||||
|
fi |
||||
|
record_id=$(echo "$records" | grep "\"type\": *\"TXT\"" | grep "\"name\": *\"$_sub_domain\"" | grep "\"content\": *\"$txtvalue\"" | _egrep_o "\"id\": *\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | _head_n 1 | tr -d " ") |
||||
|
_debug "record_id" "$record_id" |
||||
|
if [ -z "$record_id" ]; then |
||||
|
_err "Can not get record id to remove." |
||||
|
return 1 |
||||
|
fi |
||||
|
if ! _aurora_rest DELETE "zones/$_domain_id/records/$record_id"; then |
||||
|
_err "Delete record error." |
||||
|
return 1 |
||||
|
fi |
||||
|
fi |
||||
|
return 0 |
||||
|
|
||||
|
} |
||||
|
|
||||
|
#################### Private functions below ################################## |
||||
|
#_acme-challenge.www.domain.com |
||||
|
#returns |
||||
|
# _sub_domain=_acme-challenge.www |
||||
|
# _domain=domain.com |
||||
|
# _domain_id=sdjkglgdfewsdfg |
||||
|
_get_root() { |
||||
|
domain=$1 |
||||
|
i=1 |
||||
|
p=1 |
||||
|
|
||||
|
while true; do |
||||
|
h=$(printf "%s" "$domain" | cut -d . -f $i-100) |
||||
|
_debug h "$h" |
||||
|
if [ -z "$h" ]; then |
||||
|
#not valid |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
if ! _aurora_rest GET "zones/$h"; then |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
if _contains "$response" "\"name\": \"$h\""; then |
||||
|
_domain_id=$(echo "$response" | _normalizeJson | tr -d "{}" | tr "," "\n" | grep "\"id\": *\"" | cut -d : -f 2 | tr -d \" | _head_n 1 | tr -d " ") |
||||
|
_debug _domain_id "$_domain_id" |
||||
|
if [ "$_domain_id" ]; then |
||||
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) |
||||
|
_domain=$h |
||||
|
return 0 |
||||
|
fi |
||||
|
return 1 |
||||
|
fi |
||||
|
p=$i |
||||
|
i=$(_math "$i" + 1) |
||||
|
done |
||||
|
return 1 |
||||
|
} |
||||
|
|
||||
|
_aurora_rest() { |
||||
|
m=$1 |
||||
|
ep="$2" |
||||
|
data="$3" |
||||
|
_debug "$ep" |
||||
|
|
||||
|
key_trimmed=$(echo "$AURORA_Key" | tr -d '"') |
||||
|
secret_trimmed=$(echo "$AURORA_Secret" | tr -d '"') |
||||
|
|
||||
|
timestamp=$(date -u +"%Y%m%dT%H%M%SZ") |
||||
|
signature=$(printf "%s/%s%s" "$m" "$ep" "$timestamp" | _hmac sha256 "$(printf "%s" "$secret_trimmed" | _hex_dump | tr -d " ")" | _base64) |
||||
|
authorization=$(printf "AuroraDNSv1 %s" "$(printf "%s:%s" "$key_trimmed" "$signature" | _base64)") |
||||
|
|
||||
|
export _H1="Content-Type: application/json; charset=UTF-8" |
||||
|
export _H2="X-AuroraDNS-Date: $timestamp" |
||||
|
export _H3="Authorization: $authorization" |
||||
|
|
||||
|
if [ "$m" != "GET" ]; then |
||||
|
_debug data "$data" |
||||
|
response="$(_post "$data" "$AURORA_Api/$ep" "" "$m")" |
||||
|
else |
||||
|
response="$(_get "$AURORA_Api/$ep")" |
||||
|
fi |
||||
|
|
||||
|
if [ "$?" != "0" ]; then |
||||
|
_err "error $ep" |
||||
|
return 1 |
||||
|
fi |
||||
|
_debug2 response "$response" |
||||
|
return 0 |
||||
|
} |
||||
@ -0,0 +1,204 @@ |
|||||
|
#!/usr/bin/env sh |
||||
|
|
||||
|
# |
||||
|
#AZION_Email="" |
||||
|
#AZION_Password="" |
||||
|
# |
||||
|
|
||||
|
AZION_Api="https://api.azionapi.net" |
||||
|
|
||||
|
######## Public functions ######## |
||||
|
|
||||
|
# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" |
||||
|
# Used to add txt record |
||||
|
dns_azion_add() { |
||||
|
fulldomain=$1 |
||||
|
txtvalue=$2 |
||||
|
|
||||
|
_debug "Detect the root zone" |
||||
|
if ! _get_root "$fulldomain"; then |
||||
|
_err "Domain not found" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_debug _sub_domain "$_sub_domain" |
||||
|
_debug _domain "$_domain" |
||||
|
_debug _domain_id "$_domain_id" |
||||
|
|
||||
|
_info "Add or update record" |
||||
|
_get_record "$_domain_id" "$_sub_domain" |
||||
|
if [ "$record_id" ]; then |
||||
|
_payload="{\"record_type\": \"TXT\", \"entry\": \"$_sub_domain\", \"answers_list\": [$answers_list, \"$txtvalue\"], \"ttl\": 20}" |
||||
|
if _azion_rest PUT "intelligent_dns/$_domain_id/records/$record_id" "$_payload"; then |
||||
|
if _contains "$response" "$txtvalue"; then |
||||
|
_info "Record updated." |
||||
|
return 0 |
||||
|
fi |
||||
|
fi |
||||
|
else |
||||
|
_payload="{\"record_type\": \"TXT\", \"entry\": \"$_sub_domain\", \"answers_list\": [\"$txtvalue\"], \"ttl\": 20}" |
||||
|
if _azion_rest POST "intelligent_dns/$_domain_id/records" "$_payload"; then |
||||
|
if _contains "$response" "$txtvalue"; then |
||||
|
_info "Record added." |
||||
|
return 0 |
||||
|
fi |
||||
|
fi |
||||
|
fi |
||||
|
_err "Failed to add or update record." |
||||
|
return 1 |
||||
|
} |
||||
|
|
||||
|
# Usage: fulldomain txtvalue |
||||
|
# Used to remove the txt record after validation |
||||
|
dns_azion_rm() { |
||||
|
fulldomain=$1 |
||||
|
txtvalue=$2 |
||||
|
|
||||
|
_debug "Detect the root zone" |
||||
|
if ! _get_root "$fulldomain"; then |
||||
|
_err "Domain not found" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_debug _sub_domain "$_sub_domain" |
||||
|
_debug _domain "$_domain" |
||||
|
_debug _domain_id "$_domain_id" |
||||
|
|
||||
|
_info "Removing record" |
||||
|
_get_record "$_domain_id" "$_sub_domain" |
||||
|
if [ "$record_id" ]; then |
||||
|
if _azion_rest DELETE "intelligent_dns/$_domain_id/records/$record_id"; then |
||||
|
_info "Record removed." |
||||
|
return 0 |
||||
|
else |
||||
|
_err "Failed to remove record." |
||||
|
return 1 |
||||
|
fi |
||||
|
else |
||||
|
_info "Record not found or already removed." |
||||
|
return 0 |
||||
|
fi |
||||
|
} |
||||
|
|
||||
|
#################### Private functions below ################################## |
||||
|
# Usage: _acme-challenge.www.domain.com |
||||
|
# returns |
||||
|
# _sub_domain=_acme-challenge.www |
||||
|
# _domain=domain.com |
||||
|
# _domain_id=sdjkglgdfewsdfg |
||||
|
_get_root() { |
||||
|
domain=$1 |
||||
|
i=1 |
||||
|
p=1 |
||||
|
|
||||
|
if ! _azion_rest GET "intelligent_dns"; then |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
while true; do |
||||
|
h=$(printf "%s" "$domain" | cut -d . -f $i-100) |
||||
|
_debug h "$h" |
||||
|
if [ -z "$h" ]; then |
||||
|
# not valid |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
if _contains "$response" "\"domain\":\"$h\""; then |
||||
|
_domain_id=$(echo "$response" | tr '{' "\n" | grep "\"domain\":\"$h\"" | _egrep_o "\"id\":[0-9]*" | _head_n 1 | cut -d : -f 2 | tr -d \") |
||||
|
_debug _domain_id "$_domain_id" |
||||
|
if [ "$_domain_id" ]; then |
||||
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) |
||||
|
_domain=$h |
||||
|
return 0 |
||||
|
fi |
||||
|
return 1 |
||||
|
fi |
||||
|
p=$i |
||||
|
i=$(_math "$i" + 1) |
||||
|
done |
||||
|
return 1 |
||||
|
} |
||||
|
|
||||
|
_get_record() { |
||||
|
_domain_id=$1 |
||||
|
_record=$2 |
||||
|
|
||||
|
if ! _azion_rest GET "intelligent_dns/$_domain_id/records"; then |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
if _contains "$response" "\"entry\":\"$_record\""; then |
||||
|
_json_record=$(echo "$response" | tr '{' "\n" | grep "\"entry\":\"$_record\"") |
||||
|
if [ "$_json_record" ]; then |
||||
|
record_id=$(echo "$_json_record" | _egrep_o "\"record_id\":[0-9]*" | _head_n 1 | cut -d : -f 2 | tr -d \") |
||||
|
answers_list=$(echo "$_json_record" | _egrep_o "\"answers_list\":\[.*\]" | _head_n 1 | cut -d : -f 2 | tr -d \[\]) |
||||
|
return 0 |
||||
|
fi |
||||
|
return 1 |
||||
|
fi |
||||
|
return 1 |
||||
|
} |
||||
|
|
||||
|
_get_token() { |
||||
|
AZION_Email="${AZION_Email:-$(_readaccountconf_mutable AZION_Email)}" |
||||
|
AZION_Password="${AZION_Password:-$(_readaccountconf_mutable AZION_Password)}" |
||||
|
|
||||
|
if ! _contains "$AZION_Email" "@"; then |
||||
|
_err "It seems that the AZION_Email is not a valid email address. Revalidate your environments." |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
if [ -z "$AZION_Email" ] || [ -z "$AZION_Password" ]; then |
||||
|
_err "You didn't specified a AZION_Email/AZION_Password to generate Azion token." |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_saveaccountconf_mutable AZION_Email "$AZION_Email" |
||||
|
_saveaccountconf_mutable AZION_Password "$AZION_Password" |
||||
|
|
||||
|
_basic_auth=$(printf "%s:%s" "$AZION_Email" "$AZION_Password" | _base64) |
||||
|
_debug _basic_auth "$_basic_auth" |
||||
|
|
||||
|
export _H1="Accept: application/json; version=3" |
||||
|
export _H2="Content-Type: application/json" |
||||
|
export _H3="Authorization: Basic $_basic_auth" |
||||
|
|
||||
|
response="$(_post "" "$AZION_Api/tokens" "" "POST")" |
||||
|
if _contains "$response" "\"token\":\"" >/dev/null; then |
||||
|
_azion_token=$(echo "$response" | _egrep_o "\"token\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \") |
||||
|
export AZION_Token="$_azion_token" |
||||
|
else |
||||
|
_err "Failed to generate Azion token" |
||||
|
return 1 |
||||
|
fi |
||||
|
} |
||||
|
|
||||
|
_azion_rest() { |
||||
|
_method=$1 |
||||
|
_uri="$2" |
||||
|
_data="$3" |
||||
|
|
||||
|
if [ -z "$AZION_Token" ]; then |
||||
|
_get_token |
||||
|
fi |
||||
|
_debug2 token "$AZION_Token" |
||||
|
|
||||
|
export _H1="Accept: application/json; version=3" |
||||
|
export _H2="Content-Type: application/json" |
||||
|
export _H3="Authorization: token $AZION_Token" |
||||
|
|
||||
|
if [ "$_method" != "GET" ]; then |
||||
|
_debug _data "$_data" |
||||
|
response="$(_post "$_data" "$AZION_Api/$_uri" "" "$_method")" |
||||
|
else |
||||
|
response="$(_get "$AZION_Api/$_uri")" |
||||
|
fi |
||||
|
|
||||
|
_debug2 response "$response" |
||||
|
|
||||
|
if [ "$?" != "0" ]; then |
||||
|
_err "error $_method $_uri $_data" |
||||
|
return 1 |
||||
|
fi |
||||
|
return 0 |
||||
|
} |
||||
@ -0,0 +1,159 @@ |
|||||
|
#!/usr/bin/env sh |
||||
|
# |
||||
|
#Author: Bjarne Saltbaek |
||||
|
#Report Bugs here: https://github.com/acmesh-official/acme.sh/issues/3732 |
||||
|
# |
||||
|
# |
||||
|
######## Public functions ##################### |
||||
|
# |
||||
|
# Export CPANEL username,api token and hostname in the following variables |
||||
|
# |
||||
|
# cPanel_Username=username |
||||
|
# cPanel_Apitoken=apitoken |
||||
|
# cPanel_Hostname=hostname |
||||
|
# |
||||
|
# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" |
||||
|
# Used to add txt record |
||||
|
dns_cpanel_add() { |
||||
|
fulldomain=$1 |
||||
|
txtvalue=$2 |
||||
|
|
||||
|
_info "Adding TXT record to cPanel based system" |
||||
|
_debug fulldomain "$fulldomain" |
||||
|
_debug txtvalue "$txtvalue" |
||||
|
_debug cPanel_Username "$cPanel_Username" |
||||
|
_debug cPanel_Apitoken "$cPanel_Apitoken" |
||||
|
_debug cPanel_Hostname "$cPanel_Hostname" |
||||
|
|
||||
|
if ! _cpanel_login; then |
||||
|
_err "cPanel Login failed for user $cPanel_Username. Check $HTTP_HEADER file" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_debug "First detect the root zone" |
||||
|
if ! _get_root "$fulldomain"; then |
||||
|
_err "No matching root domain for $fulldomain found" |
||||
|
return 1 |
||||
|
fi |
||||
|
# adding entry |
||||
|
_info "Adding the entry" |
||||
|
stripped_fulldomain=$(echo "$fulldomain" | sed "s/.$_domain//") |
||||
|
_debug "Adding $stripped_fulldomain to $_domain zone" |
||||
|
_myget "json-api/cpanel?cpanel_jsonapi_apiversion=2&cpanel_jsonapi_module=ZoneEdit&cpanel_jsonapi_func=add_zone_record&domain=$_domain&name=$stripped_fulldomain&type=TXT&txtdata=$txtvalue&ttl=1" |
||||
|
if _successful_update; then return 0; fi |
||||
|
_err "Couldn't create entry!" |
||||
|
return 1 |
||||
|
} |
||||
|
|
||||
|
# Usage: fulldomain txtvalue |
||||
|
# Used to remove the txt record after validation |
||||
|
dns_cpanel_rm() { |
||||
|
fulldomain=$1 |
||||
|
txtvalue=$2 |
||||
|
|
||||
|
_info "Using cPanel based system" |
||||
|
_debug fulldomain "$fulldomain" |
||||
|
_debug txtvalue "$txtvalue" |
||||
|
|
||||
|
if ! _cpanel_login; then |
||||
|
_err "cPanel Login failed for user $cPanel_Username. Check $HTTP_HEADER file" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
if ! _get_root; then |
||||
|
_err "No matching root domain for $fulldomain found" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_findentry "$fulldomain" "$txtvalue" |
||||
|
if [ -z "$_id" ]; then |
||||
|
_info "Entry doesn't exist, nothing to delete" |
||||
|
return 0 |
||||
|
fi |
||||
|
_debug "Deleting record..." |
||||
|
_myget "json-api/cpanel?cpanel_jsonapi_apiversion=2&cpanel_jsonapi_module=ZoneEdit&cpanel_jsonapi_func=remove_zone_record&domain=$_domain&line=$_id" |
||||
|
# removing entry |
||||
|
_debug "_result is: $_result" |
||||
|
|
||||
|
if _successful_update; then return 0; fi |
||||
|
_err "Couldn't delete entry!" |
||||
|
return 1 |
||||
|
} |
||||
|
|
||||
|
#################### Private functions below ################################## |
||||
|
|
||||
|
_checkcredentials() { |
||||
|
cPanel_Username="${cPanel_Username:-$(_readaccountconf_mutable cPanel_Username)}" |
||||
|
cPanel_Apitoken="${cPanel_Apitoken:-$(_readaccountconf_mutable cPanel_Apitoken)}" |
||||
|
cPanel_Hostname="${cPanel_Hostname:-$(_readaccountconf_mutable cPanel_Hostname)}" |
||||
|
|
||||
|
if [ -z "$cPanel_Username" ] || [ -z "$cPanel_Apitoken" ] || [ -z "$cPanel_Hostname" ]; then |
||||
|
cPanel_Username="" |
||||
|
cPanel_Apitoken="" |
||||
|
cPanel_Hostname="" |
||||
|
_err "You haven't specified cPanel username, apitoken and hostname yet." |
||||
|
_err "Please add credentials and try again." |
||||
|
return 1 |
||||
|
fi |
||||
|
#save the credentials to the account conf file. |
||||
|
_saveaccountconf_mutable cPanel_Username "$cPanel_Username" |
||||
|
_saveaccountconf_mutable cPanel_Apitoken "$cPanel_Apitoken" |
||||
|
_saveaccountconf_mutable cPanel_Hostname "$cPanel_Hostname" |
||||
|
return 0 |
||||
|
} |
||||
|
|
||||
|
_cpanel_login() { |
||||
|
if ! _checkcredentials; then return 1; fi |
||||
|
|
||||
|
if ! _myget "json-api/cpanel?cpanel_jsonapi_apiversion=2&cpanel_jsonapi_module=CustInfo&cpanel_jsonapi_func=displaycontactinfo"; then |
||||
|
_err "cPanel login failed for user $cPanel_Username." |
||||
|
return 1 |
||||
|
fi |
||||
|
return 0 |
||||
|
} |
||||
|
|
||||
|
_myget() { |
||||
|
#Adds auth header to request |
||||
|
export _H1="Authorization: cpanel $cPanel_Username:$cPanel_Apitoken" |
||||
|
_result=$(_get "$cPanel_Hostname/$1") |
||||
|
} |
||||
|
|
||||
|
_get_root() { |
||||
|
_myget 'json-api/cpanel?cpanel_jsonapi_apiversion=2&cpanel_jsonapi_module=ZoneEdit&cpanel_jsonapi_func=fetchzones' |
||||
|
_domains=$(echo "$_result" | sed 's/.*\(zones.*\[\).*/\1/' | cut -d':' -f2 | sed 's/"//g' | sed 's/{//g') |
||||
|
_debug "_result is: $_result" |
||||
|
_debug "_domains is: $_domains" |
||||
|
if [ -z "$_domains" ]; then |
||||
|
_err "Primary domain list not found!" |
||||
|
return 1 |
||||
|
fi |
||||
|
for _domain in $_domains; do |
||||
|
_debug "Checking if $fulldomain ends with $_domain" |
||||
|
if (_endswith "$fulldomain" "$_domain"); then |
||||
|
_debug "Root domain: $_domain" |
||||
|
return 0 |
||||
|
fi |
||||
|
done |
||||
|
return 1 |
||||
|
} |
||||
|
|
||||
|
_successful_update() { |
||||
|
if (echo "$_result" | grep -q 'newserial'); then return 0; fi |
||||
|
return 1 |
||||
|
} |
||||
|
|
||||
|
_findentry() { |
||||
|
_debug "In _findentry" |
||||
|
#returns id of dns entry, if it exists |
||||
|
_myget "json-api/cpanel?cpanel_jsonapi_apiversion=2&cpanel_jsonapi_module=ZoneEdit&cpanel_jsonapi_func=fetchzone_records&domain=$_domain" |
||||
|
_id=$(echo "$_result" | sed "s/.*\(line.*$fulldomain.*$txtvalue\).*/\1/" | cut -d ':' -f 2 | cut -d ',' -f 1) |
||||
|
_debug "_result is: $_result" |
||||
|
_debug "fulldomain. is $fulldomain." |
||||
|
_debug "txtvalue is $txtvalue" |
||||
|
_debug "_id is: $_id" |
||||
|
if [ -n "$_id" ]; then |
||||
|
_debug "Entry found with _id=$_id" |
||||
|
return 0 |
||||
|
fi |
||||
|
return 1 |
||||
|
} |
||||
@ -0,0 +1,159 @@ |
|||||
|
#!/usr/bin/env sh |
||||
|
|
||||
|
#Script to use with curanet.dk, scannet.dk, wannafind.dk, dandomain.dk DNS management. |
||||
|
#Requires api credentials with scope: dns |
||||
|
#Author: Peter L. Hansen <peter@r12.dk> |
||||
|
#Version 1.0 |
||||
|
|
||||
|
CURANET_REST_URL="https://api.curanet.dk/dns/v1/Domains" |
||||
|
CURANET_AUTH_URL="https://apiauth.dk.team.blue/auth/realms/Curanet/protocol/openid-connect/token" |
||||
|
CURANET_ACCESS_TOKEN="" |
||||
|
|
||||
|
######## Public functions ##################### |
||||
|
|
||||
|
#Usage: dns_curanet_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" |
||||
|
dns_curanet_add() { |
||||
|
fulldomain=$1 |
||||
|
txtvalue=$2 |
||||
|
_info "Using curanet" |
||||
|
_debug fulldomain "$fulldomain" |
||||
|
_debug txtvalue "$txtvalue" |
||||
|
|
||||
|
CURANET_AUTHCLIENTID="${CURANET_AUTHCLIENTID:-$(_readaccountconf_mutable CURANET_AUTHCLIENTID)}" |
||||
|
CURANET_AUTHSECRET="${CURANET_AUTHSECRET:-$(_readaccountconf_mutable CURANET_AUTHSECRET)}" |
||||
|
if [ -z "$CURANET_AUTHCLIENTID" ] || [ -z "$CURANET_AUTHSECRET" ]; then |
||||
|
CURANET_AUTHCLIENTID="" |
||||
|
CURANET_AUTHSECRET="" |
||||
|
_err "You don't specify curanet api client and secret." |
||||
|
_err "Please create your auth info and try again." |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
#save the credentials to the account conf file. |
||||
|
_saveaccountconf_mutable CURANET_AUTHCLIENTID "$CURANET_AUTHCLIENTID" |
||||
|
_saveaccountconf_mutable CURANET_AUTHSECRET "$CURANET_AUTHSECRET" |
||||
|
|
||||
|
if ! _get_token; then |
||||
|
_err "Unable to get token" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
if ! _get_root "$fulldomain"; then |
||||
|
_err "Invalid domain" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
export _H1="Content-Type: application/json-patch+json" |
||||
|
export _H2="Accept: application/json" |
||||
|
export _H3="Authorization: Bearer $CURANET_ACCESS_TOKEN" |
||||
|
data="{\"name\": \"$fulldomain\",\"type\": \"TXT\",\"ttl\": 60,\"priority\": 0,\"data\": \"$txtvalue\"}" |
||||
|
response="$(_post "$data" "$CURANET_REST_URL/${_domain}/Records" "" "")" |
||||
|
|
||||
|
if _contains "$response" "$txtvalue"; then |
||||
|
_debug "TXT record added OK" |
||||
|
else |
||||
|
_err "Unable to add TXT record" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
return 0 |
||||
|
} |
||||
|
|
||||
|
#Usage: fulldomain txtvalue |
||||
|
#Remove the txt record after validation. |
||||
|
dns_curanet_rm() { |
||||
|
fulldomain=$1 |
||||
|
txtvalue=$2 |
||||
|
_info "Using curanet" |
||||
|
_debug fulldomain "$fulldomain" |
||||
|
_debug txtvalue "$txtvalue" |
||||
|
|
||||
|
CURANET_AUTHCLIENTID="${CURANET_AUTHCLIENTID:-$(_readaccountconf_mutable CURANET_AUTHCLIENTID)}" |
||||
|
CURANET_AUTHSECRET="${CURANET_AUTHSECRET:-$(_readaccountconf_mutable CURANET_AUTHSECRET)}" |
||||
|
|
||||
|
if ! _get_token; then |
||||
|
_err "Unable to get token" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
if ! _get_root "$fulldomain"; then |
||||
|
_err "Invalid domain" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_debug "Getting current record list to identify TXT to delete" |
||||
|
|
||||
|
export _H1="Content-Type: application/json" |
||||
|
export _H2="Accept: application/json" |
||||
|
export _H3="Authorization: Bearer $CURANET_ACCESS_TOKEN" |
||||
|
|
||||
|
response="$(_get "$CURANET_REST_URL/${_domain}/Records" "" "")" |
||||
|
|
||||
|
if ! _contains "$response" "$txtvalue"; then |
||||
|
_err "Unable to delete record (does not contain $txtvalue )" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
recordid=$(echo "$response" | _egrep_o "{\"id\":[0-9]+,\"name\":\"$fulldomain\",\"type\":\"TXT\",\"ttl\":60,\"priority\":0,\"data\":\"..$txtvalue" | _egrep_o "id\":[0-9]+" | cut -c 5-) |
||||
|
|
||||
|
if [ -z "$recordid" ]; then |
||||
|
_err "Unable to get recordid" |
||||
|
_debug "regex {\"id\":[0-9]+,\"name\":\"$fulldomain\",\"type\":\"TXT\",\"ttl\":60,\"priority\":0,\"data\":\"..$txtvalue" |
||||
|
_debug "response $response" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_debug "Deleting recordID $recordid" |
||||
|
response="$(_post "" "$CURANET_REST_URL/${_domain}/Records/$recordid" "" "DELETE")" |
||||
|
return 0 |
||||
|
} |
||||
|
|
||||
|
#################### Private functions below ################################## |
||||
|
|
||||
|
_get_token() { |
||||
|
response="$(_post "grant_type=client_credentials&client_id=$CURANET_AUTHCLIENTID&client_secret=$CURANET_AUTHSECRET&scope=dns" "$CURANET_AUTH_URL" "" "")" |
||||
|
if ! _contains "$response" "access_token"; then |
||||
|
_err "Unable get access token" |
||||
|
return 1 |
||||
|
fi |
||||
|
CURANET_ACCESS_TOKEN=$(echo "$response" | _egrep_o "\"access_token\":\"[^\"]+" | cut -c 17-) |
||||
|
|
||||
|
if [ -z "$CURANET_ACCESS_TOKEN" ]; then |
||||
|
_err "Unable to get token" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
return 0 |
||||
|
|
||||
|
} |
||||
|
|
||||
|
#_acme-challenge.www.domain.com |
||||
|
#returns |
||||
|
# _domain=domain.com |
||||
|
# _domain_id=sdjkglgdfewsdfg |
||||
|
_get_root() { |
||||
|
domain=$1 |
||||
|
i=1 |
||||
|
|
||||
|
while true; do |
||||
|
h=$(printf "%s" "$domain" | cut -d . -f $i-100) |
||||
|
_debug h "$h" |
||||
|
if [ -z "$h" ]; then |
||||
|
#not valid |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
export _H1="Content-Type: application/json" |
||||
|
export _H2="Accept: application/json" |
||||
|
export _H3="Authorization: Bearer $CURANET_ACCESS_TOKEN" |
||||
|
response="$(_get "$CURANET_REST_URL/$h/Records" "" "")" |
||||
|
|
||||
|
if [ ! "$(echo "$response" | _egrep_o "Entity not found")" ]; then |
||||
|
_domain=$h |
||||
|
return 0 |
||||
|
fi |
||||
|
|
||||
|
i=$(_math "$i" + 1) |
||||
|
done |
||||
|
return 1 |
||||
|
} |
||||
@ -0,0 +1,87 @@ |
|||||
|
#!/usr/bin/env sh |
||||
|
|
||||
|
# dnsHome.de API for acme.sh |
||||
|
# |
||||
|
# This Script adds the necessary TXT record to a Subdomain |
||||
|
# |
||||
|
# Author dnsHome.de (https://github.com/dnsHome-de) |
||||
|
# |
||||
|
# Report Bugs to https://github.com/acmesh-official/acme.sh/issues/3819 |
||||
|
# |
||||
|
# export DNSHOME_Subdomain="" |
||||
|
# export DNSHOME_SubdomainPassword="" |
||||
|
|
||||
|
# Usage: add subdomain.ddnsdomain.tld "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" |
||||
|
# Used to add txt record |
||||
|
dns_dnshome_add() { |
||||
|
txtvalue=$2 |
||||
|
|
||||
|
DNSHOME_Subdomain="${DNSHOME_Subdomain:-$(_readdomainconf DNSHOME_Subdomain)}" |
||||
|
DNSHOME_SubdomainPassword="${DNSHOME_SubdomainPassword:-$(_readdomainconf DNSHOME_SubdomainPassword)}" |
||||
|
|
||||
|
if [ -z "$DNSHOME_Subdomain" ] || [ -z "$DNSHOME_SubdomainPassword" ]; then |
||||
|
DNSHOME_Subdomain="" |
||||
|
DNSHOME_SubdomainPassword="" |
||||
|
_err "Please specify/export your dnsHome.de Subdomain and Password" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
#save the credentials to the account conf file. |
||||
|
_savedomainconf DNSHOME_Subdomain "$DNSHOME_Subdomain" |
||||
|
_savedomainconf DNSHOME_SubdomainPassword "$DNSHOME_SubdomainPassword" |
||||
|
|
||||
|
DNSHOME_Api="https://$DNSHOME_Subdomain:$DNSHOME_SubdomainPassword@www.dnshome.de/dyndns.php" |
||||
|
|
||||
|
_DNSHOME_rest POST "acme=add&txt=$txtvalue" |
||||
|
if ! echo "$response" | grep 'successfully' >/dev/null; then |
||||
|
_err "Error" |
||||
|
_err "$response" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
return 0 |
||||
|
} |
||||
|
|
||||
|
# Usage: txtvalue |
||||
|
# Used to remove the txt record after validation |
||||
|
dns_dnshome_rm() { |
||||
|
txtvalue=$2 |
||||
|
|
||||
|
DNSHOME_Subdomain="${DNSHOME_Subdomain:-$(_readdomainconf DNSHOME_Subdomain)}" |
||||
|
DNSHOME_SubdomainPassword="${DNSHOME_SubdomainPassword:-$(_readdomainconf DNSHOME_SubdomainPassword)}" |
||||
|
|
||||
|
DNSHOME_Api="https://$DNSHOME_Subdomain:$DNSHOME_SubdomainPassword@www.dnshome.de/dyndns.php" |
||||
|
|
||||
|
if [ -z "$DNSHOME_Subdomain" ] || [ -z "$DNSHOME_SubdomainPassword" ]; then |
||||
|
DNSHOME_Subdomain="" |
||||
|
DNSHOME_SubdomainPassword="" |
||||
|
_err "Please specify/export your dnsHome.de Subdomain and Password" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_DNSHOME_rest POST "acme=rm&txt=$txtvalue" |
||||
|
if ! echo "$response" | grep 'successfully' >/dev/null; then |
||||
|
_err "Error" |
||||
|
_err "$response" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
return 0 |
||||
|
} |
||||
|
|
||||
|
#################### Private functions below ################################## |
||||
|
_DNSHOME_rest() { |
||||
|
method=$1 |
||||
|
data="$2" |
||||
|
_debug "$data" |
||||
|
|
||||
|
_debug data "$data" |
||||
|
response="$(_post "$data" "$DNSHOME_Api" "" "$method")" |
||||
|
|
||||
|
if [ "$?" != "0" ]; then |
||||
|
_err "error $data" |
||||
|
return 1 |
||||
|
fi |
||||
|
_debug2 response "$response" |
||||
|
return 0 |
||||
|
} |
||||
@ -0,0 +1,466 @@ |
|||||
|
#!/usr/bin/env sh |
||||
|
|
||||
|
# Akamai Edge DNS v2 API |
||||
|
# User must provide Open Edgegrid API credentials to the EdgeDNS installation. The remote user in EdgeDNS must have CRUD access to |
||||
|
# Edge DNS Zones and Recordsets, e.g. DNS—Zone Record Management authorization |
||||
|
|
||||
|
# Report bugs to https://control.akamai.com/apps/support-ui/#/contact-support |
||||
|
|
||||
|
# Values to export: |
||||
|
# --EITHER-- |
||||
|
# *** TBD. NOT IMPLEMENTED YET *** |
||||
|
# specify Edgegrid credentials file and section |
||||
|
# AKAMAI_EDGERC=<full file path> |
||||
|
# AKAMAI_EDGERC_SECTION="default" |
||||
|
## --OR-- |
||||
|
# specify indiviual credentials |
||||
|
# export AKAMAI_HOST = <host> |
||||
|
# export AKAMAI_ACCESS_TOKEN = <access token> |
||||
|
# export AKAMAI_CLIENT_TOKEN = <client token> |
||||
|
# export AKAMAI_CLIENT_SECRET = <client secret> |
||||
|
|
||||
|
ACME_EDGEDNS_VERSION="0.1.0" |
||||
|
|
||||
|
######## Public functions ##################### |
||||
|
|
||||
|
# Usage: dns_edgedns_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" |
||||
|
# Used to add txt record |
||||
|
# |
||||
|
dns_edgedns_add() { |
||||
|
fulldomain=$1 |
||||
|
txtvalue=$2 |
||||
|
_debug "ENTERING DNS_EDGEDNS_ADD" |
||||
|
_debug2 "fulldomain" "$fulldomain" |
||||
|
_debug2 "txtvalue" "$txtvalue" |
||||
|
|
||||
|
if ! _EDGEDNS_credentials; then |
||||
|
_err "$@" |
||||
|
return 1 |
||||
|
fi |
||||
|
if ! _EDGEDNS_getZoneInfo "$fulldomain"; then |
||||
|
_err "Invalid domain" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_debug2 "Add: zone" "$zone" |
||||
|
acmeRecordURI=$(printf "%s/%s/names/%s/types/TXT" "$edge_endpoint" "$zone" "$fulldomain") |
||||
|
_debug3 "Add URL" "$acmeRecordURI" |
||||
|
# Get existing TXT record |
||||
|
_edge_result=$(_edgedns_rest GET "$acmeRecordURI") |
||||
|
_api_status="$?" |
||||
|
_debug3 "_edge_result" "$_edge_result" |
||||
|
if [ "$_api_status" -ne 0 ]; then |
||||
|
if [ "$curResult" = "FATAL" ]; then |
||||
|
_err "$(printf "Fatal error: acme API function call : %s" "$retVal")" |
||||
|
fi |
||||
|
if [ "$_edge_result" != "404" ]; then |
||||
|
_err "$(printf "Failure accessing Akamai Edge DNS API Server. Error: %s" "$_edge_result")" |
||||
|
return 1 |
||||
|
fi |
||||
|
fi |
||||
|
rdata="\"${txtvalue}\"" |
||||
|
record_op="POST" |
||||
|
if [ "$_api_status" -eq 0 ]; then |
||||
|
# record already exists. Get existing record data and update |
||||
|
record_op="PUT" |
||||
|
rdlist="${_edge_result#*\"rdata\":[}" |
||||
|
rdlist="${rdlist%%]*}" |
||||
|
rdlist=$(echo "$rdlist" | tr -d '"' | tr -d "\\\\") |
||||
|
_debug3 "existing TXT found" |
||||
|
_debug3 "record data" "$rdlist" |
||||
|
# value already there? |
||||
|
if _contains "$rdlist" "$txtvalue"; then |
||||
|
return 0 |
||||
|
fi |
||||
|
_txt_val="" |
||||
|
while [ "$_txt_val" != "$rdlist" ] && [ "${rdlist}" ]; do |
||||
|
_txt_val="${rdlist%%,*}" |
||||
|
rdlist="${rdlist#*,}" |
||||
|
rdata="${rdata},\"${_txt_val}\"" |
||||
|
done |
||||
|
fi |
||||
|
# Add the txtvalue TXT Record |
||||
|
body="{\"name\":\"$fulldomain\",\"type\":\"TXT\",\"ttl\":600, \"rdata\":"[${rdata}]"}" |
||||
|
_debug3 "Add body '${body}'" |
||||
|
_edge_result=$(_edgedns_rest "$record_op" "$acmeRecordURI" "$body") |
||||
|
_api_status="$?" |
||||
|
if [ "$_api_status" -eq 0 ]; then |
||||
|
_log "$(printf "Text value %s added to recordset %s" "$txtvalue" "$fulldomain")" |
||||
|
return 0 |
||||
|
else |
||||
|
_err "$(printf "error adding TXT record for validation. Error: %s" "$_edge_result")" |
||||
|
return 1 |
||||
|
fi |
||||
|
} |
||||
|
|
||||
|
# Usage: dns_edgedns_rm _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" |
||||
|
# Used to delete txt record |
||||
|
# |
||||
|
dns_edgedns_rm() { |
||||
|
fulldomain=$1 |
||||
|
txtvalue=$2 |
||||
|
_debug "ENTERING DNS_EDGEDNS_RM" |
||||
|
_debug2 "fulldomain" "$fulldomain" |
||||
|
_debug2 "txtvalue" "$txtvalue" |
||||
|
|
||||
|
if ! _EDGEDNS_credentials; then |
||||
|
_err "$@" |
||||
|
return 1 |
||||
|
fi |
||||
|
if ! _EDGEDNS_getZoneInfo "$fulldomain"; then |
||||
|
_err "Invalid domain" |
||||
|
return 1 |
||||
|
fi |
||||
|
_debug2 "RM: zone" "${zone}" |
||||
|
acmeRecordURI=$(printf "%s/%s/names/%s/types/TXT" "${edge_endpoint}" "$zone" "$fulldomain") |
||||
|
_debug3 "RM URL" "$acmeRecordURI" |
||||
|
# Get existing TXT record |
||||
|
_edge_result=$(_edgedns_rest GET "$acmeRecordURI") |
||||
|
_api_status="$?" |
||||
|
if [ "$_api_status" -ne 0 ]; then |
||||
|
if [ "$curResult" = "FATAL" ]; then |
||||
|
_err "$(printf "Fatal error: acme API function call : %s" "$retVal")" |
||||
|
fi |
||||
|
if [ "$_edge_result" != "404" ]; then |
||||
|
_err "$(printf "Failure accessing Akamai Edge DNS API Server. Error: %s" "$_edge_result")" |
||||
|
return 1 |
||||
|
fi |
||||
|
fi |
||||
|
_debug3 "_edge_result" "$_edge_result" |
||||
|
record_op="DELETE" |
||||
|
body="" |
||||
|
if [ "$_api_status" -eq 0 ]; then |
||||
|
# record already exists. Get existing record data and update |
||||
|
rdlist="${_edge_result#*\"rdata\":[}" |
||||
|
rdlist="${rdlist%%]*}" |
||||
|
rdlist=$(echo "$rdlist" | tr -d '"' | tr -d "\\\\") |
||||
|
_debug3 "rdlist" "$rdlist" |
||||
|
if [ -n "$rdlist" ]; then |
||||
|
record_op="PUT" |
||||
|
comma="" |
||||
|
rdata="" |
||||
|
_txt_val="" |
||||
|
while [ "$_txt_val" != "$rdlist" ] && [ "$rdlist" ]; do |
||||
|
_txt_val="${rdlist%%,*}" |
||||
|
rdlist="${rdlist#*,}" |
||||
|
_debug3 "_txt_val" "$_txt_val" |
||||
|
_debug3 "txtvalue" "$txtvalue" |
||||
|
if ! _contains "$_txt_val" "$txtvalue"; then |
||||
|
rdata="${rdata}${comma}\"${_txt_val}\"" |
||||
|
comma="," |
||||
|
fi |
||||
|
done |
||||
|
if [ -z "$rdata" ]; then |
||||
|
record_op="DELETE" |
||||
|
else |
||||
|
# Recreate the txtvalue TXT Record |
||||
|
body="{\"name\":\"$fulldomain\",\"type\":\"TXT\",\"ttl\":600, \"rdata\":"[${rdata}]"}" |
||||
|
_debug3 "body" "$body" |
||||
|
fi |
||||
|
fi |
||||
|
fi |
||||
|
_edge_result=$(_edgedns_rest "$record_op" "$acmeRecordURI" "$body") |
||||
|
_api_status="$?" |
||||
|
if [ "$_api_status" -eq 0 ]; then |
||||
|
_log "$(printf "Text value %s removed from recordset %s" "$txtvalue" "$fulldomain")" |
||||
|
return 0 |
||||
|
else |
||||
|
_err "$(printf "error removing TXT record for validation. Error: %s" "$_edge_result")" |
||||
|
return 1 |
||||
|
fi |
||||
|
} |
||||
|
|
||||
|
#################### Private functions below ################################## |
||||
|
|
||||
|
_EDGEDNS_credentials() { |
||||
|
_debug "GettingEdge DNS credentials" |
||||
|
_log "$(printf "ACME DNSAPI Edge DNS version %s" ${ACME_EDGEDNS_VERSION})" |
||||
|
args_missing=0 |
||||
|
if [ -z "$AKAMAI_ACCESS_TOKEN" ]; then |
||||
|
AKAMAI_ACCESS_TOKEN="" |
||||
|
AKAMAI_CLIENT_TOKEN="" |
||||
|
AKAMAI_HOST="" |
||||
|
AKAMAI_CLIENT_SECRET="" |
||||
|
_err "AKAMAI_ACCESS_TOKEN is missing" |
||||
|
args_missing=1 |
||||
|
fi |
||||
|
if [ -z "$AKAMAI_CLIENT_TOKEN" ]; then |
||||
|
AKAMAI_ACCESS_TOKEN="" |
||||
|
AKAMAI_CLIENT_TOKEN="" |
||||
|
AKAMAI_HOST="" |
||||
|
AKAMAI_CLIENT_SECRET="" |
||||
|
_err "AKAMAI_CLIENT_TOKEN is missing" |
||||
|
args_missing=1 |
||||
|
fi |
||||
|
if [ -z "$AKAMAI_HOST" ]; then |
||||
|
AKAMAI_ACCESS_TOKEN="" |
||||
|
AKAMAI_CLIENT_TOKEN="" |
||||
|
AKAMAI_HOST="" |
||||
|
AKAMAI_CLIENT_SECRET="" |
||||
|
_err "AKAMAI_HOST is missing" |
||||
|
args_missing=1 |
||||
|
fi |
||||
|
if [ -z "$AKAMAI_CLIENT_SECRET" ]; then |
||||
|
AKAMAI_ACCESS_TOKEN="" |
||||
|
AKAMAI_CLIENT_TOKEN="" |
||||
|
AKAMAI_HOST="" |
||||
|
AKAMAI_CLIENT_SECRET="" |
||||
|
_err "AKAMAI_CLIENT_SECRET is missing" |
||||
|
args_missing=1 |
||||
|
fi |
||||
|
|
||||
|
if [ "$args_missing" = 1 ]; then |
||||
|
_err "You have not properly specified the EdgeDNS Open Edgegrid API credentials. Please try again." |
||||
|
return 1 |
||||
|
else |
||||
|
_saveaccountconf_mutable AKAMAI_ACCESS_TOKEN "$AKAMAI_ACCESS_TOKEN" |
||||
|
_saveaccountconf_mutable AKAMAI_CLIENT_TOKEN "$AKAMAI_CLIENT_TOKEN" |
||||
|
_saveaccountconf_mutable AKAMAI_HOST "$AKAMAI_HOST" |
||||
|
_saveaccountconf_mutable AKAMAI_CLIENT_SECRET "$AKAMAI_CLIENT_SECRET" |
||||
|
# Set whether curl should use secure or insecure mode |
||||
|
fi |
||||
|
export HTTPS_INSECURE=0 # All Edgegrid API calls are secure |
||||
|
edge_endpoint=$(printf "https://%s/config-dns/v2/zones" "$AKAMAI_HOST") |
||||
|
_debug3 "Edge API Endpoint:" "$edge_endpoint" |
||||
|
|
||||
|
} |
||||
|
|
||||
|
_EDGEDNS_getZoneInfo() { |
||||
|
_debug "Getting Zoneinfo" |
||||
|
zoneEnd=false |
||||
|
curZone=$1 |
||||
|
while [ -n "$zoneEnd" ]; do |
||||
|
# we can strip the first part of the fulldomain, since its just the _acme-challenge string |
||||
|
curZone="${curZone#*.}" |
||||
|
# suffix . needed for zone -> domain.tld. |
||||
|
# create zone get url |
||||
|
get_zone_url=$(printf "%s/%s" "$edge_endpoint" "$curZone") |
||||
|
_debug3 "Zone Get: " "${get_zone_url}" |
||||
|
curResult=$(_edgedns_rest GET "$get_zone_url") |
||||
|
retVal=$? |
||||
|
if [ "$retVal" -ne 0 ]; then |
||||
|
if [ "$curResult" = "FATAL" ]; then |
||||
|
_err "$(printf "Fatal error: acme API function call : %s" "$retVal")" |
||||
|
fi |
||||
|
if [ "$curResult" != "404" ]; then |
||||
|
_err "$(printf "Managed zone validation failed. Error response: %s" "$retVal")" |
||||
|
return 1 |
||||
|
fi |
||||
|
fi |
||||
|
if _contains "$curResult" "\"zone\":"; then |
||||
|
_debug2 "Zone data" "${curResult}" |
||||
|
zone=$(echo "${curResult}" | _egrep_o "\"zone\"\\s*:\\s*\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d "\"") |
||||
|
_debug3 "Zone" "${zone}" |
||||
|
zoneEnd="" |
||||
|
return 0 |
||||
|
fi |
||||
|
|
||||
|
if [ "${curZone#*.}" != "$curZone" ]; then |
||||
|
_debug3 "$(printf "%s still contains a '.' - so we can check next higher level" "$curZone")" |
||||
|
else |
||||
|
zoneEnd=true |
||||
|
_err "Couldn't retrieve zone data." |
||||
|
return 1 |
||||
|
fi |
||||
|
done |
||||
|
_err "Failed to retrieve zone data." |
||||
|
return 2 |
||||
|
} |
||||
|
|
||||
|
_edgedns_headers="" |
||||
|
|
||||
|
_edgedns_rest() { |
||||
|
_debug "Handling API Request" |
||||
|
m=$1 |
||||
|
# Assume endpoint is complete path, including query args if applicable |
||||
|
ep=$2 |
||||
|
body_data=$3 |
||||
|
_edgedns_content_type="" |
||||
|
_request_url_path="$ep" |
||||
|
_request_body="$body_data" |
||||
|
_request_method="$m" |
||||
|
_edgedns_headers="" |
||||
|
tab="" |
||||
|
_edgedns_headers="${_edgedns_headers}${tab}Host: ${AKAMAI_HOST}" |
||||
|
tab="\t" |
||||
|
# Set in acme.sh _post/_get |
||||
|
#_edgedns_headers="${_edgedns_headers}${tab}User-Agent:ACME DNSAPI Edge DNS version ${ACME_EDGEDNS_VERSION}" |
||||
|
_edgedns_headers="${_edgedns_headers}${tab}Accept: application/json,*/*" |
||||
|
if [ "$m" != "GET" ] && [ "$m" != "DELETE" ]; then |
||||
|
_edgedns_content_type="application/json" |
||||
|
_debug3 "_request_body" "$_request_body" |
||||
|
_body_len=$(echo "$_request_body" | tr -d "\n\r" | awk '{print length}') |
||||
|
_edgedns_headers="${_edgedns_headers}${tab}Content-Length: ${_body_len}" |
||||
|
fi |
||||
|
_edgedns_make_auth_header |
||||
|
_edgedns_headers="${_edgedns_headers}${tab}Authorization: ${_signed_auth_header}" |
||||
|
_secure_debug2 "Made Auth Header" "$_signed_auth_header" |
||||
|
hdr_indx=1 |
||||
|
work_header="${_edgedns_headers}${tab}" |
||||
|
_debug3 "work_header" "$work_header" |
||||
|
while [ "$work_header" ]; do |
||||
|
entry="${work_header%%\\t*}" |
||||
|
work_header="${work_header#*\\t}" |
||||
|
export "$(printf "_H%s=%s" "$hdr_indx" "$entry")" |
||||
|
_debug2 "Request Header " "$entry" |
||||
|
hdr_indx=$((hdr_indx + 1)) |
||||
|
done |
||||
|
|
||||
|
# clear headers from previous request to avoid getting wrong http code on timeouts |
||||
|
: >"$HTTP_HEADER" |
||||
|
_debug2 "$ep" |
||||
|
if [ "$m" != "GET" ]; then |
||||
|
_debug3 "Method data" "$data" |
||||
|
# body url [needbase64] [POST|PUT|DELETE] [ContentType] |
||||
|
response=$(_post "$_request_body" "$ep" false "$m" "$_edgedns_content_type") |
||||
|
else |
||||
|
response=$(_get "$ep") |
||||
|
fi |
||||
|
_ret="$?" |
||||
|
if [ "$_ret" -ne 0 ]; then |
||||
|
_err "$(printf "acme.sh API function call failed. Error: %s" "$_ret")" |
||||
|
echo "FATAL" |
||||
|
return "$_ret" |
||||
|
fi |
||||
|
_debug2 "response" "${response}" |
||||
|
_code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")" |
||||
|
_debug2 "http response code" "$_code" |
||||
|
if [ "$_code" = "200" ] || [ "$_code" = "201" ]; then |
||||
|
# All good |
||||
|
response="$(echo "${response}" | _normalizeJson)" |
||||
|
echo "$response" |
||||
|
return 0 |
||||
|
fi |
||||
|
|
||||
|
if [ "$_code" = "204" ]; then |
||||
|
# Success, no body |
||||
|
echo "$_code" |
||||
|
return 0 |
||||
|
fi |
||||
|
|
||||
|
if [ "$_code" = "400" ]; then |
||||
|
_err "Bad request presented" |
||||
|
_log "$(printf "Headers: %s" "$_edgedns_headers")" |
||||
|
_log "$(printf "Method: %s" "$_request_method")" |
||||
|
_log "$(printf "URL: %s" "$ep")" |
||||
|
_log "$(printf "Data: %s" "$data")" |
||||
|
fi |
||||
|
|
||||
|
if [ "$_code" = "403" ]; then |
||||
|
_err "access denied make sure your Edgegrid cedentials are correct." |
||||
|
fi |
||||
|
|
||||
|
echo "$_code" |
||||
|
return 1 |
||||
|
} |
||||
|
|
||||
|
_edgedns_eg_timestamp() { |
||||
|
_debug "Generating signature Timestamp" |
||||
|
_debug3 "Retriving ntp time" |
||||
|
_timeheaders="$(_get "https://www.ntp.org" "onlyheader")" |
||||
|
_debug3 "_timeheaders" "$_timeheaders" |
||||
|
_ntpdate="$(echo "$_timeheaders" | grep -i "Date:" | _head_n 1 | cut -d ':' -f 2- | tr -d "\r\n")" |
||||
|
_debug3 "_ntpdate" "$_ntpdate" |
||||
|
_ntpdate="$(echo "${_ntpdate}" | sed -e 's/^[[:space:]]*//')" |
||||
|
_debug3 "_NTPDATE" "$_ntpdate" |
||||
|
_ntptime="$(echo "${_ntpdate}" | _head_n 1 | cut -d " " -f 5 | tr -d "\r\n")" |
||||
|
_debug3 "_ntptime" "$_ntptime" |
||||
|
_eg_timestamp=$(date -u "+%Y%m%dT") |
||||
|
_eg_timestamp="$(printf "%s%s+0000" "$_eg_timestamp" "$_ntptime")" |
||||
|
_debug "_eg_timestamp" "$_eg_timestamp" |
||||
|
} |
||||
|
|
||||
|
_edgedns_new_nonce() { |
||||
|
_debug "Generating Nonce" |
||||
|
_nonce=$(echo "EDGEDNS$(_time)" | _digest sha1 hex | cut -c 1-32) |
||||
|
_debug3 "_nonce" "$_nonce" |
||||
|
} |
||||
|
|
||||
|
_edgedns_make_auth_header() { |
||||
|
_debug "Constructing Auth Header" |
||||
|
_edgedns_new_nonce |
||||
|
_edgedns_eg_timestamp |
||||
|
# "Unsigned authorization header: 'EG1-HMAC-SHA256 client_token=block;access_token=block;timestamp=20200806T14:16:33+0000;nonce=72cde72c-82d9-4721-9854-2ba057929d67;'" |
||||
|
_auth_header="$(printf "EG1-HMAC-SHA256 client_token=%s;access_token=%s;timestamp=%s;nonce=%s;" "$AKAMAI_CLIENT_TOKEN" "$AKAMAI_ACCESS_TOKEN" "$_eg_timestamp" "$_nonce")" |
||||
|
_secure_debug2 "Unsigned Auth Header: " "$_auth_header" |
||||
|
|
||||
|
_edgedns_sign_request |
||||
|
_signed_auth_header="$(printf "%ssignature=%s" "$_auth_header" "$_signed_req")" |
||||
|
_secure_debug2 "Signed Auth Header: " "${_signed_auth_header}" |
||||
|
} |
||||
|
|
||||
|
_edgedns_sign_request() { |
||||
|
_debug2 "Signing http request" |
||||
|
_edgedns_make_data_to_sign "$_auth_header" |
||||
|
_secure_debug2 "Returned signed data" "$_mdata" |
||||
|
_edgedns_make_signing_key "$_eg_timestamp" |
||||
|
_edgedns_base64_hmac_sha256 "$_mdata" "$_signing_key" |
||||
|
_signed_req="$_hmac_out" |
||||
|
_secure_debug2 "Signed Request" "$_signed_req" |
||||
|
} |
||||
|
|
||||
|
_edgedns_make_signing_key() { |
||||
|
_debug2 "Creating sigining key" |
||||
|
ts=$1 |
||||
|
_edgedns_base64_hmac_sha256 "$ts" "$AKAMAI_CLIENT_SECRET" |
||||
|
_signing_key="$_hmac_out" |
||||
|
_secure_debug2 "Signing Key" "$_signing_key" |
||||
|
|
||||
|
} |
||||
|
|
||||
|
_edgedns_make_data_to_sign() { |
||||
|
_debug2 "Processing data to sign" |
||||
|
hdr=$1 |
||||
|
_secure_debug2 "hdr" "$hdr" |
||||
|
_edgedns_make_content_hash |
||||
|
path="$(echo "$_request_url_path" | tr -d "\n\r" | sed 's/https\?:\/\///')" |
||||
|
path="${path#*$AKAMAI_HOST}" |
||||
|
_debug "hier path" "$path" |
||||
|
# dont expose headers to sign so use MT string |
||||
|
_mdata="$(printf "%s\thttps\t%s\t%s\t%s\t%s\t%s" "$_request_method" "$AKAMAI_HOST" "$path" "" "$_hash" "$hdr")" |
||||
|
_secure_debug2 "Data to Sign" "$_mdata" |
||||
|
} |
||||
|
|
||||
|
_edgedns_make_content_hash() { |
||||
|
_debug2 "Generating content hash" |
||||
|
_hash="" |
||||
|
_debug2 "Request method" "${_request_method}" |
||||
|
if [ "$_request_method" != "POST" ] || [ -z "$_request_body" ]; then |
||||
|
return 0 |
||||
|
fi |
||||
|
_debug2 "Req body" "$_request_body" |
||||
|
_edgedns_base64_sha256 "$_request_body" |
||||
|
_hash="$_sha256_out" |
||||
|
_debug2 "Content hash" "$_hash" |
||||
|
} |
||||
|
|
||||
|
_edgedns_base64_hmac_sha256() { |
||||
|
_debug2 "Generating hmac" |
||||
|
data=$1 |
||||
|
key=$2 |
||||
|
encoded_data="$(echo "$data" | iconv -t utf-8)" |
||||
|
encoded_key="$(echo "$key" | iconv -t utf-8)" |
||||
|
_secure_debug2 "encoded data" "$encoded_data" |
||||
|
_secure_debug2 "encoded key" "$encoded_key" |
||||
|
|
||||
|
encoded_key_hex=$(printf "%s" "$encoded_key" | _hex_dump | tr -d ' ') |
||||
|
data_sig="$(echo "$encoded_data" | tr -d "\n\r" | _hmac sha256 "$encoded_key_hex" | _base64)" |
||||
|
|
||||
|
_secure_debug2 "data_sig:" "$data_sig" |
||||
|
_hmac_out="$(echo "$data_sig" | tr -d "\n\r" | iconv -f utf-8)" |
||||
|
_secure_debug2 "hmac" "$_hmac_out" |
||||
|
} |
||||
|
|
||||
|
_edgedns_base64_sha256() { |
||||
|
_debug2 "Creating sha256 digest" |
||||
|
trg=$1 |
||||
|
_secure_debug2 "digest data" "$trg" |
||||
|
digest="$(echo "$trg" | tr -d "\n\r" | _digest "sha256")" |
||||
|
_sha256_out="$(echo "$digest" | tr -d "\n\r" | iconv -f utf-8)" |
||||
|
_secure_debug2 "digest decode" "$_sha256_out" |
||||
|
} |
||||
|
|
||||
|
#_edgedns_parse_edgerc() { |
||||
|
# filepath=$1 |
||||
|
# section=$2 |
||||
|
#} |
||||
@ -0,0 +1,221 @@ |
|||||
|
#!/usr/bin/env sh |
||||
|
|
||||
|
######################################################################## |
||||
|
# Geoscaling hook script for acme.sh |
||||
|
# |
||||
|
# Environment variables: |
||||
|
# |
||||
|
# - $GEOSCALING_Username (your Geoscaling username - this is usually NOT an amail address) |
||||
|
# - $GEOSCALING_Password (your Geoscaling password) |
||||
|
|
||||
|
#-- dns_geoscaling_add() - Add TXT record -------------------------------------- |
||||
|
# Usage: dns_geoscaling_add _acme-challenge.subdomain.domain.com "XyZ123..." |
||||
|
|
||||
|
dns_geoscaling_add() { |
||||
|
full_domain=$1 |
||||
|
txt_value=$2 |
||||
|
_info "Using DNS-01 Geoscaling DNS2 hook" |
||||
|
|
||||
|
GEOSCALING_Username="${GEOSCALING_Username:-$(_readaccountconf_mutable GEOSCALING_Username)}" |
||||
|
GEOSCALING_Password="${GEOSCALING_Password:-$(_readaccountconf_mutable GEOSCALING_Password)}" |
||||
|
if [ -z "$GEOSCALING_Username" ] || [ -z "$GEOSCALING_Password" ]; then |
||||
|
GEOSCALING_Username= |
||||
|
GEOSCALING_Password= |
||||
|
_err "No auth details provided. Please set user credentials using the \$GEOSCALING_Username and \$GEOSCALING_Password environment variables." |
||||
|
return 1 |
||||
|
fi |
||||
|
_saveaccountconf_mutable GEOSCALING_Username "${GEOSCALING_Username}" |
||||
|
_saveaccountconf_mutable GEOSCALING_Password "${GEOSCALING_Password}" |
||||
|
|
||||
|
# Fills in the $zone_id and $zone_name |
||||
|
find_zone "${full_domain}" || return 1 |
||||
|
_debug "Zone id '${zone_id}' will be used." |
||||
|
|
||||
|
# We're logged in here |
||||
|
|
||||
|
# we should add ${full_domain} minus the trailing ${zone_name} |
||||
|
|
||||
|
prefix=$(echo "${full_domain}" | sed "s|\\.${zone_name}\$||") |
||||
|
|
||||
|
body="id=${zone_id}&name=${prefix}&type=TXT&content=${txt_value}&ttl=300&prio=0" |
||||
|
|
||||
|
do_post "$body" "https://www.geoscaling.com/dns2/ajax/add_record.php" |
||||
|
exit_code="$?" |
||||
|
if [ "${exit_code}" -eq 0 ]; then |
||||
|
_info "TXT record added successfully." |
||||
|
else |
||||
|
_err "Couldn't add the TXT record." |
||||
|
fi |
||||
|
do_logout |
||||
|
return "${exit_code}" |
||||
|
} |
||||
|
|
||||
|
#-- dns_geoscaling_rm() - Remove TXT record ------------------------------------ |
||||
|
# Usage: dns_geoscaling_rm _acme-challenge.subdomain.domain.com "XyZ123..." |
||||
|
|
||||
|
dns_geoscaling_rm() { |
||||
|
full_domain=$1 |
||||
|
txt_value=$2 |
||||
|
_info "Cleaning up after DNS-01 Geoscaling DNS2 hook" |
||||
|
|
||||
|
# fills in the $zone_id |
||||
|
find_zone "${full_domain}" || return 1 |
||||
|
_debug "Zone id '${zone_id}' will be used." |
||||
|
|
||||
|
# Here we're logged in |
||||
|
# Find the record id to clean |
||||
|
|
||||
|
# get the domain |
||||
|
response=$(do_get "https://www.geoscaling.com/dns2/index.php?module=domain&id=${zone_id}") |
||||
|
_debug2 "response" "$response" |
||||
|
|
||||
|
table="$(echo "${response}" | tr -d '\n' | sed 's|.*<div class="box"><div class="boxtitle">Basic Records</div><div class="boxtext"><table|<table|; s|</table>.*|</table>|')" |
||||
|
_debug2 table "${table}" |
||||
|
names=$(echo "${table}" | _egrep_o 'id="[0-9]+\.name">[^<]*</td>' | sed 's|</td>||; s|.*>||') |
||||
|
ids=$(echo "${table}" | _egrep_o 'id="[0-9]+\.name">[^<]*</td>' | sed 's|\.name">.*||; s|id="||') |
||||
|
types=$(echo "${table}" | _egrep_o 'id="[0-9]+\.type">[^<]*</td>' | sed 's|</td>||; s|.*>||') |
||||
|
values=$(echo "${table}" | _egrep_o 'id="[0-9]+\.content">[^<]*</td>' | sed 's|</td>||; s|.*>||') |
||||
|
|
||||
|
_debug2 names "${names}" |
||||
|
_debug2 ids "${ids}" |
||||
|
_debug2 types "${types}" |
||||
|
_debug2 values "${values}" |
||||
|
|
||||
|
# look for line whose name is ${full_domain}, whose type is TXT, and whose value is ${txt_value} |
||||
|
line_num="$(echo "${values}" | grep -F -n -- "${txt_value}" | _head_n 1 | cut -d ':' -f 1)" |
||||
|
_debug2 line_num "${line_num}" |
||||
|
found_id= |
||||
|
if [ -n "$line_num" ]; then |
||||
|
type=$(echo "${types}" | sed -n "${line_num}p") |
||||
|
name=$(echo "${names}" | sed -n "${line_num}p") |
||||
|
id=$(echo "${ids}" | sed -n "${line_num}p") |
||||
|
|
||||
|
_debug2 type "$type" |
||||
|
_debug2 name "$name" |
||||
|
_debug2 id "$id" |
||||
|
_debug2 full_domain "$full_domain" |
||||
|
|
||||
|
if [ "${type}" = "TXT" ] && [ "${name}" = "${full_domain}" ]; then |
||||
|
found_id=${id} |
||||
|
fi |
||||
|
fi |
||||
|
|
||||
|
if [ "${found_id}" = "" ]; then |
||||
|
_err "Can not find record id." |
||||
|
return 0 |
||||
|
fi |
||||
|
|
||||
|
# Remove the record |
||||
|
body="id=${zone_id}&record_id=${found_id}" |
||||
|
response=$(do_post "$body" "https://www.geoscaling.com/dns2/ajax/delete_record.php") |
||||
|
exit_code="$?" |
||||
|
if [ "$exit_code" -eq 0 ]; then |
||||
|
_info "Record removed successfully." |
||||
|
else |
||||
|
_err "Could not clean (remove) up the record. Please go to Geoscaling administration interface and clean it by hand." |
||||
|
fi |
||||
|
do_logout |
||||
|
return "${exit_code}" |
||||
|
} |
||||
|
|
||||
|
########################## PRIVATE FUNCTIONS ########################### |
||||
|
|
||||
|
do_get() { |
||||
|
_url=$1 |
||||
|
export _H1="Cookie: $geoscaling_phpsessid_cookie" |
||||
|
_get "${_url}" |
||||
|
} |
||||
|
|
||||
|
do_post() { |
||||
|
_body=$1 |
||||
|
_url=$2 |
||||
|
export _H1="Cookie: $geoscaling_phpsessid_cookie" |
||||
|
_post "${_body}" "${_url}" |
||||
|
} |
||||
|
|
||||
|
do_login() { |
||||
|
|
||||
|
_info "Logging in..." |
||||
|
|
||||
|
username_encoded="$(printf "%s" "${GEOSCALING_Username}" | _url_encode)" |
||||
|
password_encoded="$(printf "%s" "${GEOSCALING_Password}" | _url_encode)" |
||||
|
body="username=${username_encoded}&password=${password_encoded}" |
||||
|
|
||||
|
response=$(_post "$body" "https://www.geoscaling.com/dns2/index.php?module=auth") |
||||
|
_debug2 response "${response}" |
||||
|
|
||||
|
#retcode=$(grep '^HTTP[^ ]*' "${HTTP_HEADER}" | _head_n 1 | _egrep_o '[0-9]+$') |
||||
|
retcode=$(grep '^HTTP[^ ]*' "${HTTP_HEADER}" | _head_n 1 | cut -d ' ' -f 2) |
||||
|
|
||||
|
if [ "$retcode" != "302" ]; then |
||||
|
_err "Geoscaling login failed for user ${GEOSCALING_Username}. Check ${HTTP_HEADER} file" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
geoscaling_phpsessid_cookie="$(grep -i '^set-cookie:' "${HTTP_HEADER}" | _egrep_o 'PHPSESSID=[^;]*;' | tr -d ';')" |
||||
|
return 0 |
||||
|
|
||||
|
} |
||||
|
|
||||
|
do_logout() { |
||||
|
_info "Logging out." |
||||
|
response="$(do_get "https://www.geoscaling.com/dns2/index.php?module=auth")" |
||||
|
_debug2 response "$response" |
||||
|
return 0 |
||||
|
} |
||||
|
|
||||
|
find_zone() { |
||||
|
domain="$1" |
||||
|
|
||||
|
# do login |
||||
|
do_login || return 1 |
||||
|
|
||||
|
# get zones |
||||
|
response="$(do_get "https://www.geoscaling.com/dns2/index.php?module=domains")" |
||||
|
|
||||
|
table="$(echo "${response}" | tr -d '\n' | sed 's|.*<div class="box"><div class="boxtitle">Your domains</div><div class="boxtext"><table|<table|; s|</table>.*|</table>|')" |
||||
|
_debug2 table "${table}" |
||||
|
zone_names="$(echo "${table}" | _egrep_o '<b>[^<]*</b>' | sed 's|<b>||;s|</b>||')" |
||||
|
_debug2 _matches "${zone_names}" |
||||
|
# Zone names and zone IDs are in same order |
||||
|
zone_ids=$(echo "${table}" | _egrep_o '<a href=.index\.php\?module=domain&id=[0-9]+. onclick="javascript:show_loader\(\);">' | sed 's|.*id=||;s|. .*||') |
||||
|
|
||||
|
_debug2 "These are the zones on this Geoscaling account:" |
||||
|
_debug2 "zone_names" "${zone_names}" |
||||
|
_debug2 "And these are their respective IDs:" |
||||
|
_debug2 "zone_ids" "${zone_ids}" |
||||
|
if [ -z "${zone_names}" ] || [ -z "${zone_ids}" ]; then |
||||
|
_err "Can not get zone names or IDs." |
||||
|
return 1 |
||||
|
fi |
||||
|
# Walk through all possible zone names |
||||
|
strip_counter=1 |
||||
|
while true; do |
||||
|
attempted_zone=$(echo "${domain}" | cut -d . -f ${strip_counter}-) |
||||
|
|
||||
|
# All possible zone names have been tried |
||||
|
if [ -z "${attempted_zone}" ]; then |
||||
|
_err "No zone for domain '${domain}' found." |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_debug "Looking for zone '${attempted_zone}'" |
||||
|
|
||||
|
line_num="$(echo "${zone_names}" | grep -n "^${attempted_zone}\$" | _head_n 1 | cut -d : -f 1)" |
||||
|
_debug2 line_num "${line_num}" |
||||
|
if [ "$line_num" ]; then |
||||
|
zone_id=$(echo "${zone_ids}" | sed -n "${line_num}p") |
||||
|
zone_name=$(echo "${zone_names}" | sed -n "${line_num}p") |
||||
|
if [ -z "${zone_id}" ]; then |
||||
|
_err "Can not find zone id." |
||||
|
return 1 |
||||
|
fi |
||||
|
_debug "Found relevant zone '${attempted_zone}' with id '${zone_id}' - will be used for domain '${domain}'." |
||||
|
return 0 |
||||
|
fi |
||||
|
|
||||
|
_debug "Zone '${attempted_zone}' doesn't exist, let's try a less specific zone." |
||||
|
strip_counter=$(_math "${strip_counter}" + 1) |
||||
|
done |
||||
|
} |
||||
|
# vim: et:ts=2:sw=2: |
||||
@ -0,0 +1,289 @@ |
|||||
|
#!/usr/bin/env sh |
||||
|
|
||||
|
# HUAWEICLOUD_Username |
||||
|
# HUAWEICLOUD_Password |
||||
|
# HUAWEICLOUD_ProjectID |
||||
|
|
||||
|
iam_api="https://iam.myhuaweicloud.com" |
||||
|
dns_api="https://dns.ap-southeast-1.myhuaweicloud.com" # Should work |
||||
|
|
||||
|
######## Public functions ##################### |
||||
|
|
||||
|
# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" |
||||
|
# Used to add txt record |
||||
|
# |
||||
|
# Ref: https://support.huaweicloud.com/intl/zh-cn/api-dns/zh-cn_topic_0132421999.html |
||||
|
# |
||||
|
|
||||
|
dns_huaweicloud_add() { |
||||
|
fulldomain=$1 |
||||
|
txtvalue=$2 |
||||
|
|
||||
|
HUAWEICLOUD_Username="${HUAWEICLOUD_Username:-$(_readaccountconf_mutable HUAWEICLOUD_Username)}" |
||||
|
HUAWEICLOUD_Password="${HUAWEICLOUD_Password:-$(_readaccountconf_mutable HUAWEICLOUD_Password)}" |
||||
|
HUAWEICLOUD_ProjectID="${HUAWEICLOUD_ProjectID:-$(_readaccountconf_mutable HUAWEICLOUD_ProjectID)}" |
||||
|
|
||||
|
# Check information |
||||
|
if [ -z "${HUAWEICLOUD_Username}" ] || [ -z "${HUAWEICLOUD_Password}" ] || [ -z "${HUAWEICLOUD_ProjectID}" ]; then |
||||
|
_err "Not enough information provided to dns_huaweicloud!" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
unset token # Clear token |
||||
|
token="$(_get_token "${HUAWEICLOUD_Username}" "${HUAWEICLOUD_Password}" "${HUAWEICLOUD_ProjectID}")" |
||||
|
if [ -z "${token}" ]; then # Check token |
||||
|
_err "dns_api(dns_huaweicloud): Error getting token." |
||||
|
return 1 |
||||
|
fi |
||||
|
_secure_debug "Access token is:" "${token}" |
||||
|
|
||||
|
unset zoneid |
||||
|
zoneid="$(_get_zoneid "${token}" "${fulldomain}")" |
||||
|
if [ -z "${zoneid}" ]; then |
||||
|
_err "dns_api(dns_huaweicloud): Error getting zone id." |
||||
|
return 1 |
||||
|
fi |
||||
|
_debug "Zone ID is:" "${zoneid}" |
||||
|
|
||||
|
_debug "Adding Record" |
||||
|
_add_record "${token}" "${fulldomain}" "${txtvalue}" |
||||
|
ret="$?" |
||||
|
if [ "${ret}" != "0" ]; then |
||||
|
_err "dns_api(dns_huaweicloud): Error adding record." |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
# Do saving work if all succeeded |
||||
|
_saveaccountconf_mutable HUAWEICLOUD_Username "${HUAWEICLOUD_Username}" |
||||
|
_saveaccountconf_mutable HUAWEICLOUD_Password "${HUAWEICLOUD_Password}" |
||||
|
_saveaccountconf_mutable HUAWEICLOUD_ProjectID "${HUAWEICLOUD_ProjectID}" |
||||
|
return 0 |
||||
|
} |
||||
|
|
||||
|
# Usage: fulldomain txtvalue |
||||
|
# Used to remove the txt record after validation |
||||
|
# |
||||
|
# Ref: https://support.huaweicloud.com/intl/zh-cn/api-dns/dns_api_64005.html |
||||
|
# |
||||
|
|
||||
|
dns_huaweicloud_rm() { |
||||
|
fulldomain=$1 |
||||
|
txtvalue=$2 |
||||
|
|
||||
|
HUAWEICLOUD_Username="${HUAWEICLOUD_Username:-$(_readaccountconf_mutable HUAWEICLOUD_Username)}" |
||||
|
HUAWEICLOUD_Password="${HUAWEICLOUD_Password:-$(_readaccountconf_mutable HUAWEICLOUD_Password)}" |
||||
|
HUAWEICLOUD_ProjectID="${HUAWEICLOUD_ProjectID:-$(_readaccountconf_mutable HUAWEICLOUD_ProjectID)}" |
||||
|
|
||||
|
# Check information |
||||
|
if [ -z "${HUAWEICLOUD_Username}" ] || [ -z "${HUAWEICLOUD_Password}" ] || [ -z "${HUAWEICLOUD_ProjectID}" ]; then |
||||
|
_err "Not enough information provided to dns_huaweicloud!" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
unset token # Clear token |
||||
|
token="$(_get_token "${HUAWEICLOUD_Username}" "${HUAWEICLOUD_Password}" "${HUAWEICLOUD_ProjectID}")" |
||||
|
if [ -z "${token}" ]; then # Check token |
||||
|
_err "dns_api(dns_huaweicloud): Error getting token." |
||||
|
return 1 |
||||
|
fi |
||||
|
_secure_debug "Access token is:" "${token}" |
||||
|
|
||||
|
unset zoneid |
||||
|
zoneid="$(_get_zoneid "${token}" "${fulldomain}")" |
||||
|
if [ -z "${zoneid}" ]; then |
||||
|
_err "dns_api(dns_huaweicloud): Error getting zone id." |
||||
|
return 1 |
||||
|
fi |
||||
|
_debug "Zone ID is:" "${zoneid}" |
||||
|
|
||||
|
# Remove all records |
||||
|
# Therotically HuaweiCloud does not allow more than one record set |
||||
|
# But remove them recurringly to increase robusty |
||||
|
while [ "${record_id}" != "0" ]; do |
||||
|
_debug "Removing Record" |
||||
|
_rm_record "${token}" "${zoneid}" "${record_id}" |
||||
|
record_id="$(_get_recordset_id "${token}" "${fulldomain}" "${zoneid}")" |
||||
|
done |
||||
|
return 0 |
||||
|
} |
||||
|
|
||||
|
################### Private functions below ################################## |
||||
|
|
||||
|
# _get_zoneid |
||||
|
# |
||||
|
# _token=$1 |
||||
|
# _domain_string=$2 |
||||
|
# |
||||
|
# printf "%s" "${_zoneid}" |
||||
|
_get_zoneid() { |
||||
|
_token=$1 |
||||
|
_domain_string=$2 |
||||
|
export _H1="X-Auth-Token: ${_token}" |
||||
|
|
||||
|
i=1 |
||||
|
while true; do |
||||
|
h=$(printf "%s" "${_domain_string}" | cut -d . -f $i-100) |
||||
|
if [ -z "$h" ]; then |
||||
|
#not valid |
||||
|
return 1 |
||||
|
fi |
||||
|
_debug "$h" |
||||
|
response=$(_get "${dns_api}/v2/zones?name=${h}") |
||||
|
_debug2 "$response" |
||||
|
if _contains "${response}" '"id"'; then |
||||
|
zoneidlist=$(echo "${response}" | _egrep_o "\"id\": *\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | tr -d " ") |
||||
|
zonenamelist=$(echo "${response}" | _egrep_o "\"name\": *\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | tr -d " ") |
||||
|
_debug2 "Return Zone ID(s):" "${zoneidlist}" |
||||
|
_debug2 "Return Zone Name(s):" "${zonenamelist}" |
||||
|
zoneidnum=0 |
||||
|
zoneidcount=$(echo "${zoneidlist}" | grep -c '^') |
||||
|
_debug "Retund Zone ID(s) Count:" "${zoneidcount}" |
||||
|
while [ "${zoneidnum}" -lt "${zoneidcount}" ]; do |
||||
|
zoneidnum=$(_math "$zoneidnum" + 1) |
||||
|
_zoneid=$(echo "${zoneidlist}" | sed -n "${zoneidnum}p") |
||||
|
zonename=$(echo "${zonenamelist}" | sed -n "${zoneidnum}p") |
||||
|
_debug "Check Zone Name" "${zonename}" |
||||
|
if [ "${zonename}" = "${h}." ]; then |
||||
|
_debug "Get Zone ID Success." |
||||
|
_debug "ZoneID:" "${_zoneid}" |
||||
|
printf "%s" "${_zoneid}" |
||||
|
return 0 |
||||
|
fi |
||||
|
done |
||||
|
fi |
||||
|
i=$(_math "$i" + 1) |
||||
|
done |
||||
|
return 1 |
||||
|
} |
||||
|
|
||||
|
_get_recordset_id() { |
||||
|
_token=$1 |
||||
|
_domain=$2 |
||||
|
_zoneid=$3 |
||||
|
export _H1="X-Auth-Token: ${_token}" |
||||
|
|
||||
|
response=$(_get "${dns_api}/v2/zones/${_zoneid}/recordsets?name=${_domain}") |
||||
|
if _contains "${response}" '"id"'; then |
||||
|
_id="$(echo "${response}" | _egrep_o "\"id\": *\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | tr -d " ")" |
||||
|
printf "%s" "${_id}" |
||||
|
return 0 |
||||
|
fi |
||||
|
printf "%s" "0" |
||||
|
return 1 |
||||
|
} |
||||
|
|
||||
|
_add_record() { |
||||
|
_token=$1 |
||||
|
_domain=$2 |
||||
|
_txtvalue=$3 |
||||
|
|
||||
|
# Get Existing Records |
||||
|
export _H1="X-Auth-Token: ${_token}" |
||||
|
response=$(_get "${dns_api}/v2/zones/${zoneid}/recordsets?name=${_domain}") |
||||
|
|
||||
|
_debug2 "${response}" |
||||
|
_exist_record=$(echo "${response}" | _egrep_o '"records":[^]]*' | sed 's/\"records\"\:\[//g') |
||||
|
_debug "${_exist_record}" |
||||
|
|
||||
|
# Check if record exist |
||||
|
# Generate body data |
||||
|
if [ -z "${_exist_record}" ]; then |
||||
|
_post_body="{ |
||||
|
\"name\": \"${_domain}.\", |
||||
|
\"description\": \"ACME Challenge\", |
||||
|
\"type\": \"TXT\", |
||||
|
\"ttl\": 1, |
||||
|
\"records\": [ |
||||
|
\"\\\"${_txtvalue}\\\"\" |
||||
|
] |
||||
|
}" |
||||
|
else |
||||
|
_post_body="{ |
||||
|
\"name\": \"${_domain}.\", |
||||
|
\"description\": \"ACME Challenge\", |
||||
|
\"type\": \"TXT\", |
||||
|
\"ttl\": 1, |
||||
|
\"records\": [ |
||||
|
${_exist_record}, |
||||
|
\"\\\"${_txtvalue}\\\"\" |
||||
|
] |
||||
|
}" |
||||
|
fi |
||||
|
|
||||
|
_record_id="$(_get_recordset_id "${_token}" "${_domain}" "${zoneid}")" |
||||
|
_debug "Record Set ID is:" "${_record_id}" |
||||
|
|
||||
|
# Remove all records |
||||
|
while [ "${_record_id}" != "0" ]; do |
||||
|
_debug "Removing Record" |
||||
|
_rm_record "${_token}" "${zoneid}" "${_record_id}" |
||||
|
_record_id="$(_get_recordset_id "${_token}" "${_domain}" "${zoneid}")" |
||||
|
done |
||||
|
|
||||
|
# Add brand new records with all old and new records |
||||
|
export _H2="Content-Type: application/json" |
||||
|
export _H1="X-Auth-Token: ${_token}" |
||||
|
|
||||
|
_debug2 "${_post_body}" |
||||
|
_post "${_post_body}" "${dns_api}/v2/zones/${zoneid}/recordsets" >/dev/null |
||||
|
_code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")" |
||||
|
if [ "$_code" != "202" ]; then |
||||
|
_err "dns_huaweicloud: http code ${_code}" |
||||
|
return 1 |
||||
|
fi |
||||
|
return 0 |
||||
|
} |
||||
|
|
||||
|
# _rm_record $token $zoneid $recordid |
||||
|
# assume ${dns_api} exist |
||||
|
# no output |
||||
|
# return 0 |
||||
|
_rm_record() { |
||||
|
_token=$1 |
||||
|
_zone_id=$2 |
||||
|
_record_id=$3 |
||||
|
|
||||
|
export _H2="Content-Type: application/json" |
||||
|
export _H1="X-Auth-Token: ${_token}" |
||||
|
|
||||
|
_post "" "${dns_api}/v2/zones/${_zone_id}/recordsets/${_record_id}" false "DELETE" >/dev/null |
||||
|
return $? |
||||
|
} |
||||
|
|
||||
|
_get_token() { |
||||
|
_username=$1 |
||||
|
_password=$2 |
||||
|
_project=$3 |
||||
|
|
||||
|
_debug "Getting Token" |
||||
|
body="{ |
||||
|
\"auth\": { |
||||
|
\"identity\": { |
||||
|
\"methods\": [ |
||||
|
\"password\" |
||||
|
], |
||||
|
\"password\": { |
||||
|
\"user\": { |
||||
|
\"name\": \"${_username}\", |
||||
|
\"password\": \"${_password}\", |
||||
|
\"domain\": { |
||||
|
\"name\": \"${_username}\" |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
\"scope\": { |
||||
|
\"project\": { |
||||
|
\"id\": \"${_project}\" |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}" |
||||
|
export _H1="Content-Type: application/json;charset=utf8" |
||||
|
_post "${body}" "${iam_api}/v3/auth/tokens" >/dev/null |
||||
|
_code=$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n") |
||||
|
_token=$(grep "^X-Subject-Token" "$HTTP_HEADER" | cut -d " " -f 2-) |
||||
|
_secure_debug "${_code}" |
||||
|
printf "%s" "${_token}" |
||||
|
return 0 |
||||
|
} |
||||
@ -0,0 +1,199 @@ |
|||||
|
#!/usr/bin/env sh |
||||
|
|
||||
|
############################################################################### |
||||
|
# Infomaniak API integration |
||||
|
# |
||||
|
# To use this API you need visit the API dashboard of your account |
||||
|
# once logged into https://manager.infomaniak.com add /api/dashboard to the URL |
||||
|
# |
||||
|
# Please report bugs to |
||||
|
# https://github.com/acmesh-official/acme.sh/issues/3188 |
||||
|
# |
||||
|
# Note: the URL looks like this: |
||||
|
# https://manager.infomaniak.com/v3/<account_id>/api/dashboard |
||||
|
# Then generate a token with the scope Domain |
||||
|
# this is given as an environment variable INFOMANIAK_API_TOKEN |
||||
|
############################################################################### |
||||
|
|
||||
|
# base variables |
||||
|
|
||||
|
DEFAULT_INFOMANIAK_API_URL="https://api.infomaniak.com" |
||||
|
DEFAULT_INFOMANIAK_TTL=300 |
||||
|
|
||||
|
######## Public functions ##################### |
||||
|
|
||||
|
#Usage: dns_infomaniak_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" |
||||
|
dns_infomaniak_add() { |
||||
|
|
||||
|
INFOMANIAK_API_TOKEN="${INFOMANIAK_API_TOKEN:-$(_readaccountconf_mutable INFOMANIAK_API_TOKEN)}" |
||||
|
INFOMANIAK_API_URL="${INFOMANIAK_API_URL:-$(_readaccountconf_mutable INFOMANIAK_API_URL)}" |
||||
|
INFOMANIAK_TTL="${INFOMANIAK_TTL:-$(_readaccountconf_mutable INFOMANIAK_TTL)}" |
||||
|
|
||||
|
if [ -z "$INFOMANIAK_API_TOKEN" ]; then |
||||
|
INFOMANIAK_API_TOKEN="" |
||||
|
_err "Please provide a valid Infomaniak API token in variable INFOMANIAK_API_TOKEN" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
if [ -z "$INFOMANIAK_API_URL" ]; then |
||||
|
INFOMANIAK_API_URL="$DEFAULT_INFOMANIAK_API_URL" |
||||
|
fi |
||||
|
|
||||
|
if [ -z "$INFOMANIAK_TTL" ]; then |
||||
|
INFOMANIAK_TTL="$DEFAULT_INFOMANIAK_TTL" |
||||
|
fi |
||||
|
|
||||
|
#save the token to the account conf file. |
||||
|
_saveaccountconf_mutable INFOMANIAK_API_TOKEN "$INFOMANIAK_API_TOKEN" |
||||
|
|
||||
|
if [ "$INFOMANIAK_API_URL" != "$DEFAULT_INFOMANIAK_API_URL" ]; then |
||||
|
_saveaccountconf_mutable INFOMANIAK_API_URL "$INFOMANIAK_API_URL" |
||||
|
fi |
||||
|
|
||||
|
if [ "$INFOMANIAK_TTL" != "$DEFAULT_INFOMANIAK_TTL" ]; then |
||||
|
_saveaccountconf_mutable INFOMANIAK_TTL "$INFOMANIAK_TTL" |
||||
|
fi |
||||
|
|
||||
|
export _H1="Authorization: Bearer $INFOMANIAK_API_TOKEN" |
||||
|
export _H2="Content-Type: application/json" |
||||
|
|
||||
|
fulldomain="$1" |
||||
|
txtvalue="$2" |
||||
|
|
||||
|
_info "Infomaniak DNS API" |
||||
|
_debug fulldomain "$fulldomain" |
||||
|
_debug txtvalue "$txtvalue" |
||||
|
|
||||
|
fqdn=${fulldomain#_acme-challenge.} |
||||
|
|
||||
|
# guess which base domain to add record to |
||||
|
zone_and_id=$(_find_zone "$fqdn") |
||||
|
if [ -z "$zone_and_id" ]; then |
||||
|
_err "cannot find zone to modify" |
||||
|
return 1 |
||||
|
fi |
||||
|
zone=${zone_and_id% *} |
||||
|
domain_id=${zone_and_id#* } |
||||
|
|
||||
|
# extract first part of domain |
||||
|
key=${fulldomain%.$zone} |
||||
|
|
||||
|
_debug "zone:$zone id:$domain_id key:$key" |
||||
|
|
||||
|
# payload |
||||
|
data="{\"type\": \"TXT\", \"source\": \"$key\", \"target\": \"$txtvalue\", \"ttl\": $INFOMANIAK_TTL}" |
||||
|
|
||||
|
# API call |
||||
|
response=$(_post "$data" "${INFOMANIAK_API_URL}/1/domain/$domain_id/dns/record") |
||||
|
if [ -n "$response" ] && echo "$response" | _contains '"result":"success"'; then |
||||
|
_info "Record added" |
||||
|
_debug "Response: $response" |
||||
|
return 0 |
||||
|
fi |
||||
|
_err "could not create record" |
||||
|
_debug "Response: $response" |
||||
|
return 1 |
||||
|
} |
||||
|
|
||||
|
#Usage: fulldomain txtvalue |
||||
|
#Remove the txt record after validation. |
||||
|
dns_infomaniak_rm() { |
||||
|
|
||||
|
INFOMANIAK_API_TOKEN="${INFOMANIAK_API_TOKEN:-$(_readaccountconf_mutable INFOMANIAK_API_TOKEN)}" |
||||
|
INFOMANIAK_API_URL="${INFOMANIAK_API_URL:-$(_readaccountconf_mutable INFOMANIAK_API_URL)}" |
||||
|
INFOMANIAK_TTL="${INFOMANIAK_TTL:-$(_readaccountconf_mutable INFOMANIAK_TTL)}" |
||||
|
|
||||
|
if [ -z "$INFOMANIAK_API_TOKEN" ]; then |
||||
|
INFOMANIAK_API_TOKEN="" |
||||
|
_err "Please provide a valid Infomaniak API token in variable INFOMANIAK_API_TOKEN" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
if [ -z "$INFOMANIAK_API_URL" ]; then |
||||
|
INFOMANIAK_API_URL="$DEFAULT_INFOMANIAK_API_URL" |
||||
|
fi |
||||
|
|
||||
|
if [ -z "$INFOMANIAK_TTL" ]; then |
||||
|
INFOMANIAK_TTL="$DEFAULT_INFOMANIAK_TTL" |
||||
|
fi |
||||
|
|
||||
|
#save the token to the account conf file. |
||||
|
_saveaccountconf_mutable INFOMANIAK_API_TOKEN "$INFOMANIAK_API_TOKEN" |
||||
|
|
||||
|
if [ "$INFOMANIAK_API_URL" != "$DEFAULT_INFOMANIAK_API_URL" ]; then |
||||
|
_saveaccountconf_mutable INFOMANIAK_API_URL "$INFOMANIAK_API_URL" |
||||
|
fi |
||||
|
|
||||
|
if [ "$INFOMANIAK_TTL" != "$DEFAULT_INFOMANIAK_TTL" ]; then |
||||
|
_saveaccountconf_mutable INFOMANIAK_TTL "$INFOMANIAK_TTL" |
||||
|
fi |
||||
|
|
||||
|
export _H1="Authorization: Bearer $INFOMANIAK_API_TOKEN" |
||||
|
export _H2="ContentType: application/json" |
||||
|
|
||||
|
fulldomain=$1 |
||||
|
txtvalue=$2 |
||||
|
_info "Infomaniak DNS API" |
||||
|
_debug fulldomain "$fulldomain" |
||||
|
_debug txtvalue "$txtvalue" |
||||
|
|
||||
|
fqdn=${fulldomain#_acme-challenge.} |
||||
|
|
||||
|
# guess which base domain to add record to |
||||
|
zone_and_id=$(_find_zone "$fqdn") |
||||
|
if [ -z "$zone_and_id" ]; then |
||||
|
_err "cannot find zone to modify" |
||||
|
return 1 |
||||
|
fi |
||||
|
zone=${zone_and_id% *} |
||||
|
domain_id=${zone_and_id#* } |
||||
|
|
||||
|
# extract first part of domain |
||||
|
key=${fulldomain%.$zone} |
||||
|
|
||||
|
_debug "zone:$zone id:$domain_id key:$key" |
||||
|
|
||||
|
# find previous record |
||||
|
# shellcheck disable=SC1004 |
||||
|
record_id=$(_get "${INFOMANIAK_API_URL}/1/domain/$domain_id/dns/record" | sed 's/.*"data":\[\(.*\)\]}/\1/; s/},{/}\ |
||||
|
{/g' | sed -n 's/.*"id":"*\([0-9]*\)"*.*"source_idn":"'"$fulldomain"'".*"target_idn":"'"$txtvalue"'".*/\1/p') |
||||
|
if [ -z "$record_id" ]; then |
||||
|
_err "could not find record to delete" |
||||
|
return 1 |
||||
|
fi |
||||
|
_debug "record_id: $record_id" |
||||
|
|
||||
|
# API call |
||||
|
response=$(_post "" "${INFOMANIAK_API_URL}/1/domain/$domain_id/dns/record/$record_id" "" DELETE) |
||||
|
if [ -n "$response" ] && echo "$response" | _contains '"result":"success"'; then |
||||
|
_info "Record deleted" |
||||
|
return 0 |
||||
|
fi |
||||
|
_err "could not delete record" |
||||
|
return 1 |
||||
|
} |
||||
|
|
||||
|
#################### Private functions below ################################## |
||||
|
|
||||
|
_get_domain_id() { |
||||
|
domain="$1" |
||||
|
|
||||
|
# shellcheck disable=SC1004 |
||||
|
_get "${INFOMANIAK_API_URL}/1/product?service_name=domain&customer_name=$domain" | sed 's/.*"data":\[{\(.*\)}\]}/\1/; s/,/\ |
||||
|
/g' | sed -n 's/^"id":\(.*\)/\1/p' |
||||
|
} |
||||
|
|
||||
|
_find_zone() { |
||||
|
zone="$1" |
||||
|
|
||||
|
# find domain in list, removing . parts sequentialy |
||||
|
while _contains "$zone" '\.'; do |
||||
|
_debug "testing $zone" |
||||
|
id=$(_get_domain_id "$zone") |
||||
|
if [ -n "$id" ]; then |
||||
|
echo "$zone $id" |
||||
|
return |
||||
|
fi |
||||
|
zone=${zone#*.} |
||||
|
done |
||||
|
} |
||||
@ -0,0 +1,163 @@ |
|||||
|
#!/usr/bin/env sh |
||||
|
|
||||
|
# Supports IONOS DNS API Beta v1.0.0 |
||||
|
# |
||||
|
# Usage: |
||||
|
# Export IONOS_PREFIX and IONOS_SECRET before calling acme.sh: |
||||
|
# |
||||
|
# $ export IONOS_PREFIX="..." |
||||
|
# $ export IONOS_SECRET="..." |
||||
|
# |
||||
|
# $ acme.sh --issue --dns dns_ionos ... |
||||
|
|
||||
|
IONOS_API="https://api.hosting.ionos.com/dns" |
||||
|
IONOS_ROUTE_ZONES="/v1/zones" |
||||
|
|
||||
|
IONOS_TXT_TTL=60 # minimum accepted by API |
||||
|
IONOS_TXT_PRIO=10 |
||||
|
|
||||
|
dns_ionos_add() { |
||||
|
fulldomain=$1 |
||||
|
txtvalue=$2 |
||||
|
|
||||
|
if ! _ionos_init; then |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_body="[{\"name\":\"$_sub_domain.$_domain\",\"type\":\"TXT\",\"content\":\"$txtvalue\",\"ttl\":$IONOS_TXT_TTL,\"prio\":$IONOS_TXT_PRIO,\"disabled\":false}]" |
||||
|
|
||||
|
if _ionos_rest POST "$IONOS_ROUTE_ZONES/$_zone_id/records" "$_body" && [ -z "$response" ]; then |
||||
|
_info "TXT record has been created successfully." |
||||
|
return 0 |
||||
|
fi |
||||
|
|
||||
|
return 1 |
||||
|
} |
||||
|
|
||||
|
dns_ionos_rm() { |
||||
|
fulldomain=$1 |
||||
|
txtvalue=$2 |
||||
|
|
||||
|
if ! _ionos_init; then |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
if ! _ionos_get_record "$fulldomain" "$_zone_id" "$txtvalue"; then |
||||
|
_err "Could not find _acme-challenge TXT record." |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
if _ionos_rest DELETE "$IONOS_ROUTE_ZONES/$_zone_id/records/$_record_id" && [ -z "$response" ]; then |
||||
|
_info "TXT record has been deleted successfully." |
||||
|
return 0 |
||||
|
fi |
||||
|
|
||||
|
return 1 |
||||
|
} |
||||
|
|
||||
|
_ionos_init() { |
||||
|
IONOS_PREFIX="${IONOS_PREFIX:-$(_readaccountconf_mutable IONOS_PREFIX)}" |
||||
|
IONOS_SECRET="${IONOS_SECRET:-$(_readaccountconf_mutable IONOS_SECRET)}" |
||||
|
|
||||
|
if [ -z "$IONOS_PREFIX" ] || [ -z "$IONOS_SECRET" ]; then |
||||
|
_err "You didn't specify an IONOS api prefix and secret yet." |
||||
|
_err "Read https://beta.developer.hosting.ionos.de/docs/getstarted to learn how to get a prefix and secret." |
||||
|
_err "" |
||||
|
_err "Then set them before calling acme.sh:" |
||||
|
_err "\$ export IONOS_PREFIX=\"...\"" |
||||
|
_err "\$ export IONOS_SECRET=\"...\"" |
||||
|
_err "\$ acme.sh --issue -d ... --dns dns_ionos" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_saveaccountconf_mutable IONOS_PREFIX "$IONOS_PREFIX" |
||||
|
_saveaccountconf_mutable IONOS_SECRET "$IONOS_SECRET" |
||||
|
|
||||
|
if ! _get_root "$fulldomain"; then |
||||
|
_err "Cannot find this domain in your IONOS account." |
||||
|
return 1 |
||||
|
fi |
||||
|
} |
||||
|
|
||||
|
_get_root() { |
||||
|
domain=$1 |
||||
|
i=1 |
||||
|
p=1 |
||||
|
|
||||
|
if _ionos_rest GET "$IONOS_ROUTE_ZONES"; then |
||||
|
response="$(echo "$response" | tr -d "\n")" |
||||
|
|
||||
|
while true; do |
||||
|
h=$(printf "%s" "$domain" | cut -d . -f $i-100) |
||||
|
if [ -z "$h" ]; then |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_zone="$(echo "$response" | _egrep_o "\"name\":\"$h\".*\}")" |
||||
|
if [ "$_zone" ]; then |
||||
|
_zone_id=$(printf "%s\n" "$_zone" | _egrep_o "\"id\":\"[a-fA-F0-9\-]*\"" | _head_n 1 | cut -d : -f 2 | tr -d '\"') |
||||
|
if [ "$_zone_id" ]; then |
||||
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) |
||||
|
_domain=$h |
||||
|
|
||||
|
return 0 |
||||
|
fi |
||||
|
|
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
p=$i |
||||
|
i=$(_math "$i" + 1) |
||||
|
done |
||||
|
fi |
||||
|
|
||||
|
return 1 |
||||
|
} |
||||
|
|
||||
|
_ionos_get_record() { |
||||
|
fulldomain=$1 |
||||
|
zone_id=$2 |
||||
|
txtrecord=$3 |
||||
|
|
||||
|
if _ionos_rest GET "$IONOS_ROUTE_ZONES/$zone_id?recordName=$fulldomain&recordType=TXT"; then |
||||
|
response="$(echo "$response" | tr -d "\n")" |
||||
|
|
||||
|
_record="$(echo "$response" | _egrep_o "\"name\":\"$fulldomain\"[^\}]*\"type\":\"TXT\"[^\}]*\"content\":\"\\\\\"$txtrecord\\\\\"\".*\}")" |
||||
|
if [ "$_record" ]; then |
||||
|
_record_id=$(printf "%s\n" "$_record" | _egrep_o "\"id\":\"[a-fA-F0-9\-]*\"" | _head_n 1 | cut -d : -f 2 | tr -d '\"') |
||||
|
|
||||
|
return 0 |
||||
|
fi |
||||
|
fi |
||||
|
|
||||
|
return 1 |
||||
|
} |
||||
|
|
||||
|
_ionos_rest() { |
||||
|
method="$1" |
||||
|
route="$2" |
||||
|
data="$3" |
||||
|
|
||||
|
IONOS_API_KEY="$(printf "%s.%s" "$IONOS_PREFIX" "$IONOS_SECRET")" |
||||
|
|
||||
|
export _H1="X-API-Key: $IONOS_API_KEY" |
||||
|
|
||||
|
if [ "$method" != "GET" ]; then |
||||
|
export _H2="Accept: application/json" |
||||
|
export _H3="Content-Type: application/json" |
||||
|
|
||||
|
response="$(_post "$data" "$IONOS_API$route" "" "$method" "application/json")" |
||||
|
else |
||||
|
export _H2="Accept: */*" |
||||
|
export _H3= |
||||
|
response="$(_get "$IONOS_API$route")" |
||||
|
fi |
||||
|
|
||||
|
if [ "$?" != "0" ]; then |
||||
|
_err "Error $route: $response" |
||||
|
return 1 |
||||
|
fi |
||||
|
_debug2 "response" "$response" |
||||
|
|
||||
|
return 0 |
||||
|
} |
||||
@ -0,0 +1,150 @@ |
|||||
|
#!/usr/bin/env sh |
||||
|
|
||||
|
# kapper.net domain api |
||||
|
# for further questions please contact: support@kapper.net |
||||
|
# please report issues here: https://github.com/acmesh-official/acme.sh/issues/2977 |
||||
|
|
||||
|
#KAPPERNETDNS_Key="yourKAPPERNETapikey" |
||||
|
#KAPPERNETDNS_Secret="yourKAPPERNETapisecret" |
||||
|
|
||||
|
KAPPERNETDNS_Api="https://dnspanel.kapper.net/API/1.2?APIKey=$KAPPERNETDNS_Key&APISecret=$KAPPERNETDNS_Secret" |
||||
|
|
||||
|
############################################################################### |
||||
|
# called with |
||||
|
# fullhostname: something.example.com |
||||
|
# txtvalue: someacmegenerated string |
||||
|
dns_kappernet_add() { |
||||
|
fullhostname=$1 |
||||
|
txtvalue=$2 |
||||
|
|
||||
|
KAPPERNETDNS_Key="${KAPPERNETDNS_Key:-$(_readaccountconf_mutable KAPPERNETDNS_Key)}" |
||||
|
KAPPERNETDNS_Secret="${KAPPERNETDNS_Secret:-$(_readaccountconf_mutable KAPPERNETDNS_Secret)}" |
||||
|
|
||||
|
if [ -z "$KAPPERNETDNS_Key" ] || [ -z "$KAPPERNETDNS_Secret" ]; then |
||||
|
KAPPERNETDNS_Key="" |
||||
|
KAPPERNETDNS_Secret="" |
||||
|
_err "Please specify your kapper.net api key and secret." |
||||
|
_err "If you have not received yours - send your mail to" |
||||
|
_err "support@kapper.net to get your key and secret." |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
#store the api key and email to the account conf file. |
||||
|
_saveaccountconf_mutable KAPPERNETDNS_Key "$KAPPERNETDNS_Key" |
||||
|
_saveaccountconf_mutable KAPPERNETDNS_Secret "$KAPPERNETDNS_Secret" |
||||
|
_debug "Checking Domain ..." |
||||
|
if ! _get_root "$fullhostname"; then |
||||
|
_err "invalid domain" |
||||
|
return 1 |
||||
|
fi |
||||
|
_debug _sub_domain "SUBDOMAIN: $_sub_domain" |
||||
|
_debug _domain "DOMAIN: $_domain" |
||||
|
|
||||
|
_info "Trying to add TXT DNS Record" |
||||
|
data="%7B%22name%22%3A%22$fullhostname%22%2C%22type%22%3A%22TXT%22%2C%22content%22%3A%22$txtvalue%22%2C%22ttl%22%3A%223600%22%2C%22prio%22%3A%22%22%7D" |
||||
|
if _kappernet_api GET "action=new&subject=$_domain&data=$data"; then |
||||
|
|
||||
|
if _contains "$response" "{\"OK\":true"; then |
||||
|
_info "Waiting 120 seconds for DNS to spread the new record" |
||||
|
_sleep 120 |
||||
|
return 0 |
||||
|
else |
||||
|
_err "Error creating a TXT DNS Record: $fullhostname TXT $txtvalue" |
||||
|
_err "Error Message: $response" |
||||
|
return 1 |
||||
|
fi |
||||
|
fi |
||||
|
_err "Failed creating TXT Record" |
||||
|
} |
||||
|
|
||||
|
############################################################################### |
||||
|
# called with |
||||
|
# fullhostname: something.example.com |
||||
|
dns_kappernet_rm() { |
||||
|
fullhostname=$1 |
||||
|
txtvalue=$2 |
||||
|
|
||||
|
KAPPERNETDNS_Key="${KAPPERNETDNS_Key:-$(_readaccountconf_mutable KAPPERNETDNS_Key)}" |
||||
|
KAPPERNETDNS_Secret="${KAPPERNETDNS_Secret:-$(_readaccountconf_mutable KAPPERNETDNS_Secret)}" |
||||
|
|
||||
|
if [ -z "$KAPPERNETDNS_Key" ] || [ -z "$KAPPERNETDNS_Secret" ]; then |
||||
|
KAPPERNETDNS_Key="" |
||||
|
KAPPERNETDNS_Secret="" |
||||
|
_err "Please specify your kapper.net api key and secret." |
||||
|
_err "If you have not received yours - send your mail to" |
||||
|
_err "support@kapper.net to get your key and secret." |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
#store the api key and email to the account conf file. |
||||
|
_saveaccountconf_mutable KAPPERNETDNS_Key "$KAPPERNETDNS_Key" |
||||
|
_saveaccountconf_mutable KAPPERNETDNS_Secret "$KAPPERNETDNS_Secret" |
||||
|
|
||||
|
_info "Trying to remove the TXT Record: $fullhostname containing $txtvalue" |
||||
|
data="%7B%22name%22%3A%22$fullhostname%22%2C%22type%22%3A%22TXT%22%2C%22content%22%3A%22$txtvalue%22%2C%22ttl%22%3A%223600%22%2C%22prio%22%3A%22%22%7D" |
||||
|
if _kappernet_api GET "action=del&subject=$fullhostname&data=$data"; then |
||||
|
if _contains "$response" "{\"OK\":true"; then |
||||
|
return 0 |
||||
|
else |
||||
|
_err "Error deleting DNS Record: $fullhostname containing $txtvalue" |
||||
|
_err "Problem: $response" |
||||
|
return 1 |
||||
|
fi |
||||
|
fi |
||||
|
_err "Problem deleting TXT DNS record" |
||||
|
} |
||||
|
|
||||
|
#################### Private functions below ################################## |
||||
|
# called with hostname |
||||
|
# e.g._acme-challenge.www.domain.com returns |
||||
|
# _sub_domain=_acme-challenge.www |
||||
|
# _domain=domain.com |
||||
|
_get_root() { |
||||
|
domain=$1 |
||||
|
i=2 |
||||
|
p=1 |
||||
|
while true; do |
||||
|
h=$(printf "%s" "$domain" | cut -d . -f $i-100) |
||||
|
if [ -z "$h" ]; then |
||||
|
#not valid |
||||
|
return 1 |
||||
|
fi |
||||
|
if ! _kappernet_api GET "action=list&subject=$h"; then |
||||
|
return 1 |
||||
|
fi |
||||
|
if _contains "$response" '"OK":false'; then |
||||
|
_debug "$h not found" |
||||
|
else |
||||
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) |
||||
|
_domain="$h" |
||||
|
return 0 |
||||
|
fi |
||||
|
p="$i" |
||||
|
i=$(_math "$i" + 1) |
||||
|
done |
||||
|
return 1 |
||||
|
} |
||||
|
|
||||
|
################################################################################ |
||||
|
# calls the kapper.net DNS Panel API |
||||
|
# with |
||||
|
# method |
||||
|
# param |
||||
|
_kappernet_api() { |
||||
|
method=$1 |
||||
|
param="$2" |
||||
|
|
||||
|
_debug param "PARAMETER=$param" |
||||
|
url="$KAPPERNETDNS_Api&$param" |
||||
|
_debug url "URL=$url" |
||||
|
|
||||
|
if [ "$method" = "GET" ]; then |
||||
|
response="$(_get "$url")" |
||||
|
else |
||||
|
_err "Unsupported method" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_debug2 response "$response" |
||||
|
return 0 |
||||
|
} |
||||
@ -0,0 +1,261 @@ |
|||||
|
#!/usr/bin/env sh |
||||
|
# Mythic Beasts is a long-standing UK service provider using standards-based OAuth2 authentication |
||||
|
# To test: ./acme.sh --dns dns_mythic_beasts --test --debug 1 --output-insecure --issue --domain domain.com |
||||
|
# Cannot retest once cert is issued |
||||
|
# OAuth2 tokens only valid for 300 seconds so we do not store |
||||
|
# NOTE: This will remove all TXT records matching the fulldomain, not just the added ones (_acme-challenge.www.domain.com) |
||||
|
|
||||
|
# Test OAuth2 credentials |
||||
|
#MB_AK="aaaaaaaaaaaaaaaa" |
||||
|
#MB_AS="bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" |
||||
|
|
||||
|
# URLs |
||||
|
MB_API='https://api.mythic-beasts.com/dns/v2/zones' |
||||
|
MB_AUTH='https://auth.mythic-beasts.com/login' |
||||
|
|
||||
|
######## Public functions ##################### |
||||
|
|
||||
|
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" |
||||
|
dns_mythic_beasts_add() { |
||||
|
fulldomain=$1 |
||||
|
txtvalue=$2 |
||||
|
|
||||
|
_info "MYTHIC BEASTS Adding record $fulldomain = $txtvalue" |
||||
|
if ! _initAuth; then |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
if ! _get_root "$fulldomain"; then |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
# method path body_data |
||||
|
if _mb_rest POST "$_domain/records/$_sub_domain/TXT" "$txtvalue"; then |
||||
|
|
||||
|
if _contains "$response" "1 records added"; then |
||||
|
_info "Added, verifying..." |
||||
|
# Max 120 seconds to publish |
||||
|
for i in $(seq 1 6); do |
||||
|
# Retry on error |
||||
|
if ! _mb_rest GET "$_domain/records/$_sub_domain/TXT?verify"; then |
||||
|
_sleep 20 |
||||
|
else |
||||
|
_info "Record published!" |
||||
|
return 0 |
||||
|
fi |
||||
|
done |
||||
|
|
||||
|
else |
||||
|
_err "\n$response" |
||||
|
fi |
||||
|
|
||||
|
fi |
||||
|
_err "Add txt record error." |
||||
|
return 1 |
||||
|
} |
||||
|
|
||||
|
#Usage: rm _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" |
||||
|
dns_mythic_beasts_rm() { |
||||
|
fulldomain=$1 |
||||
|
txtvalue=$2 |
||||
|
|
||||
|
_info "MYTHIC BEASTS Removing record $fulldomain = $txtvalue" |
||||
|
if ! _initAuth; then |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
if ! _get_root "$fulldomain"; then |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
# method path body_data |
||||
|
if _mb_rest DELETE "$_domain/records/$_sub_domain/TXT" "$txtvalue"; then |
||||
|
_info "Record removed" |
||||
|
return 0 |
||||
|
fi |
||||
|
_err "Remove txt record error." |
||||
|
return 1 |
||||
|
} |
||||
|
|
||||
|
#################### Private functions below ################################## |
||||
|
|
||||
|
#Possible formats: |
||||
|
# _acme-challenge.www.example.com |
||||
|
# _acme-challenge.example.com |
||||
|
# _acme-challenge.example.co.uk |
||||
|
# _acme-challenge.www.example.co.uk |
||||
|
# _acme-challenge.sub1.sub2.www.example.co.uk |
||||
|
# sub1.sub2.example.co.uk |
||||
|
# example.com |
||||
|
# example.co.uk |
||||
|
#returns |
||||
|
# _sub_domain=_acme-challenge.www |
||||
|
# _domain=domain.com |
||||
|
_get_root() { |
||||
|
domain=$1 |
||||
|
i=1 |
||||
|
p=1 |
||||
|
|
||||
|
_debug "Detect the root zone" |
||||
|
while true; do |
||||
|
h=$(printf "%s" "$domain" | cut -d . -f $i-100) |
||||
|
if [ -z "$h" ]; then |
||||
|
_err "Domain exhausted" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
# Use the status errors to find the domain, continue on 403 Access denied |
||||
|
# method path body_data |
||||
|
_mb_rest GET "$h/records" |
||||
|
ret="$?" |
||||
|
if [ "$ret" -eq 0 ]; then |
||||
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) |
||||
|
_domain="$h" |
||||
|
_debug _sub_domain "$_sub_domain" |
||||
|
_debug _domain "$_domain" |
||||
|
return 0 |
||||
|
elif [ "$ret" -eq 1 ]; then |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
p=$i |
||||
|
i=$(_math "$i" + 1) |
||||
|
|
||||
|
if [ "$i" -gt 50 ]; then |
||||
|
break |
||||
|
fi |
||||
|
done |
||||
|
_err "Domain too long" |
||||
|
return 1 |
||||
|
} |
||||
|
|
||||
|
_initAuth() { |
||||
|
MB_AK="${MB_AK:-$(_readaccountconf_mutable MB_AK)}" |
||||
|
MB_AS="${MB_AS:-$(_readaccountconf_mutable MB_AS)}" |
||||
|
|
||||
|
if [ -z "$MB_AK" ] || [ -z "$MB_AS" ]; then |
||||
|
MB_AK="" |
||||
|
MB_AS="" |
||||
|
_err "Please specify an OAuth2 Key & Secret" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_saveaccountconf_mutable MB_AK "$MB_AK" |
||||
|
_saveaccountconf_mutable MB_AS "$MB_AS" |
||||
|
|
||||
|
if ! _oauth2; then |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_info "Checking authentication" |
||||
|
_secure_debug access_token "$MB_TK" |
||||
|
_sleep 1 |
||||
|
|
||||
|
# GET a list of zones |
||||
|
# method path body_data |
||||
|
if ! _mb_rest GET ""; then |
||||
|
_err "The token is invalid" |
||||
|
return 1 |
||||
|
fi |
||||
|
_info "Token OK" |
||||
|
return 0 |
||||
|
} |
||||
|
|
||||
|
# Github appears to use an outbound proxy for requests which means subsequent requests may not have the same |
||||
|
# source IP. The standard Mythic Beasts OAuth2 tokens are tied to an IP, meaning github test requests fail |
||||
|
# authentication. This is a work around using an undocumented MB API to obtain a token not tied to an |
||||
|
# IP just for the github tests. |
||||
|
_oauth2() { |
||||
|
if [ "$GITHUB_ACTIONS" = "true" ]; then |
||||
|
_oauth2_github |
||||
|
else |
||||
|
_oauth2_std |
||||
|
fi |
||||
|
return $? |
||||
|
} |
||||
|
|
||||
|
_oauth2_std() { |
||||
|
# HTTP Basic Authentication |
||||
|
_H1="Authorization: Basic $(echo "$MB_AK:$MB_AS" | _base64)" |
||||
|
_H2="Accepts: application/json" |
||||
|
export _H1 _H2 |
||||
|
body="grant_type=client_credentials" |
||||
|
|
||||
|
_info "Getting OAuth2 token..." |
||||
|
# body url [needbase64] [POST|PUT|DELETE] [ContentType] |
||||
|
response="$(_post "$body" "$MB_AUTH" "" "POST" "application/x-www-form-urlencoded")" |
||||
|
if _contains "$response" "\"token_type\":\"bearer\""; then |
||||
|
MB_TK="$(echo "$response" | _egrep_o "access_token\":\"[^\"]*\"" | cut -d : -f 2 | tr -d '"')" |
||||
|
if [ -z "$MB_TK" ]; then |
||||
|
_err "Unable to get access_token" |
||||
|
_err "\n$response" |
||||
|
return 1 |
||||
|
fi |
||||
|
else |
||||
|
_err "OAuth2 token_type not Bearer" |
||||
|
_err "\n$response" |
||||
|
return 1 |
||||
|
fi |
||||
|
_debug2 response "$response" |
||||
|
return 0 |
||||
|
} |
||||
|
|
||||
|
_oauth2_github() { |
||||
|
_H1="Accepts: application/json" |
||||
|
export _H1 |
||||
|
body="{\"login\":{\"handle\":\"$MB_AK\",\"pass\":\"$MB_AS\",\"floating\":1}}" |
||||
|
|
||||
|
_info "Getting Floating token..." |
||||
|
# body url [needbase64] [POST|PUT|DELETE] [ContentType] |
||||
|
response="$(_post "$body" "$MB_AUTH" "" "POST" "application/json")" |
||||
|
MB_TK="$(echo "$response" | _egrep_o "\"token\":\"[^\"]*\"" | cut -d : -f 2 | tr -d '"')" |
||||
|
if [ -z "$MB_TK" ]; then |
||||
|
_err "Unable to get token" |
||||
|
_err "\n$response" |
||||
|
return 1 |
||||
|
fi |
||||
|
_debug2 response "$response" |
||||
|
return 0 |
||||
|
} |
||||
|
|
||||
|
# method path body_data |
||||
|
_mb_rest() { |
||||
|
# URL encoded body for single API operations |
||||
|
m="$1" |
||||
|
ep="$2" |
||||
|
data="$3" |
||||
|
|
||||
|
if [ -z "$ep" ]; then |
||||
|
_mb_url="$MB_API" |
||||
|
else |
||||
|
_mb_url="$MB_API/$ep" |
||||
|
fi |
||||
|
|
||||
|
_H1="Authorization: Bearer $MB_TK" |
||||
|
_H2="Accepts: application/json" |
||||
|
export _H1 _H2 |
||||
|
if [ "$data" ] || [ "$m" = "POST" ] || [ "$m" = "PUT" ] || [ "$m" = "DELETE" ]; then |
||||
|
# body url [needbase64] [POST|PUT|DELETE] [ContentType] |
||||
|
response="$(_post "data=$data" "$_mb_url" "" "$m" "application/x-www-form-urlencoded")" |
||||
|
else |
||||
|
response="$(_get "$_mb_url")" |
||||
|
fi |
||||
|
|
||||
|
if [ "$?" != "0" ]; then |
||||
|
_err "Request error" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
header="$(cat "$HTTP_HEADER")" |
||||
|
status="$(echo "$header" | _egrep_o "^HTTP[^ ]* .*$" | cut -d " " -f 2-100 | tr -d "\f\n")" |
||||
|
code="$(echo "$status" | _egrep_o "^[0-9]*")" |
||||
|
if [ "$code" -ge 400 ] || _contains "$response" "\"error\"" || _contains "$response" "invalid_client"; then |
||||
|
_err "error $status" |
||||
|
_err "\n$response" |
||||
|
_debug "\n$header" |
||||
|
return 2 |
||||
|
fi |
||||
|
|
||||
|
_debug2 response "$response" |
||||
|
return 0 |
||||
|
} |
||||
@ -0,0 +1,162 @@ |
|||||
|
#!/usr/bin/env sh |
||||
|
|
||||
|
#NETLIFY_ACCESS_TOKEN="xxxx" |
||||
|
|
||||
|
NETLIFY_HOST="api.netlify.com/api/v1/" |
||||
|
NETLIFY_URL="https://$NETLIFY_HOST" |
||||
|
|
||||
|
######## Public functions ##################### |
||||
|
|
||||
|
#Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" |
||||
|
dns_netlify_add() { |
||||
|
fulldomain=$1 |
||||
|
txtvalue=$2 |
||||
|
|
||||
|
NETLIFY_ACCESS_TOKEN="${NETLIFY_ACCESS_TOKEN:-$(_readaccountconf_mutable NETLIFY_ACCESS_TOKEN)}" |
||||
|
|
||||
|
if [ -z "$NETLIFY_ACCESS_TOKEN" ]; then |
||||
|
NETLIFY_ACCESS_TOKEN="" |
||||
|
_err "Please specify your Netlify Access Token and try again." |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_info "Using Netlify" |
||||
|
_debug fulldomain "$fulldomain" |
||||
|
_debug txtvalue "$txtvalue" |
||||
|
|
||||
|
_saveaccountconf_mutable NETLIFY_ACCESS_TOKEN "$NETLIFY_ACCESS_TOKEN" |
||||
|
|
||||
|
if ! _get_root "$fulldomain" "$accesstoken"; then |
||||
|
_err "invalid domain" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_debug _domain_id "$_domain_id" |
||||
|
_debug _sub_domain "$_sub_domain" |
||||
|
_debug _domain "$_domain" |
||||
|
|
||||
|
dnsRecordURI="dns_zones/$_domain_id/dns_records" |
||||
|
|
||||
|
body="{\"type\":\"TXT\", \"hostname\":\"$_sub_domain\", \"value\":\"$txtvalue\", \"ttl\":\"10\"}" |
||||
|
|
||||
|
_netlify_rest POST "$dnsRecordURI" "$body" "$NETLIFY_ACCESS_TOKEN" |
||||
|
_code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")" |
||||
|
if [ "$_code" = "200" ] || [ "$_code" = '201' ]; then |
||||
|
_info "validation value added" |
||||
|
return 0 |
||||
|
else |
||||
|
_err "error adding validation value ($_code)" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_err "Not fully implemented!" |
||||
|
return 1 |
||||
|
} |
||||
|
|
||||
|
#Usage: dns_myapi_rm _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" |
||||
|
#Remove the txt record after validation. |
||||
|
dns_netlify_rm() { |
||||
|
_info "Using Netlify" |
||||
|
txtdomain="$1" |
||||
|
txt="$2" |
||||
|
_debug txtdomain "$txtdomain" |
||||
|
_debug txt "$txt" |
||||
|
|
||||
|
_saveaccountconf_mutable NETLIFY_ACCESS_TOKEN "$NETLIFY_ACCESS_TOKEN" |
||||
|
|
||||
|
if ! _get_root "$txtdomain" "$accesstoken"; then |
||||
|
_err "invalid domain" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_debug _domain_id "$_domain_id" |
||||
|
_debug _sub_domain "$_sub_domain" |
||||
|
_debug _domain "$_domain" |
||||
|
|
||||
|
dnsRecordURI="dns_zones/$_domain_id/dns_records" |
||||
|
|
||||
|
_netlify_rest GET "$dnsRecordURI" "" "$NETLIFY_ACCESS_TOKEN" |
||||
|
|
||||
|
_record_id=$(echo "$response" | _egrep_o "\"type\":\"TXT\",[^\}]*\"value\":\"$txt\"" | head -n 1 | _egrep_o "\"id\":\"[^\"\}]*\"" | cut -d : -f 2 | tr -d \") |
||||
|
_debug _record_id "$_record_id" |
||||
|
if [ "$_record_id" ]; then |
||||
|
_netlify_rest DELETE "$dnsRecordURI/$_record_id" "" "$NETLIFY_ACCESS_TOKEN" |
||||
|
_code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")" |
||||
|
if [ "$_code" = "200" ] || [ "$_code" = '204' ]; then |
||||
|
_info "validation value removed" |
||||
|
return 0 |
||||
|
else |
||||
|
_err "error removing validation value ($_code)" |
||||
|
return 1 |
||||
|
fi |
||||
|
return 0 |
||||
|
fi |
||||
|
return 1 |
||||
|
} |
||||
|
|
||||
|
#################### Private functions below ################################## |
||||
|
|
||||
|
_get_root() { |
||||
|
domain=$1 |
||||
|
accesstoken=$2 |
||||
|
i=1 |
||||
|
p=1 |
||||
|
|
||||
|
_netlify_rest GET "dns_zones" "" "$accesstoken" |
||||
|
|
||||
|
while true; do |
||||
|
h=$(printf "%s" "$domain" | cut -d . -f $i-100) |
||||
|
_debug2 "Checking domain: $h" |
||||
|
if [ -z "$h" ]; then |
||||
|
#not valid |
||||
|
_err "Invalid domain" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
if _contains "$response" "\"name\":\"$h\"" >/dev/null; then |
||||
|
_domain_id=$(echo "$response" | _egrep_o "\"[^\"]*\",\"name\":\"$h" | cut -d , -f 1 | tr -d \") |
||||
|
if [ "$_domain_id" ]; then |
||||
|
if [ "$i" = 1 ]; then |
||||
|
#create the record at the domain apex (@) if only the domain name was provided as --domain-alias |
||||
|
_sub_domain="@" |
||||
|
else |
||||
|
_sub_domain=$(echo "$domain" | cut -d . -f 1-$p) |
||||
|
fi |
||||
|
_domain=$h |
||||
|
return 0 |
||||
|
fi |
||||
|
return 1 |
||||
|
fi |
||||
|
p=$i |
||||
|
i=$(_math "$i" + 1) |
||||
|
done |
||||
|
return 1 |
||||
|
} |
||||
|
|
||||
|
_netlify_rest() { |
||||
|
m=$1 |
||||
|
ep="$2" |
||||
|
data="$3" |
||||
|
_debug "$ep" |
||||
|
|
||||
|
token_trimmed=$(echo "$NETLIFY_ACCESS_TOKEN" | tr -d '"') |
||||
|
|
||||
|
export _H1="Content-Type: application/json" |
||||
|
export _H2="Authorization: Bearer $token_trimmed" |
||||
|
|
||||
|
: >"$HTTP_HEADER" |
||||
|
|
||||
|
if [ "$m" != "GET" ]; then |
||||
|
_debug data "$data" |
||||
|
response="$(_post "$data" "$NETLIFY_URL$ep" "" "$m")" |
||||
|
else |
||||
|
response="$(_get "$NETLIFY_URL$ep")" |
||||
|
fi |
||||
|
|
||||
|
if [ "$?" != "0" ]; then |
||||
|
_err "error $ep" |
||||
|
return 1 |
||||
|
fi |
||||
|
_debug2 response "$response" |
||||
|
return 0 |
||||
|
} |
||||
@ -0,0 +1,324 @@ |
|||||
|
#!/usr/bin/env sh |
||||
|
# |
||||
|
# Acme.sh DNS API plugin for Oracle Cloud Infrastructure |
||||
|
# Copyright (c) 2021, Oracle and/or its affiliates |
||||
|
# |
||||
|
# The plugin will automatically use the default profile from an OCI SDK and CLI |
||||
|
# configuration file, if it exists. |
||||
|
# |
||||
|
# Alternatively, set the following environment variables: |
||||
|
# - OCI_CLI_TENANCY : OCID of tenancy that contains the target DNS zone |
||||
|
# - OCI_CLI_USER : OCID of user with permission to add/remove records from zones |
||||
|
# - OCI_CLI_REGION : Should point to the tenancy home region |
||||
|
# |
||||
|
# One of the following two variables is required: |
||||
|
# - OCI_CLI_KEY_FILE: Path to private API signing key file in PEM format; or |
||||
|
# - OCI_CLI_KEY : The private API signing key in PEM format |
||||
|
# |
||||
|
# NOTE: using an encrypted private key that needs a passphrase is not supported. |
||||
|
# |
||||
|
|
||||
|
dns_oci_add() { |
||||
|
_fqdn="$1" |
||||
|
_rdata="$2" |
||||
|
|
||||
|
if _get_oci_zone; then |
||||
|
|
||||
|
_add_record_body="{\"items\":[{\"domain\":\"${_sub_domain}.${_domain}\",\"rdata\":\"$_rdata\",\"rtype\":\"TXT\",\"ttl\": 30,\"operation\":\"ADD\"}]}" |
||||
|
response=$(_signed_request "PATCH" "/20180115/zones/${_domain}/records" "$_add_record_body") |
||||
|
if [ "$response" ]; then |
||||
|
_info "Success: added TXT record for ${_sub_domain}.${_domain}." |
||||
|
else |
||||
|
_err "Error: failed to add TXT record for ${_sub_domain}.${_domain}." |
||||
|
_err "Check that the user has permission to add records to this zone." |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
else |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
} |
||||
|
|
||||
|
dns_oci_rm() { |
||||
|
_fqdn="$1" |
||||
|
_rdata="$2" |
||||
|
|
||||
|
if _get_oci_zone; then |
||||
|
|
||||
|
_remove_record_body="{\"items\":[{\"domain\":\"${_sub_domain}.${_domain}\",\"rdata\":\"$_rdata\",\"rtype\":\"TXT\",\"operation\":\"REMOVE\"}]}" |
||||
|
response=$(_signed_request "PATCH" "/20180115/zones/${_domain}/records" "$_remove_record_body") |
||||
|
if [ "$response" ]; then |
||||
|
_info "Success: removed TXT record for ${_sub_domain}.${_domain}." |
||||
|
else |
||||
|
_err "Error: failed to remove TXT record for ${_sub_domain}.${_domain}." |
||||
|
_err "Check that the user has permission to remove records from this zone." |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
else |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
} |
||||
|
|
||||
|
#################### Private functions below ################################## |
||||
|
_get_oci_zone() { |
||||
|
|
||||
|
if ! _oci_config; then |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
if ! _get_zone "$_fqdn"; then |
||||
|
_err "Error: DNS Zone not found for $_fqdn in $OCI_CLI_TENANCY" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
return 0 |
||||
|
|
||||
|
} |
||||
|
|
||||
|
_oci_config() { |
||||
|
|
||||
|
_DEFAULT_OCI_CLI_CONFIG_FILE="$HOME/.oci/config" |
||||
|
OCI_CLI_CONFIG_FILE="${OCI_CLI_CONFIG_FILE:-$(_readaccountconf_mutable OCI_CLI_CONFIG_FILE)}" |
||||
|
|
||||
|
if [ -z "$OCI_CLI_CONFIG_FILE" ]; then |
||||
|
OCI_CLI_CONFIG_FILE="$_DEFAULT_OCI_CLI_CONFIG_FILE" |
||||
|
fi |
||||
|
|
||||
|
if [ "$_DEFAULT_OCI_CLI_CONFIG_FILE" != "$OCI_CLI_CONFIG_FILE" ]; then |
||||
|
_saveaccountconf_mutable OCI_CLI_CONFIG_FILE "$OCI_CLI_CONFIG_FILE" |
||||
|
else |
||||
|
_clearaccountconf_mutable OCI_CLI_CONFIG_FILE |
||||
|
fi |
||||
|
|
||||
|
_DEFAULT_OCI_CLI_PROFILE="DEFAULT" |
||||
|
OCI_CLI_PROFILE="${OCI_CLI_PROFILE:-$(_readaccountconf_mutable OCI_CLI_PROFILE)}" |
||||
|
if [ "$_DEFAULT_OCI_CLI_PROFILE" != "$OCI_CLI_PROFILE" ]; then |
||||
|
_saveaccountconf_mutable OCI_CLI_PROFILE "$OCI_CLI_PROFILE" |
||||
|
else |
||||
|
OCI_CLI_PROFILE="$_DEFAULT_OCI_CLI_PROFILE" |
||||
|
_clearaccountconf_mutable OCI_CLI_PROFILE |
||||
|
fi |
||||
|
|
||||
|
OCI_CLI_TENANCY="${OCI_CLI_TENANCY:-$(_readaccountconf_mutable OCI_CLI_TENANCY)}" |
||||
|
if [ "$OCI_CLI_TENANCY" ]; then |
||||
|
_saveaccountconf_mutable OCI_CLI_TENANCY "$OCI_CLI_TENANCY" |
||||
|
elif [ -f "$OCI_CLI_CONFIG_FILE" ]; then |
||||
|
_debug "Reading OCI_CLI_TENANCY value from: $OCI_CLI_CONFIG_FILE" |
||||
|
OCI_CLI_TENANCY="${OCI_CLI_TENANCY:-$(_readini "$OCI_CLI_CONFIG_FILE" tenancy "$OCI_CLI_PROFILE")}" |
||||
|
fi |
||||
|
|
||||
|
if [ -z "$OCI_CLI_TENANCY" ]; then |
||||
|
_err "Error: unable to read OCI_CLI_TENANCY from config file or environment variable." |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
OCI_CLI_USER="${OCI_CLI_USER:-$(_readaccountconf_mutable OCI_CLI_USER)}" |
||||
|
if [ "$OCI_CLI_USER" ]; then |
||||
|
_saveaccountconf_mutable OCI_CLI_USER "$OCI_CLI_USER" |
||||
|
elif [ -f "$OCI_CLI_CONFIG_FILE" ]; then |
||||
|
_debug "Reading OCI_CLI_USER value from: $OCI_CLI_CONFIG_FILE" |
||||
|
OCI_CLI_USER="${OCI_CLI_USER:-$(_readini "$OCI_CLI_CONFIG_FILE" user "$OCI_CLI_PROFILE")}" |
||||
|
fi |
||||
|
if [ -z "$OCI_CLI_USER" ]; then |
||||
|
_err "Error: unable to read OCI_CLI_USER from config file or environment variable." |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
OCI_CLI_REGION="${OCI_CLI_REGION:-$(_readaccountconf_mutable OCI_CLI_REGION)}" |
||||
|
if [ "$OCI_CLI_REGION" ]; then |
||||
|
_saveaccountconf_mutable OCI_CLI_REGION "$OCI_CLI_REGION" |
||||
|
elif [ -f "$OCI_CLI_CONFIG_FILE" ]; then |
||||
|
_debug "Reading OCI_CLI_REGION value from: $OCI_CLI_CONFIG_FILE" |
||||
|
OCI_CLI_REGION="${OCI_CLI_REGION:-$(_readini "$OCI_CLI_CONFIG_FILE" region "$OCI_CLI_PROFILE")}" |
||||
|
fi |
||||
|
if [ -z "$OCI_CLI_REGION" ]; then |
||||
|
_err "Error: unable to read OCI_CLI_REGION from config file or environment variable." |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
OCI_CLI_KEY="${OCI_CLI_KEY:-$(_readaccountconf_mutable OCI_CLI_KEY)}" |
||||
|
if [ -z "$OCI_CLI_KEY" ]; then |
||||
|
_clearaccountconf_mutable OCI_CLI_KEY |
||||
|
OCI_CLI_KEY_FILE="${OCI_CLI_KEY_FILE:-$(_readini "$OCI_CLI_CONFIG_FILE" key_file "$OCI_CLI_PROFILE")}" |
||||
|
if [ "$OCI_CLI_KEY_FILE" ] && [ -f "$OCI_CLI_KEY_FILE" ]; then |
||||
|
_debug "Reading OCI_CLI_KEY value from: $OCI_CLI_KEY_FILE" |
||||
|
OCI_CLI_KEY=$(_base64 <"$OCI_CLI_KEY_FILE") |
||||
|
_saveaccountconf_mutable OCI_CLI_KEY "$OCI_CLI_KEY" |
||||
|
fi |
||||
|
else |
||||
|
_saveaccountconf_mutable OCI_CLI_KEY "$OCI_CLI_KEY" |
||||
|
fi |
||||
|
|
||||
|
if [ -z "$OCI_CLI_KEY_FILE" ] && [ -z "$OCI_CLI_KEY" ]; then |
||||
|
_err "Error: unable to find key file path in OCI config file or OCI_CLI_KEY_FILE." |
||||
|
_err "Error: unable to load private API signing key from OCI_CLI_KEY." |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
if [ "$(printf "%s\n" "$OCI_CLI_KEY" | wc -l)" -eq 1 ]; then |
||||
|
OCI_CLI_KEY=$(printf "%s" "$OCI_CLI_KEY" | _dbase64 multiline) |
||||
|
fi |
||||
|
|
||||
|
return 0 |
||||
|
|
||||
|
} |
||||
|
|
||||
|
# _get_zone(): retrieves the Zone name and OCID |
||||
|
# |
||||
|
# _sub_domain=_acme-challenge.www |
||||
|
# _domain=domain.com |
||||
|
# _domain_ociid=ocid1.dns-zone.oc1.. |
||||
|
_get_zone() { |
||||
|
domain=$1 |
||||
|
i=1 |
||||
|
p=1 |
||||
|
|
||||
|
while true; do |
||||
|
h=$(printf "%s" "$domain" | cut -d . -f $i-100) |
||||
|
_debug h "$h" |
||||
|
if [ -z "$h" ]; then |
||||
|
# not valid |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_domain_id=$(_signed_request "GET" "/20180115/zones/$h" "" "id") |
||||
|
if [ "$_domain_id" ]; then |
||||
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) |
||||
|
_domain=$h |
||||
|
|
||||
|
_debug _domain_id "$_domain_id" |
||||
|
_debug _sub_domain "$_sub_domain" |
||||
|
_debug _domain "$_domain" |
||||
|
return 0 |
||||
|
fi |
||||
|
|
||||
|
p=$i |
||||
|
i=$(_math "$i" + 1) |
||||
|
done |
||||
|
return 1 |
||||
|
|
||||
|
} |
||||
|
|
||||
|
#Usage: privatekey |
||||
|
#Output MD5 fingerprint |
||||
|
_fingerprint() { |
||||
|
|
||||
|
pkey="$1" |
||||
|
if [ -z "$pkey" ]; then |
||||
|
_usage "Usage: _fingerprint privkey" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
printf "%s" "$pkey" | ${ACME_OPENSSL_BIN:-openssl} rsa -pubout -outform DER 2>/dev/null | ${ACME_OPENSSL_BIN:-openssl} md5 -c | cut -d = -f 2 | tr -d ' ' |
||||
|
|
||||
|
} |
||||
|
|
||||
|
_signed_request() { |
||||
|
|
||||
|
_sig_method="$1" |
||||
|
_sig_target="$2" |
||||
|
_sig_body="$3" |
||||
|
_return_field="$4" |
||||
|
|
||||
|
_key_fingerprint=$(_fingerprint "$OCI_CLI_KEY") |
||||
|
_sig_host="dns.$OCI_CLI_REGION.oraclecloud.com" |
||||
|
_sig_keyId="$OCI_CLI_TENANCY/$OCI_CLI_USER/$_key_fingerprint" |
||||
|
_sig_alg="rsa-sha256" |
||||
|
_sig_version="1" |
||||
|
_sig_now="$(LC_ALL=C \date -u "+%a, %d %h %Y %H:%M:%S GMT")" |
||||
|
|
||||
|
_request_method=$(printf %s "$_sig_method" | _lower_case) |
||||
|
_curl_method=$(printf %s "$_sig_method" | _upper_case) |
||||
|
|
||||
|
_request_target="(request-target): $_request_method $_sig_target" |
||||
|
_date_header="date: $_sig_now" |
||||
|
_host_header="host: $_sig_host" |
||||
|
|
||||
|
_string_to_sign="$_request_target\n$_date_header\n$_host_header" |
||||
|
_sig_headers="(request-target) date host" |
||||
|
|
||||
|
if [ "$_sig_body" ]; then |
||||
|
_secure_debug3 _sig_body "$_sig_body" |
||||
|
_sig_body_sha256="x-content-sha256: $(printf %s "$_sig_body" | _digest sha256)" |
||||
|
_sig_body_type="content-type: application/json" |
||||
|
_sig_body_length="content-length: ${#_sig_body}" |
||||
|
_string_to_sign="$_string_to_sign\n$_sig_body_sha256\n$_sig_body_type\n$_sig_body_length" |
||||
|
_sig_headers="$_sig_headers x-content-sha256 content-type content-length" |
||||
|
fi |
||||
|
|
||||
|
_tmp_file=$(_mktemp) |
||||
|
if [ -f "$_tmp_file" ]; then |
||||
|
printf '%s' "$OCI_CLI_KEY" >"$_tmp_file" |
||||
|
_signature=$(printf '%b' "$_string_to_sign" | _sign "$_tmp_file" sha256 | tr -d '\r\n') |
||||
|
rm -f "$_tmp_file" |
||||
|
fi |
||||
|
|
||||
|
_signed_header="Authorization: Signature version=\"$_sig_version\",keyId=\"$_sig_keyId\",algorithm=\"$_sig_alg\",headers=\"$_sig_headers\",signature=\"$_signature\"" |
||||
|
_secure_debug3 _signed_header "$_signed_header" |
||||
|
|
||||
|
if [ "$_curl_method" = "GET" ]; then |
||||
|
export _H1="$_date_header" |
||||
|
export _H2="$_signed_header" |
||||
|
_response="$(_get "https://${_sig_host}${_sig_target}")" |
||||
|
elif [ "$_curl_method" = "PATCH" ]; then |
||||
|
export _H1="$_date_header" |
||||
|
export _H2="$_sig_body_sha256" |
||||
|
export _H3="$_sig_body_type" |
||||
|
export _H4="$_sig_body_length" |
||||
|
export _H5="$_signed_header" |
||||
|
_response="$(_post "$_sig_body" "https://${_sig_host}${_sig_target}" "" "PATCH")" |
||||
|
else |
||||
|
_err "Unable to process method: $_curl_method." |
||||
|
fi |
||||
|
|
||||
|
_ret="$?" |
||||
|
if [ "$_return_field" ]; then |
||||
|
_response="$(echo "$_response" | sed 's/\\\"//g'))" |
||||
|
_return=$(echo "${_response}" | _egrep_o "\"$_return_field\"\\s*:\\s*\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d "\"") |
||||
|
else |
||||
|
_return="$_response" |
||||
|
fi |
||||
|
|
||||
|
printf "%s" "$_return" |
||||
|
return $_ret |
||||
|
|
||||
|
} |
||||
|
|
||||
|
# file key [section] |
||||
|
_readini() { |
||||
|
_file="$1" |
||||
|
_key="$2" |
||||
|
_section="${3:-DEFAULT}" |
||||
|
|
||||
|
_start_n=$(grep -n '\['"$_section"']' "$_file" | cut -d : -f 1) |
||||
|
_debug3 _start_n "$_start_n" |
||||
|
if [ -z "$_start_n" ]; then |
||||
|
_err "Can not find section: $_section" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_start_nn=$(_math "$_start_n" + 1) |
||||
|
_debug3 "_start_nn" "$_start_nn" |
||||
|
|
||||
|
_left="$(sed -n "${_start_nn},99999p" "$_file")" |
||||
|
_debug3 _left "$_left" |
||||
|
_end="$(echo "$_left" | grep -n "^\[" | _head_n 1)" |
||||
|
_debug3 "_end" "$_end" |
||||
|
if [ "$_end" ]; then |
||||
|
_end_n=$(echo "$_end" | cut -d : -f 1) |
||||
|
_debug3 "_end_n" "$_end_n" |
||||
|
_seg_n=$(echo "$_left" | sed -n "1,${_end_n}p") |
||||
|
else |
||||
|
_seg_n="$_left" |
||||
|
fi |
||||
|
|
||||
|
_debug3 "_seg_n" "$_seg_n" |
||||
|
_lineini="$(echo "$_seg_n" | grep "^ *$_key *= *")" |
||||
|
_inivalue="$(printf "%b" "$(eval "echo $_lineini | sed \"s/^ *${_key} *= *//g\"")")" |
||||
|
_debug2 _inivalue "$_inivalue" |
||||
|
echo "$_inivalue" |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,348 @@ |
|||||
|
#!/usr/bin/env sh |
||||
|
|
||||
|
# OpenStack Designate API plugin |
||||
|
# |
||||
|
# This requires you to have OpenStackClient and python-desginateclient |
||||
|
# installed. |
||||
|
# |
||||
|
# You will require Keystone V3 credentials loaded into your environment, which |
||||
|
# could be either password or v3applicationcredential type. |
||||
|
# |
||||
|
# Author: Andy Botting <andy@andybotting.com> |
||||
|
|
||||
|
######## Public functions ##################### |
||||
|
|
||||
|
# Usage: dns_openstack_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" |
||||
|
dns_openstack_add() { |
||||
|
fulldomain=$1 |
||||
|
txtvalue=$2 |
||||
|
_debug fulldomain "$fulldomain" |
||||
|
_debug txtvalue "$txtvalue" |
||||
|
|
||||
|
_dns_openstack_credentials || return $? |
||||
|
_dns_openstack_check_setup || return $? |
||||
|
_dns_openstack_find_zone || return $? |
||||
|
_dns_openstack_get_recordset || return $? |
||||
|
_debug _recordset_id "$_recordset_id" |
||||
|
if [ -n "$_recordset_id" ]; then |
||||
|
_dns_openstack_get_records || return $? |
||||
|
_debug _records "$_records" |
||||
|
fi |
||||
|
_dns_openstack_create_recordset || return $? |
||||
|
} |
||||
|
|
||||
|
# Usage: dns_openstack_rm _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" |
||||
|
# Remove the txt record after validation. |
||||
|
dns_openstack_rm() { |
||||
|
fulldomain=$1 |
||||
|
txtvalue=$2 |
||||
|
_debug fulldomain "$fulldomain" |
||||
|
_debug txtvalue "$txtvalue" |
||||
|
|
||||
|
_dns_openstack_credentials || return $? |
||||
|
_dns_openstack_check_setup || return $? |
||||
|
_dns_openstack_find_zone || return $? |
||||
|
_dns_openstack_get_recordset || return $? |
||||
|
_debug _recordset_id "$_recordset_id" |
||||
|
if [ -n "$_recordset_id" ]; then |
||||
|
_dns_openstack_get_records || return $? |
||||
|
_debug _records "$_records" |
||||
|
fi |
||||
|
_dns_openstack_delete_recordset || return $? |
||||
|
} |
||||
|
|
||||
|
#################### Private functions below ################################## |
||||
|
|
||||
|
_dns_openstack_create_recordset() { |
||||
|
|
||||
|
if [ -z "$_recordset_id" ]; then |
||||
|
_info "Creating a new recordset" |
||||
|
if ! _recordset_id=$(openstack recordset create -c id -f value --type TXT --record "$txtvalue" "$_zone_id" "$fulldomain."); then |
||||
|
_err "No recordset ID found after create" |
||||
|
return 1 |
||||
|
fi |
||||
|
else |
||||
|
_info "Updating existing recordset" |
||||
|
# Build new list of --record <rec> args for update |
||||
|
_record_args="--record $txtvalue" |
||||
|
for _rec in $_records; do |
||||
|
_record_args="$_record_args --record $_rec" |
||||
|
done |
||||
|
# shellcheck disable=SC2086 |
||||
|
if ! _recordset_id=$(openstack recordset set -c id -f value $_record_args "$_zone_id" "$fulldomain."); then |
||||
|
_err "Recordset update failed" |
||||
|
return 1 |
||||
|
fi |
||||
|
fi |
||||
|
|
||||
|
_max_retries=60 |
||||
|
_sleep_sec=5 |
||||
|
_retry_times=0 |
||||
|
while [ "$_retry_times" -lt "$_max_retries" ]; do |
||||
|
_retry_times=$(_math "$_retry_times" + 1) |
||||
|
_debug3 _retry_times "$_retry_times" |
||||
|
|
||||
|
_record_status=$(openstack recordset show -c status -f value "$_zone_id" "$_recordset_id") |
||||
|
_info "Recordset status is $_record_status" |
||||
|
if [ "$_record_status" = "ACTIVE" ]; then |
||||
|
return 0 |
||||
|
elif [ "$_record_status" = "ERROR" ]; then |
||||
|
return 1 |
||||
|
else |
||||
|
_sleep $_sleep_sec |
||||
|
fi |
||||
|
done |
||||
|
|
||||
|
_err "Recordset failed to become ACTIVE" |
||||
|
return 1 |
||||
|
} |
||||
|
|
||||
|
_dns_openstack_delete_recordset() { |
||||
|
|
||||
|
if [ "$_records" = "$txtvalue" ]; then |
||||
|
_info "Only one record found, deleting recordset" |
||||
|
if ! openstack recordset delete "$_zone_id" "$fulldomain." >/dev/null; then |
||||
|
_err "Failed to delete recordset" |
||||
|
return 1 |
||||
|
fi |
||||
|
else |
||||
|
_info "Found existing records, updating recordset" |
||||
|
# Build new list of --record <rec> args for update |
||||
|
_record_args="" |
||||
|
for _rec in $_records; do |
||||
|
if [ "$_rec" = "$txtvalue" ]; then |
||||
|
continue |
||||
|
fi |
||||
|
_record_args="$_record_args --record $_rec" |
||||
|
done |
||||
|
# shellcheck disable=SC2086 |
||||
|
if ! openstack recordset set -c id -f value $_record_args "$_zone_id" "$fulldomain." >/dev/null; then |
||||
|
_err "Recordset update failed" |
||||
|
return 1 |
||||
|
fi |
||||
|
fi |
||||
|
} |
||||
|
|
||||
|
_dns_openstack_get_root() { |
||||
|
# Take the full fqdn and strip away pieces until we get an exact zone name |
||||
|
# match. For example, _acme-challenge.something.domain.com might need to go |
||||
|
# into something.domain.com or domain.com |
||||
|
_zone_name=$1 |
||||
|
_zone_list=$2 |
||||
|
while [ "$_zone_name" != "" ]; do |
||||
|
_zone_name="$(echo "$_zone_name" | sed 's/[^.]*\.*//')" |
||||
|
echo "$_zone_list" | while read -r id name; do |
||||
|
if _startswith "$_zone_name." "$name"; then |
||||
|
echo "$id" |
||||
|
fi |
||||
|
done |
||||
|
done | _head_n 1 |
||||
|
} |
||||
|
|
||||
|
_dns_openstack_find_zone() { |
||||
|
if ! _zone_list="$(openstack zone list -c id -c name -f value)"; then |
||||
|
_err "Can't list zones. Check your OpenStack credentials" |
||||
|
return 1 |
||||
|
fi |
||||
|
_debug _zone_list "$_zone_list" |
||||
|
|
||||
|
if ! _zone_id="$(_dns_openstack_get_root "$fulldomain" "$_zone_list")"; then |
||||
|
_err "Can't find a matching zone. Check your OpenStack credentials" |
||||
|
return 1 |
||||
|
fi |
||||
|
_debug _zone_id "$_zone_id" |
||||
|
} |
||||
|
|
||||
|
_dns_openstack_get_records() { |
||||
|
if ! _records=$(openstack recordset show -c records -f value "$_zone_id" "$fulldomain."); then |
||||
|
_err "Failed to get records" |
||||
|
return 1 |
||||
|
fi |
||||
|
return 0 |
||||
|
} |
||||
|
|
||||
|
_dns_openstack_get_recordset() { |
||||
|
if ! _recordset_id=$(openstack recordset list -c id -f value --name "$fulldomain." "$_zone_id"); then |
||||
|
_err "Failed to get recordset" |
||||
|
return 1 |
||||
|
fi |
||||
|
return 0 |
||||
|
} |
||||
|
|
||||
|
_dns_openstack_check_setup() { |
||||
|
if ! _exists openstack; then |
||||
|
_err "OpenStack client not found" |
||||
|
return 1 |
||||
|
fi |
||||
|
} |
||||
|
|
||||
|
_dns_openstack_credentials() { |
||||
|
_debug "Check OpenStack credentials" |
||||
|
|
||||
|
# If we have OS_AUTH_URL already set in the environment, then assume we want |
||||
|
# to use those, otherwise use stored credentials |
||||
|
if [ -n "$OS_AUTH_URL" ]; then |
||||
|
_debug "OS_AUTH_URL env var found, using environment" |
||||
|
else |
||||
|
_debug "OS_AUTH_URL not found, loading stored credentials" |
||||
|
OS_AUTH_URL="${OS_AUTH_URL:-$(_readaccountconf_mutable OS_AUTH_URL)}" |
||||
|
OS_IDENTITY_API_VERSION="${OS_IDENTITY_API_VERSION:-$(_readaccountconf_mutable OS_IDENTITY_API_VERSION)}" |
||||
|
OS_AUTH_TYPE="${OS_AUTH_TYPE:-$(_readaccountconf_mutable OS_AUTH_TYPE)}" |
||||
|
OS_APPLICATION_CREDENTIAL_ID="${OS_APPLICATION_CREDENTIAL_ID:-$(_readaccountconf_mutable OS_APPLICATION_CREDENTIAL_ID)}" |
||||
|
OS_APPLICATION_CREDENTIAL_SECRET="${OS_APPLICATION_CREDENTIAL_SECRET:-$(_readaccountconf_mutable OS_APPLICATION_CREDENTIAL_SECRET)}" |
||||
|
OS_USERNAME="${OS_USERNAME:-$(_readaccountconf_mutable OS_USERNAME)}" |
||||
|
OS_PASSWORD="${OS_PASSWORD:-$(_readaccountconf_mutable OS_PASSWORD)}" |
||||
|
OS_PROJECT_NAME="${OS_PROJECT_NAME:-$(_readaccountconf_mutable OS_PROJECT_NAME)}" |
||||
|
OS_PROJECT_ID="${OS_PROJECT_ID:-$(_readaccountconf_mutable OS_PROJECT_ID)}" |
||||
|
OS_USER_DOMAIN_NAME="${OS_USER_DOMAIN_NAME:-$(_readaccountconf_mutable OS_USER_DOMAIN_NAME)}" |
||||
|
OS_USER_DOMAIN_ID="${OS_USER_DOMAIN_ID:-$(_readaccountconf_mutable OS_USER_DOMAIN_ID)}" |
||||
|
OS_PROJECT_DOMAIN_NAME="${OS_PROJECT_DOMAIN_NAME:-$(_readaccountconf_mutable OS_PROJECT_DOMAIN_NAME)}" |
||||
|
OS_PROJECT_DOMAIN_ID="${OS_PROJECT_DOMAIN_ID:-$(_readaccountconf_mutable OS_PROJECT_DOMAIN_ID)}" |
||||
|
fi |
||||
|
|
||||
|
# Check each var and either save or clear it depending on whether its set. |
||||
|
# The helps us clear out old vars in the case where a user may want |
||||
|
# to switch between password and app creds |
||||
|
_debug "OS_AUTH_URL" "$OS_AUTH_URL" |
||||
|
if [ -n "$OS_AUTH_URL" ]; then |
||||
|
export OS_AUTH_URL |
||||
|
_saveaccountconf_mutable OS_AUTH_URL "$OS_AUTH_URL" |
||||
|
else |
||||
|
unset OS_AUTH_URL |
||||
|
_clearaccountconf SAVED_OS_AUTH_URL |
||||
|
fi |
||||
|
|
||||
|
_debug "OS_IDENTITY_API_VERSION" "$OS_IDENTITY_API_VERSION" |
||||
|
if [ -n "$OS_IDENTITY_API_VERSION" ]; then |
||||
|
export OS_IDENTITY_API_VERSION |
||||
|
_saveaccountconf_mutable OS_IDENTITY_API_VERSION "$OS_IDENTITY_API_VERSION" |
||||
|
else |
||||
|
unset OS_IDENTITY_API_VERSION |
||||
|
_clearaccountconf SAVED_OS_IDENTITY_API_VERSION |
||||
|
fi |
||||
|
|
||||
|
_debug "OS_AUTH_TYPE" "$OS_AUTH_TYPE" |
||||
|
if [ -n "$OS_AUTH_TYPE" ]; then |
||||
|
export OS_AUTH_TYPE |
||||
|
_saveaccountconf_mutable OS_AUTH_TYPE "$OS_AUTH_TYPE" |
||||
|
else |
||||
|
unset OS_AUTH_TYPE |
||||
|
_clearaccountconf SAVED_OS_AUTH_TYPE |
||||
|
fi |
||||
|
|
||||
|
_debug "OS_APPLICATION_CREDENTIAL_ID" "$OS_APPLICATION_CREDENTIAL_ID" |
||||
|
if [ -n "$OS_APPLICATION_CREDENTIAL_ID" ]; then |
||||
|
export OS_APPLICATION_CREDENTIAL_ID |
||||
|
_saveaccountconf_mutable OS_APPLICATION_CREDENTIAL_ID "$OS_APPLICATION_CREDENTIAL_ID" |
||||
|
else |
||||
|
unset OS_APPLICATION_CREDENTIAL_ID |
||||
|
_clearaccountconf SAVED_OS_APPLICATION_CREDENTIAL_ID |
||||
|
fi |
||||
|
|
||||
|
_secure_debug "OS_APPLICATION_CREDENTIAL_SECRET" "$OS_APPLICATION_CREDENTIAL_SECRET" |
||||
|
if [ -n "$OS_APPLICATION_CREDENTIAL_SECRET" ]; then |
||||
|
export OS_APPLICATION_CREDENTIAL_SECRET |
||||
|
_saveaccountconf_mutable OS_APPLICATION_CREDENTIAL_SECRET "$OS_APPLICATION_CREDENTIAL_SECRET" |
||||
|
else |
||||
|
unset OS_APPLICATION_CREDENTIAL_SECRET |
||||
|
_clearaccountconf SAVED_OS_APPLICATION_CREDENTIAL_SECRET |
||||
|
fi |
||||
|
|
||||
|
_debug "OS_USERNAME" "$OS_USERNAME" |
||||
|
if [ -n "$OS_USERNAME" ]; then |
||||
|
export OS_USERNAME |
||||
|
_saveaccountconf_mutable OS_USERNAME "$OS_USERNAME" |
||||
|
else |
||||
|
unset OS_USERNAME |
||||
|
_clearaccountconf SAVED_OS_USERNAME |
||||
|
fi |
||||
|
|
||||
|
_secure_debug "OS_PASSWORD" "$OS_PASSWORD" |
||||
|
if [ -n "$OS_PASSWORD" ]; then |
||||
|
export OS_PASSWORD |
||||
|
_saveaccountconf_mutable OS_PASSWORD "$OS_PASSWORD" |
||||
|
else |
||||
|
unset OS_PASSWORD |
||||
|
_clearaccountconf SAVED_OS_PASSWORD |
||||
|
fi |
||||
|
|
||||
|
_debug "OS_PROJECT_NAME" "$OS_PROJECT_NAME" |
||||
|
if [ -n "$OS_PROJECT_NAME" ]; then |
||||
|
export OS_PROJECT_NAME |
||||
|
_saveaccountconf_mutable OS_PROJECT_NAME "$OS_PROJECT_NAME" |
||||
|
else |
||||
|
unset OS_PROJECT_NAME |
||||
|
_clearaccountconf SAVED_OS_PROJECT_NAME |
||||
|
fi |
||||
|
|
||||
|
_debug "OS_PROJECT_ID" "$OS_PROJECT_ID" |
||||
|
if [ -n "$OS_PROJECT_ID" ]; then |
||||
|
export OS_PROJECT_ID |
||||
|
_saveaccountconf_mutable OS_PROJECT_ID "$OS_PROJECT_ID" |
||||
|
else |
||||
|
unset OS_PROJECT_ID |
||||
|
_clearaccountconf SAVED_OS_PROJECT_ID |
||||
|
fi |
||||
|
|
||||
|
_debug "OS_USER_DOMAIN_NAME" "$OS_USER_DOMAIN_NAME" |
||||
|
if [ -n "$OS_USER_DOMAIN_NAME" ]; then |
||||
|
export OS_USER_DOMAIN_NAME |
||||
|
_saveaccountconf_mutable OS_USER_DOMAIN_NAME "$OS_USER_DOMAIN_NAME" |
||||
|
else |
||||
|
unset OS_USER_DOMAIN_NAME |
||||
|
_clearaccountconf SAVED_OS_USER_DOMAIN_NAME |
||||
|
fi |
||||
|
|
||||
|
_debug "OS_USER_DOMAIN_ID" "$OS_USER_DOMAIN_ID" |
||||
|
if [ -n "$OS_USER_DOMAIN_ID" ]; then |
||||
|
export OS_USER_DOMAIN_ID |
||||
|
_saveaccountconf_mutable OS_USER_DOMAIN_ID "$OS_USER_DOMAIN_ID" |
||||
|
else |
||||
|
unset OS_USER_DOMAIN_ID |
||||
|
_clearaccountconf SAVED_OS_USER_DOMAIN_ID |
||||
|
fi |
||||
|
|
||||
|
_debug "OS_PROJECT_DOMAIN_NAME" "$OS_PROJECT_DOMAIN_NAME" |
||||
|
if [ -n "$OS_PROJECT_DOMAIN_NAME" ]; then |
||||
|
export OS_PROJECT_DOMAIN_NAME |
||||
|
_saveaccountconf_mutable OS_PROJECT_DOMAIN_NAME "$OS_PROJECT_DOMAIN_NAME" |
||||
|
else |
||||
|
unset OS_PROJECT_DOMAIN_NAME |
||||
|
_clearaccountconf SAVED_OS_PROJECT_DOMAIN_NAME |
||||
|
fi |
||||
|
|
||||
|
_debug "OS_PROJECT_DOMAIN_ID" "$OS_PROJECT_DOMAIN_ID" |
||||
|
if [ -n "$OS_PROJECT_DOMAIN_ID" ]; then |
||||
|
export OS_PROJECT_DOMAIN_ID |
||||
|
_saveaccountconf_mutable OS_PROJECT_DOMAIN_ID "$OS_PROJECT_DOMAIN_ID" |
||||
|
else |
||||
|
unset OS_PROJECT_DOMAIN_ID |
||||
|
_clearaccountconf SAVED_OS_PROJECT_DOMAIN_ID |
||||
|
fi |
||||
|
|
||||
|
if [ "$OS_AUTH_TYPE" = "v3applicationcredential" ]; then |
||||
|
# Application Credential auth |
||||
|
if [ -z "$OS_APPLICATION_CREDENTIAL_ID" ] || [ -z "$OS_APPLICATION_CREDENTIAL_SECRET" ]; then |
||||
|
_err "When using OpenStack application credentials, OS_APPLICATION_CREDENTIAL_ID" |
||||
|
_err "and OS_APPLICATION_CREDENTIAL_SECRET must be set." |
||||
|
_err "Please check your credentials and try again." |
||||
|
return 1 |
||||
|
fi |
||||
|
else |
||||
|
# Password auth |
||||
|
if [ -z "$OS_USERNAME" ] || [ -z "$OS_PASSWORD" ]; then |
||||
|
_err "OpenStack username or password not found." |
||||
|
_err "Please check your credentials and try again." |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
if [ -z "$OS_PROJECT_NAME" ] && [ -z "$OS_PROJECT_ID" ]; then |
||||
|
_err "When using password authentication, OS_PROJECT_NAME or" |
||||
|
_err "OS_PROJECT_ID must be set." |
||||
|
_err "Please check your credentials and try again." |
||||
|
return 1 |
||||
|
fi |
||||
|
fi |
||||
|
|
||||
|
return 0 |
||||
|
} |
||||
@ -0,0 +1,157 @@ |
|||||
|
#!/usr/bin/env sh |
||||
|
|
||||
|
# |
||||
|
#PORKBUN_API_KEY="pk1_0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" |
||||
|
#PORKBUN_SECRET_API_KEY="sk1_0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" |
||||
|
|
||||
|
PORKBUN_Api="https://porkbun.com/api/json/v3" |
||||
|
|
||||
|
######## Public functions ##################### |
||||
|
|
||||
|
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" |
||||
|
dns_porkbun_add() { |
||||
|
fulldomain=$1 |
||||
|
txtvalue=$2 |
||||
|
|
||||
|
PORKBUN_API_KEY="${PORKBUN_API_KEY:-$(_readaccountconf_mutable PORKBUN_API_KEY)}" |
||||
|
PORKBUN_SECRET_API_KEY="${PORKBUN_SECRET_API_KEY:-$(_readaccountconf_mutable PORKBUN_SECRET_API_KEY)}" |
||||
|
|
||||
|
if [ -z "$PORKBUN_API_KEY" ] || [ -z "$PORKBUN_SECRET_API_KEY" ]; then |
||||
|
PORKBUN_API_KEY='' |
||||
|
PORKBUN_SECRET_API_KEY='' |
||||
|
_err "You didn't specify a Porkbun api key and secret api key yet." |
||||
|
_err "You can get yours from here https://porkbun.com/account/api." |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
#save the credentials to the account conf file. |
||||
|
_saveaccountconf_mutable PORKBUN_API_KEY "$PORKBUN_API_KEY" |
||||
|
_saveaccountconf_mutable PORKBUN_SECRET_API_KEY "$PORKBUN_SECRET_API_KEY" |
||||
|
|
||||
|
_debug 'First detect the root zone' |
||||
|
if ! _get_root "$fulldomain"; then |
||||
|
return 1 |
||||
|
fi |
||||
|
_debug _sub_domain "$_sub_domain" |
||||
|
_debug _domain "$_domain" |
||||
|
|
||||
|
# For wildcard cert, the main root domain and the wildcard domain have the same txt subdomain name, so |
||||
|
# we can not use updating anymore. |
||||
|
# count=$(printf "%s\n" "$response" | _egrep_o "\"count\":[^,]*" | cut -d : -f 2) |
||||
|
# _debug count "$count" |
||||
|
# if [ "$count" = "0" ]; then |
||||
|
_info "Adding record" |
||||
|
if _porkbun_rest POST "dns/create/$_domain" "{\"name\":\"$_sub_domain\",\"type\":\"TXT\",\"content\":\"$txtvalue\",\"ttl\":120}"; then |
||||
|
if _contains "$response" '\"status\":"SUCCESS"'; then |
||||
|
_info "Added, OK" |
||||
|
return 0 |
||||
|
elif _contains "$response" "The record already exists"; then |
||||
|
_info "Already exists, OK" |
||||
|
return 0 |
||||
|
else |
||||
|
_err "Add txt record error. ($response)" |
||||
|
return 1 |
||||
|
fi |
||||
|
fi |
||||
|
_err "Add txt record error." |
||||
|
return 1 |
||||
|
|
||||
|
} |
||||
|
|
||||
|
#fulldomain txtvalue |
||||
|
dns_porkbun_rm() { |
||||
|
fulldomain=$1 |
||||
|
txtvalue=$2 |
||||
|
|
||||
|
PORKBUN_API_KEY="${PORKBUN_API_KEY:-$(_readaccountconf_mutable PORKBUN_API_KEY)}" |
||||
|
PORKBUN_SECRET_API_KEY="${PORKBUN_SECRET_API_KEY:-$(_readaccountconf_mutable PORKBUN_SECRET_API_KEY)}" |
||||
|
|
||||
|
_debug 'First detect the root zone' |
||||
|
if ! _get_root "$fulldomain"; then |
||||
|
return 1 |
||||
|
fi |
||||
|
_debug _sub_domain "$_sub_domain" |
||||
|
_debug _domain "$_domain" |
||||
|
|
||||
|
count=$(echo "$response" | _egrep_o "\"count\": *[^,]*" | cut -d : -f 2 | tr -d " ") |
||||
|
_debug count "$count" |
||||
|
if [ "$count" = "0" ]; then |
||||
|
_info "Don't need to remove." |
||||
|
else |
||||
|
record_id=$(echo "$response" | tr '{' '\n' | grep -- "$txtvalue" | cut -d, -f1 | cut -d: -f2 | tr -d \") |
||||
|
_debug "record_id" "$record_id" |
||||
|
if [ -z "$record_id" ]; then |
||||
|
_err "Can not get record id to remove." |
||||
|
return 1 |
||||
|
fi |
||||
|
if ! _porkbun_rest POST "dns/delete/$_domain/$record_id"; then |
||||
|
_err "Delete record error." |
||||
|
return 1 |
||||
|
fi |
||||
|
echo "$response" | tr -d " " | grep '\"status\":"SUCCESS"' >/dev/null |
||||
|
fi |
||||
|
|
||||
|
} |
||||
|
|
||||
|
#################### Private functions below ################################## |
||||
|
#_acme-challenge.www.domain.com |
||||
|
#returns |
||||
|
# _sub_domain=_acme-challenge.www |
||||
|
# _domain=domain.com |
||||
|
_get_root() { |
||||
|
domain=$1 |
||||
|
i=1 |
||||
|
while true; do |
||||
|
h=$(printf "%s" "$domain" | cut -d . -f $i-100) |
||||
|
_debug h "$h" |
||||
|
if [ -z "$h" ]; then |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
if _porkbun_rest POST "dns/retrieve/$h"; then |
||||
|
if _contains "$response" "\"status\":\"SUCCESS\""; then |
||||
|
_domain=$h |
||||
|
_sub_domain="$(echo "$fulldomain" | sed "s/\\.$_domain\$//")" |
||||
|
return 0 |
||||
|
else |
||||
|
_debug "Go to next level of $_domain" |
||||
|
fi |
||||
|
else |
||||
|
_debug "Go to next level of $_domain" |
||||
|
fi |
||||
|
i=$(_math "$i" + 1) |
||||
|
done |
||||
|
|
||||
|
return 1 |
||||
|
} |
||||
|
|
||||
|
_porkbun_rest() { |
||||
|
m=$1 |
||||
|
ep="$2" |
||||
|
data="$3" |
||||
|
_debug "$ep" |
||||
|
|
||||
|
api_key_trimmed=$(echo "$PORKBUN_API_KEY" | tr -d '"') |
||||
|
secret_api_key_trimmed=$(echo "$PORKBUN_SECRET_API_KEY" | tr -d '"') |
||||
|
|
||||
|
test -z "$data" && data="{" || data="$(echo $data | cut -d'}' -f1)," |
||||
|
data="$data\"apikey\":\"$api_key_trimmed\",\"secretapikey\":\"$secret_api_key_trimmed\"}" |
||||
|
|
||||
|
export _H1="Content-Type: application/json" |
||||
|
|
||||
|
if [ "$m" != "GET" ]; then |
||||
|
_debug data "$data" |
||||
|
response="$(_post "$data" "$PORKBUN_Api/$ep" "" "$m")" |
||||
|
else |
||||
|
response="$(_get "$PORKBUN_Api/$ep")" |
||||
|
fi |
||||
|
|
||||
|
_sleep 3 # prevent rate limit |
||||
|
|
||||
|
if [ "$?" != "0" ]; then |
||||
|
_err "error $ep" |
||||
|
return 1 |
||||
|
fi |
||||
|
_debug2 response "$response" |
||||
|
return 0 |
||||
|
} |
||||
@ -0,0 +1,156 @@ |
|||||
|
#!/usr/bin/env sh |
||||
|
|
||||
|
# Provider: RackCorp (www.rackcorp.com) |
||||
|
# Author: Stephen Dendtler (sdendtler@rackcorp.com) |
||||
|
# Report Bugs here: https://github.com/senjoo/acme.sh |
||||
|
# Alternate email contact: support@rackcorp.com |
||||
|
# |
||||
|
# You'll need an API key (Portal: ADMINISTRATION -> API) |
||||
|
# Set the environment variables as below: |
||||
|
# |
||||
|
# export RACKCORP_APIUUID="UUIDHERE" |
||||
|
# export RACKCORP_APISECRET="SECRETHERE" |
||||
|
# |
||||
|
|
||||
|
RACKCORP_API_ENDPOINT="https://api.rackcorp.net/api/rest/v2.4/json.php" |
||||
|
|
||||
|
######## Public functions ##################### |
||||
|
|
||||
|
dns_rackcorp_add() { |
||||
|
fulldomain="$1" |
||||
|
txtvalue="$2" |
||||
|
|
||||
|
_debug fulldomain="$fulldomain" |
||||
|
_debug txtvalue="$txtvalue" |
||||
|
|
||||
|
if ! _rackcorp_validate; then |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_debug "Searching for root zone" |
||||
|
if ! _get_root "$fulldomain"; then |
||||
|
return 1 |
||||
|
fi |
||||
|
_debug _lookup "$_lookup" |
||||
|
_debug _domain "$_domain" |
||||
|
|
||||
|
_info "Creating TXT record." |
||||
|
|
||||
|
if ! _rackcorp_api dns.record.create "\"name\":\"$_domain\",\"type\":\"TXT\",\"lookup\":\"$_lookup\",\"data\":\"$txtvalue\",\"ttl\":300"; then |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
return 0 |
||||
|
} |
||||
|
|
||||
|
#Usage: fulldomain txtvalue |
||||
|
#Remove the txt record after validation. |
||||
|
dns_rackcorp_rm() { |
||||
|
fulldomain=$1 |
||||
|
txtvalue=$2 |
||||
|
|
||||
|
_debug fulldomain="$fulldomain" |
||||
|
_debug txtvalue="$txtvalue" |
||||
|
|
||||
|
if ! _rackcorp_validate; then |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_debug "Searching for root zone" |
||||
|
if ! _get_root "$fulldomain"; then |
||||
|
return 1 |
||||
|
fi |
||||
|
_debug _lookup "$_lookup" |
||||
|
_debug _domain "$_domain" |
||||
|
|
||||
|
_info "Creating TXT record." |
||||
|
|
||||
|
if ! _rackcorp_api dns.record.delete "\"name\":\"$_domain\",\"type\":\"TXT\",\"lookup\":\"$_lookup\",\"data\":\"$txtvalue\""; then |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
return 0 |
||||
|
} |
||||
|
|
||||
|
#################### Private functions below ################################## |
||||
|
#_acme-challenge.domain.com |
||||
|
#returns |
||||
|
# _lookup=_acme-challenge |
||||
|
# _domain=domain.com |
||||
|
_get_root() { |
||||
|
domain=$1 |
||||
|
i=1 |
||||
|
p=1 |
||||
|
if ! _rackcorp_api dns.domain.getall "\"name\":\"$domain\""; then |
||||
|
return 1 |
||||
|
fi |
||||
|
while true; do |
||||
|
h=$(printf "%s" "$domain" | cut -d . -f $i-100) |
||||
|
_debug searchhost "$h" |
||||
|
if [ -z "$h" ]; then |
||||
|
_err "Could not find domain for record $domain in RackCorp using the provided credentials" |
||||
|
#not valid |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_rackcorp_api dns.domain.getall "\"exactName\":\"$h\"" |
||||
|
|
||||
|
if _contains "$response" "\"matches\":1"; then |
||||
|
if _contains "$response" "\"name\":\"$h\""; then |
||||
|
_lookup=$(printf "%s" "$domain" | cut -d . -f 1-$p) |
||||
|
_domain="$h" |
||||
|
return 0 |
||||
|
fi |
||||
|
fi |
||||
|
p=$i |
||||
|
i=$(_math "$i" + 1) |
||||
|
done |
||||
|
|
||||
|
return 1 |
||||
|
} |
||||
|
|
||||
|
_rackcorp_validate() { |
||||
|
RACKCORP_APIUUID="${RACKCORP_APIUUID:-$(_readaccountconf_mutable RACKCORP_APIUUID)}" |
||||
|
if [ -z "$RACKCORP_APIUUID" ]; then |
||||
|
RACKCORP_APIUUID="" |
||||
|
_err "You require a RackCorp API UUID (export RACKCORP_APIUUID=\"<api uuid>\")" |
||||
|
_err "Please login to the portal and create an API key and try again." |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_saveaccountconf_mutable RACKCORP_APIUUID "$RACKCORP_APIUUID" |
||||
|
|
||||
|
RACKCORP_APISECRET="${RACKCORP_APISECRET:-$(_readaccountconf_mutable RACKCORP_APISECRET)}" |
||||
|
if [ -z "$RACKCORP_APISECRET" ]; then |
||||
|
RACKCORP_APISECRET="" |
||||
|
_err "You require a RackCorp API secret (export RACKCORP_APISECRET=\"<api secret>\")" |
||||
|
_err "Please login to the portal and create an API key and try again." |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_saveaccountconf_mutable RACKCORP_APISECRET "$RACKCORP_APISECRET" |
||||
|
|
||||
|
return 0 |
||||
|
} |
||||
|
_rackcorp_api() { |
||||
|
_rackcorpcmd=$1 |
||||
|
_rackcorpinputdata=$2 |
||||
|
_debug cmd "$_rackcorpcmd $_rackcorpinputdata" |
||||
|
|
||||
|
export _H1="Accept: application/json" |
||||
|
response="$(_post "{\"APIUUID\":\"$RACKCORP_APIUUID\",\"APISECRET\":\"$RACKCORP_APISECRET\",\"cmd\":\"$_rackcorpcmd\",$_rackcorpinputdata}" "$RACKCORP_API_ENDPOINT" "" "POST")" |
||||
|
|
||||
|
if [ "$?" != "0" ]; then |
||||
|
_err "error $response" |
||||
|
return 1 |
||||
|
fi |
||||
|
_debug2 response "$response" |
||||
|
if _contains "$response" "\"code\":\"OK\""; then |
||||
|
_debug code "OK" |
||||
|
else |
||||
|
_debug code "FAILED" |
||||
|
response="" |
||||
|
return 1 |
||||
|
fi |
||||
|
return 0 |
||||
|
} |
||||
@ -0,0 +1,176 @@ |
|||||
|
#!/usr/bin/env sh |
||||
|
|
||||
|
# Scaleway API |
||||
|
# https://developers.scaleway.com/en/products/domain/dns/api/ |
||||
|
# |
||||
|
# Requires Scaleway API token set in SCALEWAY_API_TOKEN |
||||
|
|
||||
|
######## Public functions ##################### |
||||
|
|
||||
|
SCALEWAY_API="https://api.scaleway.com/domain/v2beta1" |
||||
|
|
||||
|
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" |
||||
|
dns_scaleway_add() { |
||||
|
fulldomain=$1 |
||||
|
txtvalue=$2 |
||||
|
|
||||
|
if ! _scaleway_check_config; then |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_debug "First detect the root zone" |
||||
|
if ! _get_root "$fulldomain"; then |
||||
|
_err "invalid domain" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_debug _sub_domain "$_sub_domain" |
||||
|
_debug _domain "$_domain" |
||||
|
|
||||
|
_info "Adding record" |
||||
|
_scaleway_create_TXT_record "$_domain" "$_sub_domain" "$txtvalue" |
||||
|
if _contains "$response" "records"; then |
||||
|
return 0 |
||||
|
else |
||||
|
_err error "$response" |
||||
|
return 1 |
||||
|
fi |
||||
|
_info "Record added." |
||||
|
|
||||
|
return 0 |
||||
|
} |
||||
|
|
||||
|
dns_scaleway_rm() { |
||||
|
fulldomain=$1 |
||||
|
txtvalue=$2 |
||||
|
|
||||
|
if ! _scaleway_check_config; then |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_debug "First detect the root zone" |
||||
|
if ! _get_root "$fulldomain"; then |
||||
|
_err "invalid domain" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_debug _sub_domain "$_sub_domain" |
||||
|
_debug _domain "$_domain" |
||||
|
|
||||
|
_info "Deleting record" |
||||
|
_scaleway_delete_TXT_record "$_domain" "$_sub_domain" "$txtvalue" |
||||
|
if _contains "$response" "records"; then |
||||
|
return 0 |
||||
|
else |
||||
|
_err error "$response" |
||||
|
return 1 |
||||
|
fi |
||||
|
_info "Record deleted." |
||||
|
|
||||
|
return 0 |
||||
|
} |
||||
|
|
||||
|
#################### Private functions below ################################## |
||||
|
|
||||
|
_scaleway_check_config() { |
||||
|
SCALEWAY_API_TOKEN="${SCALEWAY_API_TOKEN:-$(_readaccountconf_mutable SCALEWAY_API_TOKEN)}" |
||||
|
if [ -z "$SCALEWAY_API_TOKEN" ]; then |
||||
|
_err "No API key specified for Scaleway API." |
||||
|
_err "Create your key and export it as SCALEWAY_API_TOKEN" |
||||
|
return 1 |
||||
|
fi |
||||
|
if ! _scaleway_rest GET "dns-zones"; then |
||||
|
_err "Invalid API key specified for Scaleway API." |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_saveaccountconf_mutable SCALEWAY_API_TOKEN "$SCALEWAY_API_TOKEN" |
||||
|
|
||||
|
return 0 |
||||
|
} |
||||
|
|
||||
|
#_acme-challenge.www.domain.com |
||||
|
#returns |
||||
|
# _sub_domain=_acme-challenge.www |
||||
|
# _domain=domain.com |
||||
|
_get_root() { |
||||
|
domain=$1 |
||||
|
i=1 |
||||
|
p=1 |
||||
|
while true; do |
||||
|
h=$(printf "%s" "$domain" | cut -d . -f $i-100) |
||||
|
if [ -z "$h" ]; then |
||||
|
#not valid |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_scaleway_rest GET "dns-zones/$h/records" |
||||
|
|
||||
|
if ! _contains "$response" "subdomain not found" >/dev/null; then |
||||
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) |
||||
|
_domain="$h" |
||||
|
return 0 |
||||
|
fi |
||||
|
p=$i |
||||
|
i=$(_math "$i" + 1) |
||||
|
done |
||||
|
_err "Unable to retrive DNS zone matching this domain" |
||||
|
return 1 |
||||
|
} |
||||
|
|
||||
|
# this function add a TXT record |
||||
|
_scaleway_create_TXT_record() { |
||||
|
txt_zone=$1 |
||||
|
txt_name=$2 |
||||
|
txt_value=$3 |
||||
|
|
||||
|
_scaleway_rest PATCH "dns-zones/$txt_zone/records" "{\"return_all_records\":false,\"changes\":[{\"add\":{\"records\":[{\"name\":\"$txt_name\",\"data\":\"$txt_value\",\"type\":\"TXT\",\"ttl\":60}]}}]}" |
||||
|
|
||||
|
if _contains "$response" "records"; then |
||||
|
return 0 |
||||
|
else |
||||
|
_err "error1 $response" |
||||
|
return 1 |
||||
|
fi |
||||
|
} |
||||
|
|
||||
|
# this function delete a TXT record based on name and content |
||||
|
_scaleway_delete_TXT_record() { |
||||
|
txt_zone=$1 |
||||
|
txt_name=$2 |
||||
|
txt_value=$3 |
||||
|
|
||||
|
_scaleway_rest PATCH "dns-zones/$txt_zone/records" "{\"return_all_records\":false,\"changes\":[{\"delete\":{\"id_fields\":{\"name\":\"$txt_name\",\"data\":\"$txt_value\",\"type\":\"TXT\"}}}]}" |
||||
|
|
||||
|
if _contains "$response" "records"; then |
||||
|
return 0 |
||||
|
else |
||||
|
_err "error2 $response" |
||||
|
return 1 |
||||
|
fi |
||||
|
} |
||||
|
|
||||
|
_scaleway_rest() { |
||||
|
m=$1 |
||||
|
ep="$2" |
||||
|
data="$3" |
||||
|
_debug "$ep" |
||||
|
_scaleway_url="$SCALEWAY_API/$ep" |
||||
|
_debug2 _scaleway_url "$_scaleway_url" |
||||
|
export _H1="x-auth-token: $SCALEWAY_API_TOKEN" |
||||
|
export _H2="Accept: application/json" |
||||
|
export _H3="Content-Type: application/json" |
||||
|
|
||||
|
if [ "$data" ] || [ "$m" != "GET" ]; then |
||||
|
_debug data "$data" |
||||
|
response="$(_post "$data" "$_scaleway_url" "" "$m")" |
||||
|
else |
||||
|
response="$(_get "$_scaleway_url")" |
||||
|
fi |
||||
|
if [ "$?" != "0" ] || _contains "$response" "denied_authentication" || _contains "$response" "Method not allowed" || _contains "$response" "json parse error: unexpected EOF"; then |
||||
|
_err "error $response" |
||||
|
return 1 |
||||
|
fi |
||||
|
_debug2 response "$response" |
||||
|
return 0 |
||||
|
} |
||||
@ -0,0 +1,263 @@ |
|||||
|
#!/usr/bin/env sh |
||||
|
|
||||
|
# API-integration for Simply.com (https://www.simply.com) |
||||
|
|
||||
|
#SIMPLY_AccountName="accountname" |
||||
|
#SIMPLY_ApiKey="apikey" |
||||
|
# |
||||
|
#SIMPLY_Api="https://api.simply.com/1/[ACCOUNTNAME]/[APIKEY]" |
||||
|
SIMPLY_Api_Default="https://api.simply.com/1" |
||||
|
|
||||
|
#This is used for determining success of REST call |
||||
|
SIMPLY_SUCCESS_CODE='"status":200' |
||||
|
|
||||
|
######## Public functions ##################### |
||||
|
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" |
||||
|
dns_simply_add() { |
||||
|
fulldomain=$1 |
||||
|
txtvalue=$2 |
||||
|
|
||||
|
if ! _simply_load_config; then |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_simply_save_config |
||||
|
|
||||
|
_debug "First detect the root zone" |
||||
|
if ! _get_root "$fulldomain"; then |
||||
|
_err "invalid domain" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_debug _sub_domain "$_sub_domain" |
||||
|
_debug _domain "$_domain" |
||||
|
|
||||
|
_info "Adding record" |
||||
|
|
||||
|
if ! _simply_add_record "$_domain" "$_sub_domain" "$txtvalue"; then |
||||
|
_err "Could not add DNS record" |
||||
|
return 1 |
||||
|
fi |
||||
|
return 0 |
||||
|
} |
||||
|
|
||||
|
dns_simply_rm() { |
||||
|
fulldomain=$1 |
||||
|
txtvalue=$2 |
||||
|
|
||||
|
if ! _simply_load_config; then |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_simply_save_config |
||||
|
|
||||
|
_debug "Find the DNS zone" |
||||
|
|
||||
|
if ! _get_root "$fulldomain"; then |
||||
|
_err "invalid domain" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_debug _sub_domain "$_sub_domain" |
||||
|
_debug _domain "$_domain" |
||||
|
_debug txtvalue "$txtvalue" |
||||
|
|
||||
|
_info "Getting all existing records" |
||||
|
|
||||
|
if ! _simply_get_all_records "$_domain"; then |
||||
|
_err "invalid domain" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
records=$(echo "$response" | tr '{' "\n" | grep 'record_id\|type\|data\|\name' | sed 's/\"record_id/;\"record_id/' | tr "\n" ' ' | tr -d ' ' | tr ';' ' ') |
||||
|
|
||||
|
nr_of_deleted_records=0 |
||||
|
_info "Fetching txt record" |
||||
|
|
||||
|
for record in $records; do |
||||
|
_debug record "$record" |
||||
|
|
||||
|
record_data=$(echo "$record" | sed -n "s/.*\"data\":\"\([^\"]*\)\".*/\1/p") |
||||
|
record_type=$(echo "$record" | sed -n "s/.*\"type\":\"\([^\"]*\)\".*/\1/p") |
||||
|
|
||||
|
_debug2 record_data "$record_data" |
||||
|
_debug2 record_type "$record_type" |
||||
|
|
||||
|
if [ "$record_data" = "$txtvalue" ] && [ "$record_type" = "TXT" ]; then |
||||
|
|
||||
|
record_id=$(echo "$record" | cut -d "," -f 1 | grep "record_id" | cut -d ":" -f 2) |
||||
|
|
||||
|
_info "Deleting record $record" |
||||
|
_debug2 record_id "$record_id" |
||||
|
|
||||
|
if [ "$record_id" -gt 0 ]; then |
||||
|
|
||||
|
if ! _simply_delete_record "$_domain" "$_sub_domain" "$record_id"; then |
||||
|
_err "Record with id $record_id could not be deleted" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
nr_of_deleted_records=1 |
||||
|
break |
||||
|
else |
||||
|
_err "Fetching record_id could not be done, this should not happen, exiting function. Failing record is $record" |
||||
|
break |
||||
|
fi |
||||
|
fi |
||||
|
|
||||
|
done |
||||
|
|
||||
|
if [ "$nr_of_deleted_records" -eq 0 ]; then |
||||
|
_err "No record deleted, the DNS record needs to be removed manually." |
||||
|
else |
||||
|
_info "Deleted $nr_of_deleted_records record" |
||||
|
fi |
||||
|
|
||||
|
return 0 |
||||
|
} |
||||
|
|
||||
|
#################### Private functions below ################################## |
||||
|
|
||||
|
_simply_load_config() { |
||||
|
SIMPLY_Api="${SIMPLY_Api:-$(_readaccountconf_mutable SIMPLY_Api)}" |
||||
|
SIMPLY_AccountName="${SIMPLY_AccountName:-$(_readaccountconf_mutable SIMPLY_AccountName)}" |
||||
|
SIMPLY_ApiKey="${SIMPLY_ApiKey:-$(_readaccountconf_mutable SIMPLY_ApiKey)}" |
||||
|
|
||||
|
if [ -z "$SIMPLY_Api" ]; then |
||||
|
SIMPLY_Api="$SIMPLY_Api_Default" |
||||
|
fi |
||||
|
|
||||
|
if [ -z "$SIMPLY_AccountName" ] || [ -z "$SIMPLY_ApiKey" ]; then |
||||
|
SIMPLY_AccountName="" |
||||
|
SIMPLY_ApiKey="" |
||||
|
|
||||
|
_err "A valid Simply API account and apikey not provided." |
||||
|
_err "Please provide a valid API user and try again." |
||||
|
|
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
return 0 |
||||
|
} |
||||
|
|
||||
|
_simply_save_config() { |
||||
|
if [ "$SIMPLY_Api" != "$SIMPLY_Api_Default" ]; then |
||||
|
_saveaccountconf_mutable SIMPLY_Api "$SIMPLY_Api" |
||||
|
fi |
||||
|
_saveaccountconf_mutable SIMPLY_AccountName "$SIMPLY_AccountName" |
||||
|
_saveaccountconf_mutable SIMPLY_ApiKey "$SIMPLY_ApiKey" |
||||
|
} |
||||
|
|
||||
|
_simply_get_all_records() { |
||||
|
domain=$1 |
||||
|
|
||||
|
if ! _simply_rest GET "my/products/$domain/dns/records/"; then |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
return 0 |
||||
|
} |
||||
|
|
||||
|
_get_root() { |
||||
|
domain=$1 |
||||
|
i=2 |
||||
|
p=1 |
||||
|
while true; do |
||||
|
h=$(printf "%s" "$domain" | cut -d . -f $i-100) |
||||
|
if [ -z "$h" ]; then |
||||
|
#not valid |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
if ! _simply_rest GET "my/products/$h/dns/"; then |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
if ! _contains "$response" "$SIMPLY_SUCCESS_CODE"; then |
||||
|
_debug "$h not found" |
||||
|
else |
||||
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) |
||||
|
_domain="$h" |
||||
|
return 0 |
||||
|
fi |
||||
|
p="$i" |
||||
|
i=$(_math "$i" + 1) |
||||
|
done |
||||
|
return 1 |
||||
|
} |
||||
|
|
||||
|
_simply_add_record() { |
||||
|
domain=$1 |
||||
|
sub_domain=$2 |
||||
|
txtval=$3 |
||||
|
|
||||
|
data="{\"name\": \"$sub_domain\", \"type\":\"TXT\", \"data\": \"$txtval\", \"priority\":0, \"ttl\": 3600}" |
||||
|
|
||||
|
if ! _simply_rest POST "my/products/$domain/dns/records/" "$data"; then |
||||
|
_err "Adding record not successfull!" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
if ! _contains "$response" "$SIMPLY_SUCCESS_CODE"; then |
||||
|
_err "Call to API not sucessfull, see below message for more details" |
||||
|
_err "$response" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
return 0 |
||||
|
} |
||||
|
|
||||
|
_simply_delete_record() { |
||||
|
domain=$1 |
||||
|
sub_domain=$2 |
||||
|
record_id=$3 |
||||
|
|
||||
|
_debug record_id "Delete record with id $record_id" |
||||
|
|
||||
|
if ! _simply_rest DELETE "my/products/$domain/dns/records/$record_id/"; then |
||||
|
_err "Deleting record not successfull!" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
if ! _contains "$response" "$SIMPLY_SUCCESS_CODE"; then |
||||
|
_err "Call to API not sucessfull, see below message for more details" |
||||
|
_err "$response" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
return 0 |
||||
|
} |
||||
|
|
||||
|
_simply_rest() { |
||||
|
m=$1 |
||||
|
ep="$2" |
||||
|
data="$3" |
||||
|
|
||||
|
_debug2 data "$data" |
||||
|
_debug2 ep "$ep" |
||||
|
_debug2 m "$m" |
||||
|
|
||||
|
export _H1="Content-Type: application/json" |
||||
|
|
||||
|
if [ "$m" != "GET" ]; then |
||||
|
response="$(_post "$data" "$SIMPLY_Api/$SIMPLY_AccountName/$SIMPLY_ApiKey/$ep" "" "$m")" |
||||
|
else |
||||
|
response="$(_get "$SIMPLY_Api/$SIMPLY_AccountName/$SIMPLY_ApiKey/$ep")" |
||||
|
fi |
||||
|
|
||||
|
if [ "$?" != "0" ]; then |
||||
|
_err "error $ep" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
response="$(echo "$response" | _normalizeJson)" |
||||
|
|
||||
|
_debug2 response "$response" |
||||
|
|
||||
|
if _contains "$response" "Invalid account authorization"; then |
||||
|
_err "It seems that your api key or accountnumber is not correct." |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
return 0 |
||||
|
} |
||||
@ -0,0 +1,160 @@ |
|||||
|
#!/usr/bin/env sh |
||||
|
|
||||
|
# united-domains Reselling (https://www.ud-reselling.com/) DNS API |
||||
|
# Author: Andreas Scherer (https://github.com/andischerer) |
||||
|
# Created: 2021-02-01 |
||||
|
# |
||||
|
# Set the environment variables as below: |
||||
|
# |
||||
|
# export UDR_USER="your_username_goes_here" |
||||
|
# export UDR_PASS="some_password_goes_here" |
||||
|
# |
||||
|
|
||||
|
UDR_API="https://api.domainreselling.de/api/call.cgi" |
||||
|
UDR_TTL="30" |
||||
|
|
||||
|
######## Public functions ##################### |
||||
|
|
||||
|
#Usage: add _acme-challenge.www.domain.com "some_long_string_of_characters_go_here_from_lets_encrypt" |
||||
|
dns_udr_add() { |
||||
|
fulldomain=$1 |
||||
|
txtvalue=$2 |
||||
|
|
||||
|
UDR_USER="${UDR_USER:-$(_readaccountconf_mutable UDR_USER)}" |
||||
|
UDR_PASS="${UDR_PASS:-$(_readaccountconf_mutable UDR_PASS)}" |
||||
|
if [ -z "$UDR_USER" ] || [ -z "$UDR_PASS" ]; then |
||||
|
UDR_USER="" |
||||
|
UDR_PASS="" |
||||
|
_err "You didn't specify an UD-Reselling username and password yet" |
||||
|
return 1 |
||||
|
fi |
||||
|
# save the username and password to the account conf file. |
||||
|
_saveaccountconf_mutable UDR_USER "$UDR_USER" |
||||
|
_saveaccountconf_mutable UDR_PASS "$UDR_PASS" |
||||
|
_debug "First detect the root zone" |
||||
|
if ! _get_root "$fulldomain"; then |
||||
|
_err "invalid domain" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_debug _dnszone "${_dnszone}" |
||||
|
|
||||
|
_debug "Getting txt records" |
||||
|
if ! _udr_rest "QueryDNSZoneRRList" "dnszone=${_dnszone}"; then |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
rr="${fulldomain}. ${UDR_TTL} IN TXT ${txtvalue}" |
||||
|
_debug resource_record "${rr}" |
||||
|
if _contains "$response" "$rr" >/dev/null; then |
||||
|
_err "Error, it would appear that this record already exists. Please review existing TXT records for this domain." |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_info "Adding record" |
||||
|
if ! _udr_rest "UpdateDNSZone" "dnszone=${_dnszone}&addrr0=${rr}"; then |
||||
|
_err "Adding the record did not succeed, please verify/check." |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_info "Added, OK" |
||||
|
return 0 |
||||
|
} |
||||
|
|
||||
|
dns_udr_rm() { |
||||
|
fulldomain=$1 |
||||
|
txtvalue=$2 |
||||
|
|
||||
|
UDR_USER="${UDR_USER:-$(_readaccountconf_mutable UDR_USER)}" |
||||
|
UDR_PASS="${UDR_PASS:-$(_readaccountconf_mutable UDR_PASS)}" |
||||
|
if [ -z "$UDR_USER" ] || [ -z "$UDR_PASS" ]; then |
||||
|
UDR_USER="" |
||||
|
UDR_PASS="" |
||||
|
_err "You didn't specify an UD-Reselling username and password yet" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_debug "First detect the root zone" |
||||
|
if ! _get_root "$fulldomain"; then |
||||
|
_err "invalid domain" |
||||
|
return 1 |
||||
|
fi |
||||
|
_debug _dnszone "${_dnszone}" |
||||
|
|
||||
|
_debug "Getting txt records" |
||||
|
if ! _udr_rest "QueryDNSZoneRRList" "dnszone=${_dnszone}"; then |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
rr="${fulldomain}. ${UDR_TTL} IN TXT ${txtvalue}" |
||||
|
_debug resource_record "${rr}" |
||||
|
if _contains "$response" "$rr" >/dev/null; then |
||||
|
if ! _udr_rest "UpdateDNSZone" "dnszone=${_dnszone}&delrr0=${rr}"; then |
||||
|
_err "Deleting the record did not succeed, please verify/check." |
||||
|
return 1 |
||||
|
fi |
||||
|
_info "Removed, OK" |
||||
|
return 0 |
||||
|
else |
||||
|
_info "Text record is not present, will not delete anything." |
||||
|
return 0 |
||||
|
fi |
||||
|
} |
||||
|
|
||||
|
#################### Private functions below ################################## |
||||
|
#_acme-challenge.www.domain.com |
||||
|
#returns |
||||
|
# _sub_domain=_acme-challenge.www |
||||
|
# _domain=domain.com |
||||
|
_get_root() { |
||||
|
domain=$1 |
||||
|
i=1 |
||||
|
|
||||
|
if ! _udr_rest "QueryDNSZoneList" ""; then |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
while true; do |
||||
|
h=$(printf "%s" "$domain" | cut -d . -f $i-100) |
||||
|
_debug h "$h" |
||||
|
|
||||
|
if [ -z "$h" ]; then |
||||
|
#not valid |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
if _contains "${response}" "${h}." >/dev/null; then |
||||
|
_dnszone=$(echo "$response" | _egrep_o "${h}") |
||||
|
if [ "$_dnszone" ]; then |
||||
|
return 0 |
||||
|
fi |
||||
|
return 1 |
||||
|
fi |
||||
|
i=$(_math "$i" + 1) |
||||
|
done |
||||
|
return 1 |
||||
|
} |
||||
|
|
||||
|
_udr_rest() { |
||||
|
if [ -n "$2" ]; then |
||||
|
data="command=$1&$2" |
||||
|
else |
||||
|
data="command=$1" |
||||
|
fi |
||||
|
|
||||
|
_debug data "${data}" |
||||
|
response="$(_post "${data}" "${UDR_API}?s_login=${UDR_USER}&s_pw=${UDR_PASS}" "" "POST")" |
||||
|
|
||||
|
_code=$(echo "$response" | _egrep_o "code = ([0-9]+)" | _head_n 1 | cut -d = -f 2 | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//') |
||||
|
_description=$(echo "$response" | _egrep_o "description = .*" | _head_n 1 | cut -d = -f 2 | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//') |
||||
|
|
||||
|
_debug response_code "$_code" |
||||
|
_debug response_description "$_description" |
||||
|
|
||||
|
if [ ! "$_code" = "200" ]; then |
||||
|
_err "DNS-API-Error: $_description" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
return 0 |
||||
|
} |
||||
@ -0,0 +1,158 @@ |
|||||
|
#!/usr/bin/env sh |
||||
|
|
||||
|
# bug reports to stepan@plyask.in |
||||
|
|
||||
|
# |
||||
|
# export VEESP_User="username" |
||||
|
# export VEESP_Password="password" |
||||
|
|
||||
|
VEESP_Api="https://secure.veesp.com/api" |
||||
|
|
||||
|
######## Public functions ##################### |
||||
|
|
||||
|
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" |
||||
|
dns_veesp_add() { |
||||
|
fulldomain=$1 |
||||
|
txtvalue=$2 |
||||
|
|
||||
|
VEESP_Password="${VEESP_Password:-$(_readaccountconf_mutable VEESP_Password)}" |
||||
|
VEESP_User="${VEESP_User:-$(_readaccountconf_mutable VEESP_User)}" |
||||
|
VEESP_auth=$(printf "%s" "$VEESP_User:$VEESP_Password" | _base64) |
||||
|
|
||||
|
if [ -z "$VEESP_Password" ] || [ -z "$VEESP_User" ]; then |
||||
|
VEESP_Password="" |
||||
|
VEESP_User="" |
||||
|
_err "You don't specify veesp api key and email yet." |
||||
|
_err "Please create you key and try again." |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
#save the api key and email to the account conf file. |
||||
|
_saveaccountconf_mutable VEESP_Password "$VEESP_Password" |
||||
|
_saveaccountconf_mutable VEESP_User "$VEESP_User" |
||||
|
|
||||
|
_debug "First detect the root zone" |
||||
|
if ! _get_root "$fulldomain"; then |
||||
|
_err "invalid domain" |
||||
|
return 1 |
||||
|
fi |
||||
|
_debug _domain_id "$_domain_id" |
||||
|
_debug _sub_domain "$_sub_domain" |
||||
|
_debug _domain "$_domain" |
||||
|
|
||||
|
_info "Adding record" |
||||
|
if VEESP_rest POST "service/$_service_id/dns/$_domain_id/records" "{\"name\":\"$fulldomain\",\"ttl\":1,\"priority\":0,\"type\":\"TXT\",\"content\":\"$txtvalue\"}"; then |
||||
|
if _contains "$response" "\"success\":true"; then |
||||
|
_info "Added" |
||||
|
#todo: check if the record takes effect |
||||
|
return 0 |
||||
|
else |
||||
|
_err "Add txt record error." |
||||
|
return 1 |
||||
|
fi |
||||
|
fi |
||||
|
} |
||||
|
|
||||
|
# Usage: fulldomain txtvalue |
||||
|
# Used to remove the txt record after validation |
||||
|
dns_veesp_rm() { |
||||
|
fulldomain=$1 |
||||
|
txtvalue=$2 |
||||
|
|
||||
|
VEESP_Password="${VEESP_Password:-$(_readaccountconf_mutable VEESP_Password)}" |
||||
|
VEESP_User="${VEESP_User:-$(_readaccountconf_mutable VEESP_User)}" |
||||
|
VEESP_auth=$(printf "%s" "$VEESP_User:$VEESP_Password" | _base64) |
||||
|
|
||||
|
_debug "First detect the root zone" |
||||
|
if ! _get_root "$fulldomain"; then |
||||
|
_err "invalid domain" |
||||
|
return 1 |
||||
|
fi |
||||
|
_debug _domain_id "$_domain_id" |
||||
|
_debug _sub_domain "$_sub_domain" |
||||
|
_debug _domain "$_domain" |
||||
|
|
||||
|
_debug "Getting txt records" |
||||
|
VEESP_rest GET "service/$_service_id/dns/$_domain_id" |
||||
|
|
||||
|
count=$(printf "%s\n" "$response" | _egrep_o "\"type\":\"TXT\",\"content\":\".\"$txtvalue.\"\"" | wc -l | tr -d " ") |
||||
|
_debug count "$count" |
||||
|
if [ "$count" = "0" ]; then |
||||
|
_info "Don't need to remove." |
||||
|
else |
||||
|
record_id=$(printf "%s\n" "$response" | _egrep_o "{\"id\":[^}]*\"type\":\"TXT\",\"content\":\".\"$txtvalue.\"\"" | cut -d\" -f4) |
||||
|
_debug "record_id" "$record_id" |
||||
|
if [ -z "$record_id" ]; then |
||||
|
_err "Can not get record id to remove." |
||||
|
return 1 |
||||
|
fi |
||||
|
if ! VEESP_rest DELETE "service/$_service_id/dns/$_domain_id/records/$record_id"; then |
||||
|
_err "Delete record error." |
||||
|
return 1 |
||||
|
fi |
||||
|
_contains "$response" "\"success\":true" |
||||
|
fi |
||||
|
} |
||||
|
|
||||
|
#################### Private functions below ################################## |
||||
|
#_acme-challenge.www.domain.com |
||||
|
#returns |
||||
|
# _sub_domain=_acme-challenge.www |
||||
|
# _domain=domain.com |
||||
|
# _domain_id=sdjkglgdfewsdfg |
||||
|
_get_root() { |
||||
|
domain=$1 |
||||
|
i=2 |
||||
|
p=1 |
||||
|
if ! VEESP_rest GET "dns"; then |
||||
|
return 1 |
||||
|
fi |
||||
|
while true; do |
||||
|
h=$(printf "%s" "$domain" | cut -d . -f $i-100) |
||||
|
_debug h "$h" |
||||
|
if [ -z "$h" ]; then |
||||
|
#not valid |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
if _contains "$response" "\"name\":\"$h\""; then |
||||
|
_domain_id=$(printf "%s\n" "$response" | _egrep_o "\"domain_id\":[^,]*,\"name\":\"$h\"" | cut -d : -f 2 | cut -d , -f 1 | cut -d '"' -f 2) |
||||
|
_debug _domain_id "$_domain_id" |
||||
|
_service_id=$(printf "%s\n" "$response" | _egrep_o "\"name\":\"$h\",\"service_id\":[^}]*" | cut -d : -f 3 | cut -d '"' -f 2) |
||||
|
_debug _service_id "$_service_id" |
||||
|
if [ "$_domain_id" ]; then |
||||
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) |
||||
|
_domain="$h" |
||||
|
return 0 |
||||
|
fi |
||||
|
return 1 |
||||
|
fi |
||||
|
p=$i |
||||
|
i=$(_math "$i" + 1) |
||||
|
done |
||||
|
return 1 |
||||
|
} |
||||
|
|
||||
|
VEESP_rest() { |
||||
|
m=$1 |
||||
|
ep="$2" |
||||
|
data="$3" |
||||
|
_debug "$ep" |
||||
|
|
||||
|
export _H1="Accept: application/json" |
||||
|
export _H2="Authorization: Basic $VEESP_auth" |
||||
|
if [ "$m" != "GET" ]; then |
||||
|
_debug data "$data" |
||||
|
export _H3="Content-Type: application/json" |
||||
|
response="$(_post "$data" "$VEESP_Api/$ep" "" "$m")" |
||||
|
else |
||||
|
response="$(_get "$VEESP_Api/$ep")" |
||||
|
fi |
||||
|
|
||||
|
if [ "$?" != "0" ]; then |
||||
|
_err "error $ep" |
||||
|
return 1 |
||||
|
fi |
||||
|
_debug2 response "$response" |
||||
|
return 0 |
||||
|
} |
||||
Some files were not shown because too many files changed in this diff
Write
Preview
Loading…
Cancel
Save
Reference in new issue