Compare commits

...

165 Commits

Author SHA1 Message Date
An | Anton Röhm
e592bb2689 Merge 65bd3d67b4 into 15197455f2 2025-10-14 13:51:35 +00:00
An
65bd3d67b4 nanelo dns: minor log improvements 2025-10-14 15:51:28 +02:00
neil
15197455f2 Merge pull request #5061 from b1n23/dev
Add deployhook for CacheFly, Edgio and Netlify
2025-10-11 12:47:40 +02:00
neil
2584f09083 Merge pull request #6543 from szhu25/add-to-list
Add "Profile" column to --list command
2025-10-06 12:30:49 +01:00
Steven Zhu
d439933b52 add Profile column to --list output
This commit adds a new "Profile" column to the output of the `--list` command.

The column displays the value of the `Le_Certificate_Profile` variable stored in each domain's respective configuration file. If a profile is not set for a certificate, the column is left empty.

This enhances the utility of the list command by providing more at-a-glance information about each certificate's configuration, which is particularly useful for CAs that support different certificate profiles.
2025-09-28 19:20:08 -04:00
neil
094d03bf7a Merge pull request #6542 from szhu25/list-profiles
Implement discovery of CA certificate profiles
2025-09-28 15:42:50 +01:00
Steven Zhu
0f5093c0b7 Remove space 2025-09-27 17:52:44 -04:00
Steven Zhu
80748b9fe0 Quick Patch 2025-09-27 17:37:37 -04:00
Steven Zhu
b244c76dd5 Add --list-profiles command to show CA profiles
This commit introduces a new command, `--list-profiles`, to allow users to discover the certificate profiles supported by a Certificate Authority.

The command queries the `meta.profiles` object within the ACME directory JSON for the selected server and formats the output for readability. If a CA does not publish profiles in its directory, the command reports that none were found.

Usage:
  acme.sh --list-profiles [--server letsencrypt]
2025-09-27 17:29:12 -04:00
neil
f2dbf56db1 Merge pull request #6514 from zjwangmin/dev
fix bug for #6510
2025-09-27 22:16:14 +01:00
neil
e5214ea2e5 add Actalis.com CA 2025-09-27 23:12:05 +02:00
neil
11995b958a add actalis.com CA 2025-09-27 22:57:42 +02:00
neil
493ec4be52 Merge pull request #6442 from JensSpanier/dev
Support certificate profile selection
2025-09-27 21:27:55 +01:00
Jens Spanier
604e6873ba Add short name + wiki link to help 2025-09-22 12:12:17 +02:00
Jens Spanier
5954f0dde5 Change to --cert-profile 2025-09-22 12:11:50 +02:00
neil
f22b490a10 remove buypass 2025-09-21 18:04:59 +02:00
neil
471e0c05f9 remove mageia 2025-09-20 10:38:43 +02:00
neil
c3ec827fdd remove buypass 2025-09-19 20:54:09 +02:00
neil
ca7bdd9101 Merge pull request #6424 from rglidden/truenas_ws_remote
truenas_ws: Add ability to deploy to remote TrueNAS server via WebSockets
2025-09-19 20:49:29 +02:00
Richard Glidden
070cd0f4df Use _sleep instead of sleep 2025-09-16 22:19:16 -04:00
Jens Spanier
1b00ced7ad Add --profile as option for selecting certificate profile 2025-09-16 09:20:31 +02:00
Min Wang
44c7473ef9 fix bug for #6510 2025-09-16 15:09:12 +08:00
neil
b4a5149ba2 Merge pull request #6503 from benyamin-codez/dnsapi-dns_opnsense-v25.7-refresh-dev
dnsapi/dns_opnsense.sh: Refresh for OPNsense v25.7 series
2025-09-15 19:35:42 +02:00
neil
df350e6660 fix format 2025-09-15 19:34:54 +02:00
benyamin-codez
d76f4b27b0 dnsapi/dns_opnsense.sh: Refresh for OPNsense v25.7 series
Updates the dns_opnsense.sh Bourne shell script for OPNSense v25.7 series:

1. Fixes historical error in rm_record() [used incorrect response variable]
2. Improves debug messaging in rm_record()
3. Fixes _get_root() for change in OPNsense API
    * Response is now split into pseudo-rows
    * We now iterate through pseudo-rows for matching domainname field
4. Fixes _existingchallenge() for change in OPNsense API
    * Fixes unreliable regex for uuid
    * Adds domain regex and %domain field
5. Fixes historical error in _existingchallenge() [incorrect variable syntax]

Resolves #6467

Signed-off-by: benyamin-codez <115509179+benyamin-codez@users.noreply.github.com>
2025-09-15 15:50:48 +10:00
neil
a1ea2a5aa6 fix tr
https://github.com/acmesh-official/acme.sh/issues/6511#issuecomment-3282521860
2025-09-14 10:35:34 +02:00
Richard Glidden
8608e9cd3a Save and read variables 2025-09-12 22:22:30 -04:00
Jens Spanier
5f8f7ee576 Merge branch 'acmesh-official:dev' into dev 2025-09-10 09:09:43 +02:00
An
d8a92a2e65 switch nanelo api to post requests 2025-09-08 21:32:15 +02:00
An | Anton Röhm
67ed930100 Merge branch 'acmesh-official:master' into patch-1 2025-09-08 21:22:55 +02:00
neil
03dd318265 Merge pull request #6505 from acmesh-official/dev
sync
2025-09-07 20:49:42 +02:00
neil
fc3bfda3bd Merge pull request #6487 from aSauerwein/master
add template_stack option to push to device
2025-09-07 20:48:00 +02:00
An | Anton Röhm
7eb0ce6e90 Merge branch 'dev' into patch-1 2025-09-07 17:19:02 +02:00
An | Anton Röhm
5aa964cde9 Formatting
using shfmt to format the code
2025-09-07 17:17:51 +02:00
An | Anton Röhm
31d7264583 Fix pattern matching for best zone selection 2025-09-07 17:14:06 +02:00
An | Anton Röhm
93dc22a71f Support Nanelo DNS Team- & Workspace-specific API keys
Nanelo Team- and Workspace-specific API keys require the "domain" parameter to be set containing the DNS zone name (unlike the Domain-specific API keys).
So I've added a function to detect the root DNS zone and set the required parameter as described here: https://github.com/acmesh-official/acme.sh/wiki/DNS-API-Dev-Guide#3-detect-which-part-is-your-root-zone
2025-09-07 16:57:10 +02:00
neil
c4d228ad8d Merge pull request #6493 from jdevera/proxmox_response_check
Show proxmox deploy scripts response only on debug
2025-09-07 10:41:24 +02:00
neil
30faf500eb fix https://github.com/acmesh-official/acme.sh/pull/6499#issuecomment-3259771356 2025-09-07 10:09:27 +02:00
neil
26d4bac85f Merge pull request #6501 from fuyufjh/patch-1
Fix sed command in telegram notifier
2025-09-07 10:06:35 +02:00
Eric Fu
e0e3cdc316 Fix sed command in telegram notifier 2025-09-06 22:31:50 +08:00
neil
1deb52f86d Merge pull request #6499 from OnyxMsi/ipv6_only_socat_error
socat rejects TCP-LISTEN on ipv6 only networks
2025-09-05 22:19:21 +02:00
neil
39cb87dc4b fix for DragonflyBSD
just move "date -u -j -f" before the linux branch.
2025-09-05 22:08:55 +02:00
Guillaume PELURE
04e2549239 socat rejects TCP-LISTEN on ipv6 only networks 2025-09-02 21:13:38 +02:00
neil
e06cdbf0ac Merge pull request #6495 from jdevera/patch-1
Fix diff in wiki notifications (use full clone)
2025-09-02 17:38:41 +02:00
Jacobo de Vera
d366b7e4fc Fix diff in wiki notifications (use full clone)
The checkout action fetches one single commit, so attempts to find previous states of a page result in error. Adding fetch-depth:0 to the configuration fetches all commits and makes finding the previous commit that changed a page possible in the github action.
2025-09-01 19:54:36 +01:00
Jacobo de Vera
5aae3333bc Show proxmox deploy scripts response only on debug 2025-08-31 20:44:24 +01:00
Richard Glidden
6d40ac8644 chore: Fix shellcheck errors 2025-08-29 16:24:30 -04:00
Richard Glidden
d7c428fc8d feat: Add ability to deploy to remote TrueNAS instances 2025-08-29 16:24:30 -04:00
neil
28f8f56fa3 Merge pull request #6365 from OPPO9008/dev
dnsla api更新
2025-08-28 13:45:15 +08:00
neil
b1f6b5314c Merge pull request #5197 from aorith/dev
fix: rage4 - add error 400 and TXT cleanup
2025-08-28 13:42:03 +08:00
asauerwein
fdb1e8c2e4 fix usage of H1 header
change to while loop

use global variable for loop

fix if statement to be sh compliant

shfmt
2025-08-20 18:37:25 +02:00
asauerwein
5b02e86334 add template_stack option to push to device 2025-08-20 17:47:36 +02:00
neil
10627e4974 Merge pull request #6473 from woutd/constellix-rate-limit
Add sleep before each REST call to Constellix to prevent rate limit
2025-08-19 10:05:37 +08:00
neil
1b89e7c2ec Merge pull request #6476 from TobiasGrave/fix_variomedia_api_2
Variomedia API: Fix DNS challenge TXT record deletion
2025-08-16 00:18:03 +08:00
Tobias Grave
bcf0afb25e Variomedia API: Fix DNS deletion issues 2025-08-15 09:02:57 +02:00
wout
ab22c8ca1c Convert domain to lower case, needed for Constellix REST API 2025-08-12 19:04:19 +02:00
neil
49513cb2ee Merge pull request #6284 from fornex-com/master
Optimize root domain search through api
2025-08-12 22:31:02 +08:00
wout
1b5e66f9c2 Add sleep before each REST call to Constellix to prevent rate limit 2025-08-12 11:30:16 +02:00
keryfan
1f486fc9a5 Upload latest dev branch to master (#3)
* Fix for empty error objects in response breaking extraction of domain validation types

Fix for empty error objects in the response which mess up the extraction of domain validation types due to the closing brace in the error object prematurely matching the end of the search pattern.

This seems to be a recent change with ZeroSSL in particular where "error":{} is being included in responses.

There could potentially be a related issue if there is a complex error object ever returned in the validation check response where an embedded sub-object could lead to an incomplete extraction of the error message, roughly around line 5040.

Adapted from fix suggested here: https://github.com/acmesh-official/acme.sh/issues/4933#issuecomment-1870499018

* Add new dnsapi support for OpenProvider.eu using new REST API

* Cleanup duplicate debug log output based on DNS test run

* Resolve spellcheck error

* Configure 10 second timeout to ACME_DIRECTORY API call

* add support for AIX style netstat

* add

* fix for wiki

* minor

* minor

* wiki

* wiki

* dnsapi: dns_mydnsjp.sh fix author

The @epgdatacapbon was renamed to @tkmsst

Signed-off-by: Sergey Ponomarev <stokito@gmail.com>

* dnsapi: dns_ddnss.sh remove RaidenII from authors

He made the DuckDNS script that was used for this script but he can't support the script.

Signed-off-by: Sergey Ponomarev <stokito@gmail.com>

* dnsapi: fix authors: use @ for GitHub profiles

Signed-off-by: Sergey Ponomarev <stokito@gmail.com>

* dnsapi: dns_vultr.sh remove empty author

Signed-off-by: Sergey Ponomarev <stokito@gmail.com>

* dnsapi: dns_mijnhost.sh rearrange fields, use user docs instead of API docs

Signed-off-by: Sergey Ponomarev <stokito@gmail.com>

* dnsapi: fix Structured DNS Info

Signed-off-by: Sergey Ponomarev <stokito@gmail.com>

* Fix logged typo when running pre hook

* Run post hook when _on_before_issue errors

---------

Signed-off-by: Sergey Ponomarev <stokito@gmail.com>
Co-authored-by: Ciaran Walsh <ciaran@ciaran-walsh.com>
Co-authored-by: Lambiek12 <algemeen@lambiek12.nl>
Co-authored-by: Erwin Oegema <blablaechthema@hotmail.com>
Co-authored-by: laDanz <cdanzmann@gmail.com>
Co-authored-by: neil <github@neilpang.com>
Co-authored-by: neil <gitpc@neilpang.com>
Co-authored-by: Sergey Ponomarev <stokito@gmail.com>
Co-authored-by: David Beitey <david@davidjb.com>
Co-authored-by: Jan-willem van Kampen <Lambiek12@users.noreply.github.com>
2025-08-12 11:12:09 +03:00
neil
4cdbdf2125 Merge pull request #6391 from Lambiek12/dev
New dnsapi support for OpenProvider.eu using REST API
2025-08-06 19:28:09 +08:00
neil
ec5ecabdec Merge pull request #5008 from cjwalsh/patch-1
Fix for empty error objects in response breaking extraction of domain validation types
2025-08-06 19:07:19 +08:00
Ciaran Walsh
e391301348 Merge branch 'acmesh-official:master' into patch-1 2025-08-03 22:42:11 +01:00
Ciaran Walsh
49866b6cf4 Merge branch 'dev' into patch-1 2025-08-03 21:48:26 +01:00
Jan-willem van Kampen
09d326de3a Merge branch 'acmesh-official:dev' into dev 2025-07-25 18:49:53 +02:00
neil
8cac773e34 Merge pull request #6444 from davidjb/fix-typo
Fix logged typo when running pre hook
2025-07-13 12:08:19 +08:00
neil
365ec1fd5e Merge pull request #6445 from davidjb/onbeforeissue-error-handling
Run post hook when `_on_before_issue()` errors
2025-07-13 12:07:45 +08:00
David Beitey
40e58ed12d Run post hook when _on_before_issue errors 2025-07-13 11:40:34 +10:00
David Beitey
0c98dc54fe Fix logged typo when running pre hook 2025-07-13 11:19:23 +10:00
Jens Spanier
3b0f624302 Support certificate profile selection 2025-07-10 10:55:05 +02:00
neil
56242423b2 Merge pull request #6434 from stokito/dnsapi_fix_info
DNS API: Fix Structural  Info
2025-07-08 10:47:54 +08:00
Sergey Ponomarev
8113711b7a dnsapi: fix Structured DNS Info
Signed-off-by: Sergey Ponomarev <stokito@gmail.com>
2025-07-06 01:43:16 +03:00
Sergey Ponomarev
85ec6343ff dnsapi: dns_mijnhost.sh rearrange fields, use user docs instead of API docs
Signed-off-by: Sergey Ponomarev <stokito@gmail.com>
2025-07-06 01:42:39 +03:00
Sergey Ponomarev
daf183e2cc dnsapi: dns_vultr.sh remove empty author
Signed-off-by: Sergey Ponomarev <stokito@gmail.com>
2025-07-06 01:41:58 +03:00
Sergey Ponomarev
c6819cbd6b dnsapi: fix authors: use @ for GitHub profiles
Signed-off-by: Sergey Ponomarev <stokito@gmail.com>
2025-07-06 01:40:53 +03:00
Sergey Ponomarev
01ed3c3326 dnsapi: dns_ddnss.sh remove RaidenII from authors
He made the DuckDNS script that was used for this script but he can't support the script.

Signed-off-by: Sergey Ponomarev <stokito@gmail.com>
2025-07-06 01:36:40 +03:00
Sergey Ponomarev
76b68f7ccb dnsapi: dns_mydnsjp.sh fix author
The @epgdatacapbon was renamed to @tkmsst

Signed-off-by: Sergey Ponomarev <stokito@gmail.com>
2025-07-06 01:34:21 +03:00
OPPO9008
2bea808251 Update dns_la.sh 2025-07-02 21:15:46 +08:00
neil
52e051bb02 Merge pull request #6417 from acmesh-official/dev
wiki
2025-06-25 22:15:26 +02:00
neil
74fdf649d3 wiki 2025-06-25 22:14:30 +02:00
neil
2402f463e1 Merge pull request #6416 from acmesh-official/dev
sync
2025-06-25 22:12:27 +02:00
neil
424d33faa0 wiki 2025-06-25 22:11:35 +02:00
neil
b64c57fa36 Merge pull request #6414 from acmesh-official/dev
minor
2025-06-25 22:02:02 +02:00
neil
6966b3810d minor 2025-06-25 22:01:11 +02:00
neil
02cb4764c2 Merge pull request #6412 from acmesh-official/dev
minor
2025-06-25 21:52:41 +02:00
neil
89071f7226 minor 2025-06-25 21:51:55 +02:00
neil
d6eca62e56 Merge pull request #6411 from acmesh-official/dev
fix for wiki
2025-06-25 21:47:46 +02:00
neil
b025e7f0f2 fix for wiki 2025-06-25 21:46:58 +02:00
neil
0f7ecad4e8 Merge pull request #6409 from acmesh-official/dev
sync
2025-06-25 21:34:58 +02:00
neil
4a16aaacb6 add 2025-06-25 21:27:08 +02:00
neil
21c758c3d3 Merge pull request #6393 from diamondo25/dev-configure-acme_directory-timeout
Configure 10 second timeout to ACME_DIRECTORY API call
2025-06-21 22:19:56 +02:00
neil
c338f1a3fa Merge pull request #6404 from laDanz/master
add support for AIX style netstat
2025-06-21 21:52:10 +02:00
laDanz
242085d676 add support for AIX style netstat 2025-06-17 14:05:40 +02:00
keryfan
289d895423 Merge branch 'dev' into master 2025-06-13 17:59:18 +03:00
Erwin Oegema
f2b248243c Configure 10 second timeout to ACME_DIRECTORY API call 2025-06-10 10:36:06 +02:00
Lambiek12
fcd358eb71 Resolve spellcheck error 2025-06-08 17:35:09 +02:00
Lambiek12
06d3739a8d Cleanup duplicate debug log output based on DNS test run 2025-06-08 17:29:39 +02:00
Lambiek12
bff1064dbd Add new dnsapi support for OpenProvider.eu using new REST API 2025-06-08 15:39:10 +02:00
OPPO9008
19678db933 Update dns_la.sh 2025-06-06 02:06:27 +08:00
neil
3d8b682380 Merge pull request #6380 from schlegel11/master
Cloudns provider: Fix missing api password encoding
2025-05-31 17:52:44 +02:00
neil
7b76502ee6 Merge pull request #6352 from ecm75/kemploadmaster
Add deploy script for Kemp Loadmaster
2025-05-31 17:30:50 +02:00
Marcel Schlegel
28687ad7c7 Issue 3968: Fix missing api password encoding. 2025-05-31 15:02:25 +02:00
neil
d420ff0e56 Merge pull request #5043 from coderjoe/feature/deploy-to-zyxel-gs1900-switches
Add deploy/zyxel_gs1900.sh
2025-05-25 19:05:33 +02:00
neil
ed1adfd878 Merge pull request #6355 from ymol-spraaklab/dev
Set DNS Record TTL to 60 instead of 300 sec
2025-05-24 14:08:57 +02:00
neil
58a7b9ffa7 Merge pull request #6366 from stokito/patch-1
DNS API: fix structural info for new providers
2025-05-24 14:07:19 +02:00
neil
af5d046698 Merge pull request #6367 from KincaidYang/dev
docs (dns_tencent) : update documentation links
2025-05-24 14:00:28 +02:00
YANGJINZE
9e75b707ce Merge pull request #1 from KincaidYang/KincaidYang-patch-1
docs (dns_tencent) : update documentation links
2025-05-23 18:03:56 +08:00
YANGJINZE
8241b078ce docs (dns_tencent) : update documentation links 2025-05-23 17:54:56 +08:00
OPPO9008
c8f1e41197 Update dns_la.sh 2025-05-20 20:29:44 +08:00
OPPO9008
cddf098f47 Update dns_la.sh 2025-05-20 20:28:59 +08:00
OPPO9008
500cfbc19c Update dns_la.sh 2025-05-19 21:29:33 +08:00
Sergey Ponomarev
133ae8555a dns_freemyip.sh: fix strutural info 2025-05-19 15:15:46 +03:00
Sergey Ponomarev
f132010acb dns_edgecenter.sh: fix structural info 2025-05-19 15:08:54 +03:00
ymol-spraaklab
426305dc33 Merge branch 'dev' into dev 2025-05-19 10:49:48 +02:00
emueller
55282851c4 implemented all suggestions 2025-05-19 09:18:29 +02:00
OPPO9008
9e7d1b9ce7 Update dns_la.sh 2025-05-19 13:16:30 +08:00
OPPO9008
e0da5f1703 Update dns_la.sh 2025-05-19 09:49:21 +08:00
Joe Bauser
4f5a70b804 Apply suggested fixes from shfmt diffs 2025-05-17 21:25:39 -04:00
neil
42bbd1b44a Merge pull request #6359 from acmesh-official/dev
sync
2025-05-17 16:56:42 -04:00
neil
fdeaf861e3 Merge pull request #6197 from xvybihal/dns_active24v2
dns_active24: rewrite for supporting new v2 API
2025-05-17 16:35:59 -04:00
neil
8e032a27c4 Merge pull request #6354 from phedoreanu/dev
dns_1984.hosting.sh: fix session cookie name
2025-05-17 15:38:54 -04:00
neil
5bfd43b718 fix pr 2025-05-17 21:28:26 +02:00
neil
ce7f4cb9a8 Merge pull request #6356 from Attackwave/deploy/truenas_ws
Deploy/truenas ws
2025-05-17 15:23:03 -04:00
Attackwave
99a4cf9e07 Quickfix TrueNAS 25.04 2025-05-16 22:44:25 +02:00
ymol-spraaklab
b82f6801cd Set DNS Record TTL to 60 instead of 300 sec 2025-05-16 15:40:36 +02:00
Adrian Fedoreanu
184cb0b9a8 dns_1984.hosting.sh: fix session cookie name 2025-05-15 16:01:24 +02:00
emueller
bf2e99efa6 fixed quoting in kemplm.sh 2025-05-12 10:52:35 +02:00
emueller
7543d5220c fixed kemplm.sh formatting 2025-05-12 10:45:01 +02:00
emueller
ca73e1f024 added deploy/kemplm.sh for deploying certs on Kemp Loadmaster 2025-05-12 10:28:35 +02:00
neil
c2ccc1f980 Merge pull request #6347 from acmesh-official/dev 2025-05-08 15:18:55 -04:00
keryfan
f34d4bd6da Merge branch 'acmesh-official:master' into master 2025-05-08 16:26:10 +02:00
Josef Vybíhal
8a4b436314 Merge branch 'acmesh-official:master' into dns_active24v2 2025-05-05 15:48:45 +02:00
Josef Vybíhal
42aaf7c2a0 dns_active24: rewrite for supporting new v2 API 2025-05-05 15:48:02 +02:00
neil
eb00852a71 remove ocsp for letsencrypt server 2025-05-01 13:28:20 +02:00
neil
b0535d8b40 Merge pull request #6306 from Meo597/dns-spaceship
Add Spaceship DNS API
2025-04-27 18:38:38 +02:00
Meo597
8b4d93cc14 Spaceship: fix doc 2025-04-28 00:32:46 +08:00
Meo597
e2d0923122 Spaceship: replace ~/.acme.sh with $LE_CONFIG_HOME for configurable paths 2025-04-28 00:18:23 +08:00
Meo597
2928d84339 Spaceship: replace debug with debug2 for detailed output in complex debugging 2025-04-28 00:04:49 +08:00
neil
b5e3883891 update 2025-04-26 16:47:29 +02:00
Meo597
d01aefd1eb Spaceship: i starts from 1 2025-04-25 05:24:05 +08:00
Meo597
e1d447847f Spaceship: fix domain conf 2025-04-25 05:21:52 +08:00
Joe Bauser
24a1b93842 Add deploy/zyxel_gs1900.sh
Add support for deploying to the Zyxel GS1900 line of switches as long
as those switches are running at least firmware V2.80.

Tested on a Zyxel GS1900-8 and GS1900-24E

Resolves #5042
2025-04-23 17:52:12 -04:00
neil
bed1c311e3 Merge pull request #6283 from tklk-forks/azure-managed-identities-endpoint
Azure: Use endpoint environment variable for managed identities if set
2025-04-23 21:52:07 +02:00
neil
1ce8d3ae9b start 3.1.2 2025-04-23 21:42:39 +02:00
Meo597
827315e059 Spaceship: valid api response 2025-04-15 21:02:46 +08:00
Meo597
e55a54f3d4 Spaceship: fix get_root 2025-04-15 21:02:46 +08:00
Meo597
5e8b40faf6 Spaceship: fix rm 2025-04-15 21:02:46 +08:00
Meo597
45b9982172 Add Spaceship DNS API 2025-04-15 21:02:45 +08:00
techknowlogick
dd29f970a2 Use endpoint environment variable for managed identities if set
Some environments in azure don't use the default metadata endpoint, and instead inject an env var that should be used.
2025-03-31 10:47:49 -04:00
keryfan
b6a9a1a7ee Merge branch 'dev' into master 2025-03-31 11:03:27 +03:00
taras-l
b38d15adb3 Merge pull request #2 from fornex-com/fornex_dnsapi_fix
Update _get_root url in dnsapi/dns_fornex.sh
2025-03-11 10:21:35 +02:00
kir
29342e036f Update _get_root url in dnsapi/dns_fornex.sh 2025-03-11 08:07:38 +00:00
b1n23
2f5ea120cb deployhook KeyHelp: fix bug 2024-07-16 00:25:53 +08:00
Manuel Sanchez Pinar
2beb2f5659 fix: rage4 - add error 400 and TXT cleanup
The following error happens if the header is set
    to 'Content-Type: application/json':

        {"statusCode":400,"message":"One or more errors occurred!",
        "errors":{"serializerErrors":["The input does not contain
        any JSON tokens. Expected the input to start with a valid
        JSON token, when isFinalBlock is true. LineNumber:
        0 | BytePositionInLine: 0."]}}

    Fix TXT removal
2024-07-05 09:23:14 +02:00
b1n23
3f40380c69 deployhook Directadmin: Support for selecting the scheme of DirectAdmin , HTTP or HTTPS 2024-06-03 16:57:51 +08:00
b1n23
1116b73a08 deployhook KeyHelp: Support enabling the Enforce HTTPS option 2024-06-03 16:47:43 +08:00
b1n23
bfba44fbad format adjustment 2024-04-07 12:36:19 +00:00
b1n23
c466f063c8 add newline at end of file 2024-04-01 21:59:12 +08:00
b1n23
295af01687 Add deployhook for KeyHelp 2024-03-28 23:07:14 +08:00
b1n23
e7284df1df Add deployhook for DirectAdmin 2024-03-21 21:44:33 +08:00
b1n23
3b46060caa deployhook Netlify: Support multiple Site ID 2024-03-20 23:06:09 +08:00
b1n23
696182cfa4 deployhook Edgio: Support multiple Environment ID 2024-03-20 23:05:43 +08:00
b1n23
d1a1d1da8f Add deployhook for CacheFly 2024-03-20 21:00:50 +08:00
b1n23
c508984f56 Add deployhook for Edgio 2024-03-20 21:00:50 +08:00
b1n23
54eba51b35 Add deployhook for Netlify 2024-03-20 21:00:50 +08:00
Ciaran Walsh
b0ca4435fd Fix for empty error objects in response breaking extraction of domain validation types
Fix for empty error objects in the response which mess up the extraction of domain validation types due to the closing brace in the error object prematurely matching the end of the search pattern.

This seems to be a recent change with ZeroSSL in particular where "error":{} is being included in responses.

There could potentially be a related issue if there is a complex error object ever returned in the validation check response where an embedded sub-object could lead to an incomplete extraction of the error message, roughly around line 5040.

Adapted from fix suggested here: https://github.com/acmesh-official/acme.sh/issues/4933#issuecomment-1870499018
2024-02-21 00:21:09 +00:00
59 changed files with 2178 additions and 264 deletions

View File

@@ -26,7 +26,7 @@ jobs:
Linux:
strategy:
matrix:
os: ["ubuntu:latest", "debian:latest", "almalinux:latest", "fedora:latest", "opensuse/leap:latest", "alpine:latest", "oraclelinux:8", "kalilinux/kali", "archlinux:latest", "mageia", "gentoo/stage3"]
os: ["ubuntu:latest", "debian:latest", "almalinux:latest", "fedora:latest", "opensuse/leap:latest", "alpine:latest", "oraclelinux:8", "kalilinux/kali", "archlinux:latest", "gentoo/stage3"]
runs-on: ubuntu-latest
env:
TEST_LOCAL: 1

View File

@@ -20,12 +20,14 @@ jobs:
owner: context.repo.owner,
repo: context.repo.repo,
body: `**Welcome**
First thing: don't send PR to the master branch, please send to the dev branch instead.
Please make sure you've read our [DNS API Dev Guide](../wiki/DNS-API-Dev-Guide) and [DNS-API-Test](../wiki/DNS-API-Test).
Then reply on this message, otherwise, your code will not be reviewed or merged.
Please also make sure to add/update the usage here: https://github.com/acmesh-official/acme.sh/wiki/dnsapi2
We look forward to reviewing your Pull request shortly ✨
注意: 必须通过了 [DNS-API-Test](../wiki/DNS-API-Test) 才会被 review. 无论是修改, 还是新加的 dns api, 都必须确保通过这个测试.
READ ME !!!!!
Read me !!!!!!
First thing: don't send PR to the master branch, please send to the dev branch instead.
Please read the [DNS API Dev Guide](../wiki/DNS-API-Dev-Guide).
You MUST pass the [DNS-API-Test](../wiki/DNS-API-Test).
Then reply on this message, otherwise, your code will not be reviewed or merged.
Please also make sure to add/update the usage here: https://github.com/acmesh-official/acme.sh/wiki/dnsapi2
注意: 必须通过了 [DNS-API-Test](../wiki/DNS-API-Test) 才会被 review. 无论是修改, 还是新加的 dns api, 都必须确保通过这个测试.
`
})

62
.github/workflows/wiki-monitor.yml vendored Normal file
View File

@@ -0,0 +1,62 @@
name: Notify via Issue on Wiki Edit
on:
gollum:
jobs:
notify:
runs-on: ubuntu-latest
steps:
- name: Checkout wiki repository
uses: actions/checkout@v4
with:
repository: ${{ github.repository }}.wiki
path: wiki
fetch-depth: 0
- name: Generate wiki change message
run: |
actor="${{ github.actor }}"
sender_url=$(jq -r '.sender.html_url' "$GITHUB_EVENT_PATH")
page_name=$(jq -r '.pages[0].page_name' "$GITHUB_EVENT_PATH")
page_sha=$(jq -r '.pages[0].sha' "$GITHUB_EVENT_PATH")
page_url=$(jq -r '.pages[0].html_url' "$GITHUB_EVENT_PATH")
page_action=$(jq -r '.pages[0].action' "$GITHUB_EVENT_PATH")
now="$(date '+%Y-%m-%d %H:%M:%S')"
cd wiki
prev_sha=$(git rev-list $page_sha^ -- "$page_name.md" | head -n 1)
if [ -n "$prev_sha" ]; then
git diff $prev_sha $page_sha -- "$page_name.md" > ../wiki.diff || echo "(No diff found)" > ../wiki.diff
else
echo "(no diff)" > ../wiki.diff
fi
cd ..
{
echo "Wiki edited"
echo -n "User: "
echo "[$actor]($sender_url)"
echo "Time: $now"
echo "Page: [$page_name]($page_url) (Action: $page_action)"
echo ""
echo "----"
echo "### diff"
echo '```diff'
cat wiki.diff
echo '```'
} > wiki-change-msg.txt
- name: Create issue to notify Neilpang
uses: peter-evans/create-issue-from-file@v5
with:
title: "Wiki edited"
content-filepath: ./wiki-change-msg.txt
assignees: Neilpang
env:
TZ: Asia/Shanghai

View File

@@ -98,9 +98,9 @@ https://github.com/acmesh-official/acmetest
- [ZeroSSL.com CA](https://github.com/acmesh-official/acme.sh/wiki/ZeroSSL.com-CA)(default)
- Letsencrypt.org CA
- [BuyPass.com CA](https://github.com/acmesh-official/acme.sh/wiki/BuyPass.com-CA)
- [SSL.com CA](https://github.com/acmesh-official/acme.sh/wiki/SSL.com-CA)
- [Google.com Public CA](https://github.com/acmesh-official/acme.sh/wiki/Google-Public-CA)
- [Actalis.com CA](https://github.com/acmesh-official/acme.sh/wiki/Actalis.com-CA)
- [Pebble strict Mode](https://github.com/letsencrypt/pebble)
- Any other [RFC8555](https://tools.ietf.org/html/rfc8555)-compliant CA

154
acme.sh
View File

@@ -1,6 +1,6 @@
#!/usr/bin/env sh
VER=3.1.1
VER=3.1.2
PROJECT_NAME="acme.sh"
@@ -23,9 +23,6 @@ _SUB_FOLDERS="$_SUB_FOLDER_DNSAPI $_SUB_FOLDER_DEPLOY $_SUB_FOLDER_NOTIFY"
CA_LETSENCRYPT_V2="https://acme-v02.api.letsencrypt.org/directory"
CA_LETSENCRYPT_V2_TEST="https://acme-staging-v02.api.letsencrypt.org/directory"
CA_BUYPASS="https://api.buypass.com/acme/directory"
CA_BUYPASS_TEST="https://api.test4.buypass.no/acme/directory"
CA_ZEROSSL="https://acme.zerossl.com/v2/DV90"
_ZERO_EAB_ENDPOINT="https://api.zerossl.com/acme/eab-credentials-email"
@@ -35,6 +32,8 @@ CA_SSLCOM_ECC="https://acme.ssl.com/sslcom-dv-ecc"
CA_GOOGLE="https://dv.acme-v02.api.pki.goog/directory"
CA_GOOGLE_TEST="https://dv.acme-v02.test-api.pki.goog/directory"
CA_ACTALIS="https://acme-api.actalis.com/acme/directory"
DEFAULT_CA=$CA_ZEROSSL
DEFAULT_STAGING_CA=$CA_LETSENCRYPT_V2_TEST
@@ -42,14 +41,13 @@ CA_NAMES="
ZeroSSL.com,zerossl
LetsEncrypt.org,letsencrypt
LetsEncrypt.org_test,letsencrypt_test,letsencrypttest
BuyPass.com,buypass
BuyPass.com_test,buypass_test,buypasstest
SSL.com,sslcom
Google.com,google
Google.com_test,googletest,google_test
Actalis.com,actalis.com,actalis
"
CA_SERVERS="$CA_ZEROSSL,$CA_LETSENCRYPT_V2,$CA_LETSENCRYPT_V2_TEST,$CA_BUYPASS,$CA_BUYPASS_TEST,$CA_SSLCOM_RSA,$CA_GOOGLE,$CA_GOOGLE_TEST"
CA_SERVERS="$CA_ZEROSSL,$CA_LETSENCRYPT_V2,$CA_LETSENCRYPT_V2_TEST,$CA_SSLCOM_RSA,$CA_GOOGLE,$CA_GOOGLE_TEST,$CA_ACTALIS"
DEFAULT_USER_AGENT="$PROJECT_NAME/$VER ($PROJECT)"
@@ -180,6 +178,8 @@ _VALIDITY_WIKI="https://github.com/acmesh-official/acme.sh/wiki/Validity"
_DNSCHECK_WIKI="https://github.com/acmesh-official/acme.sh/wiki/dnscheck"
_PROFILESELECTION_WIKI="https://github.com/acmesh-official/acme.sh/wiki/Profile-selection"
_DNS_MANUAL_ERR="The dns manual mode can not renew automatically, you must issue it again manually. You'd better use the other modes instead."
_DNS_MANUAL_WARN="It seems that you are using dns manual mode. please take care: $_DNS_MANUAL_ERR"
@@ -436,14 +436,28 @@ _secure_debug3() {
fi
}
__USE_TR_TAG=""
if [ "$(echo "abc" | LANG=C tr a-z A-Z 2>/dev/null)" != "ABC" ]; then
__USE_TR_TAG="1"
fi
export __USE_TR_TAG
_upper_case() {
# shellcheck disable=SC2018,SC2019
tr '[a-z]' '[A-Z]'
if [ "$__USE_TR_TAG" ]; then
LANG=C tr '[:lower:]' '[:upper:]'
else
# shellcheck disable=SC2018,SC2019
LANG=C tr '[a-z]' '[A-Z]'
fi
}
_lower_case() {
# shellcheck disable=SC2018,SC2019
tr '[A-Z]' '[a-z]'
if [ "$__USE_TR_TAG" ]; then
LANG=C tr '[:upper:]' '[:lower:]'
else
# shellcheck disable=SC2018,SC2019
LANG=C tr '[A-Z]' '[a-z]'
fi
}
_startswith() {
@@ -1401,6 +1415,12 @@ _ss() {
return 0
fi
if [ "$(uname)" = "AIX" ]; then
_debug "Using: AIX netstat"
netstat -an | grep "^tcp" | grep "LISTEN" | grep "\.$_port "
return 0
fi
if _exists "netstat"; then
_debug "Using: netstat"
if netstat -help 2>&1 | grep "\-p proto" >/dev/null; then
@@ -1805,6 +1825,10 @@ _time() {
# 2022-04-01 08:10:33 to 1648800633
#or 2022-04-01T08:10:33Z to 1648800633
_date2time() {
#Mac/BSD
if date -u -j -f "%Y-%m-%d %H:%M:%S" "$(echo "$1" | tr -d "Z" | tr "T" ' ')" +"%s" 2>/dev/null; then
return
fi
#Linux
if date -u -d "$(echo "$1" | tr -d "Z" | tr "T" ' ')" +"%s" 2>/dev/null; then
return
@@ -1814,10 +1838,6 @@ _date2time() {
if gdate -u -d "$(echo "$1" | tr -d "Z" | tr "T" ' ')" +"%s" 2>/dev/null; then
return
fi
#Mac/BSD
if date -u -j -f "%Y-%m-%d %H:%M:%S" "$(echo "$1" | tr -d "Z" | tr "T" ' ')" +"%s" 2>/dev/null; then
return
fi
#Omnios
if python3 -c "import datetime; print(int(datetime.datetime.strptime(\"$1\", \"%Y-%m-%d %H:%M:%S\").replace(tzinfo=datetime.timezone.utc).timestamp()))" 2>/dev/null; then
return
@@ -2532,15 +2552,19 @@ _startserver() {
_NC="socat"
if [ "$Le_Listen_V6" ]; then
_NC="$_NC -6"
else
SOCAT_OPTIONS=TCP6-LISTEN
elif [ "$Le_Listen_V4" ]; then
_NC="$_NC -4"
SOCAT_OPTIONS=TCP4-LISTEN
else
SOCAT_OPTIONS=TCP-LISTEN
fi
if [ "$DEBUG" ] && [ "$DEBUG" -gt "1" ]; then
_NC="$_NC -d -d -v"
fi
SOCAT_OPTIONS=TCP-LISTEN:$Le_HTTPPort,crlf,reuseaddr,fork
SOCAT_OPTIONS=$SOCAT_OPTIONS:$Le_HTTPPort,crlf,reuseaddr,fork
#Adding bind to local-address
if [ "$ncaddr" ]; then
@@ -2761,7 +2785,7 @@ _initAPI() {
_request_retry_times=0
while [ -z "$ACME_NEW_ACCOUNT" ] && [ "${_request_retry_times}" -lt "$MAX_API_RETRY_TIMES" ]; do
_request_retry_times=$(_math "$_request_retry_times" + 1)
response=$(_get "$_api_server")
response=$(_get "$_api_server" "" 10)
if [ "$?" != "0" ]; then
_debug2 "response" "$response"
_info "Cannot init API for: $_api_server."
@@ -3507,7 +3531,7 @@ _on_before_issue() {
_debug _chk_alt_domains "$_chk_alt_domains"
#run pre hook
if [ "$_chk_pre_hook" ]; then
_info "Runing pre hook:'$_chk_pre_hook'"
_info "Running pre hook:'$_chk_pre_hook'"
if ! (
export Le_Domain="$_chk_main_domain"
export Le_Alt="$_chk_alt_domains"
@@ -4410,6 +4434,7 @@ issue() {
_preferred_chain="${15}"
_valid_from="${16}"
_valid_to="${17}"
_certificate_profile="${18}"
if [ -z "$_ACME_IS_RENEW" ]; then
_initpath "$_main_domain" "$_key_length"
@@ -4485,6 +4510,11 @@ issue() {
else
_cleardomainconf "Le_Preferred_Chain"
fi
if [ "$_certificate_profile" ]; then
_savedomainconf "Le_Certificate_Profile" "$_certificate_profile"
else
_cleardomainconf "Le_Certificate_Profile"
fi
Le_API="$ACME_DIRECTORY"
_savedomainconf "Le_API" "$Le_API"
@@ -4496,6 +4526,7 @@ issue() {
if ! _on_before_issue "$_web_roots" "$_main_domain" "$_alt_domains" "$_pre_hook" "$_local_addr"; then
_err "_on_before_issue."
_on_issue_err "$_post_hook"
return 1
fi
@@ -4616,6 +4647,9 @@ issue() {
if [ "$_notAfter" ]; then
_newOrderObj="$_newOrderObj,\"notAfter\": \"$_notAfter\""
fi
if [ "$_certificate_profile" ]; then
_newOrderObj="$_newOrderObj,\"profile\": \"$_certificate_profile\""
fi
_debug "STEP 1, Ordering a Certificate"
if ! _send_signed_request "$ACME_NEW_ORDER" "$_newOrderObj}"; then
_err "Error creating new order."
@@ -4755,7 +4789,8 @@ $_authorizations_map"
_debug keyauthorization "$keyauthorization"
fi
entry="$(echo "$response" | _egrep_o '[^\{]*"type":"'$vtype'"[^\}]*')"
# Fix for empty error objects in response which mess up the original code, adapted from fix suggested here: https://github.com/acmesh-official/acme.sh/issues/4933#issuecomment-1870499018
entry="$(echo "$response" | sed s/'"error":{}'/'"error":null'/ | _egrep_o '[^\{]*"type":"'$vtype'"[^\}]*')"
_debug entry "$entry"
if [ -z "$keyauthorization" -a -z "$entry" ]; then
@@ -5452,10 +5487,6 @@ renew() {
_info "Switching back to $CA_LETSENCRYPT_V2"
Le_API="$CA_LETSENCRYPT_V2"
;;
"$CA_BUYPASS_TEST")
_info "Switching back to $CA_BUYPASS"
Le_API="$CA_BUYPASS"
;;
"$CA_GOOGLE_TEST")
_info "Switching back to $CA_GOOGLE"
Le_API="$CA_GOOGLE"
@@ -5497,6 +5528,7 @@ renew() {
Le_PostHook="$(_readdomainconf Le_PostHook)"
Le_RenewHook="$(_readdomainconf Le_RenewHook)"
Le_Preferred_Chain="$(_readdomainconf Le_Preferred_Chain)"
Le_Certificate_Profile="$(_readdomainconf Le_Certificate_Profile)"
# When renewing from an old version, the empty Le_Keylength means 2048.
# Note, do not use DEFAULT_DOMAIN_KEY_LENGTH as that value may change over
# time but an empty value implies 2048 specifically.
@@ -5504,7 +5536,14 @@ renew() {
if [ -z "$Le_Keylength" ]; then
Le_Keylength=2048
fi
issue "$Le_Webroot" "$Le_Domain" "$Le_Alt" "$Le_Keylength" "$Le_RealCertPath" "$Le_RealKeyPath" "$Le_RealCACertPath" "$Le_ReloadCmd" "$Le_RealFullChainPath" "$Le_PreHook" "$Le_PostHook" "$Le_RenewHook" "$Le_LocalAddress" "$Le_ChallengeAlias" "$Le_Preferred_Chain" "$Le_Valid_From" "$Le_Valid_To"
if [ "$CA_LETSENCRYPT_V2" = "$Le_API" ]; then
#letsencrypt doesn't support ocsp anymore
if [ "$Le_OCSP_Staple" ]; then
export Le_OCSP_Staple=""
_cleardomainconf Le_OCSP_Staple
fi
fi
issue "$Le_Webroot" "$Le_Domain" "$Le_Alt" "$Le_Keylength" "$Le_RealCertPath" "$Le_RealKeyPath" "$Le_RealCACertPath" "$Le_ReloadCmd" "$Le_RealFullChainPath" "$Le_PreHook" "$Le_PostHook" "$Le_RenewHook" "$Le_LocalAddress" "$Le_ChallengeAlias" "$Le_Preferred_Chain" "$Le_Valid_From" "$Le_Valid_To" "$Le_Certificate_Profile"
res="$?"
if [ "$res" != "0" ]; then
return "$res"
@@ -5765,7 +5804,7 @@ list() {
_sep="|"
if [ "$_raw" ]; then
if [ -z "$_domain" ]; then
printf "%s\n" "Main_Domain${_sep}KeyLength${_sep}SAN_Domains${_sep}CA${_sep}Created${_sep}Renew"
printf "%s\n" "Main_Domain${_sep}KeyLength${_sep}SAN_Domains${_sep}Profile${_sep}CA${_sep}Created${_sep}Renew"
fi
for di in "${CERT_HOME}"/*.*/; do
d=$(basename "$di")
@@ -5780,7 +5819,7 @@ list() {
. "$DOMAIN_CONF"
_ca="$(_getCAShortName "$Le_API")"
if [ -z "$_domain" ]; then
printf "%s\n" "$Le_Domain${_sep}\"$Le_Keylength\"${_sep}$Le_Alt${_sep}$_ca${_sep}$Le_CertCreateTimeStr${_sep}$Le_NextRenewTimeStr"
printf "%s\n" "$Le_Domain${_sep}\"$Le_Keylength\"${_sep}$Le_Alt${_sep}$Le_Certificate_Profile${_sep}$_ca${_sep}$Le_CertCreateTimeStr${_sep}$Le_NextRenewTimeStr"
else
if [ "$_domain" = "$d" ]; then
cat "$DOMAIN_CONF"
@@ -5799,6 +5838,48 @@ list() {
}
list_profiles() {
_initpath
_initAPI
_l_server_url="$ACME_DIRECTORY"
_l_server_name="$(_getCAShortName "$_l_server_url")"
_info "Fetching profiles from $_l_server_name ($_l_server_url)..."
response=$(_get "$_l_server_url" "" 10)
if [ "$?" != "0" ]; then
_err "Failed to connect to CA directory: $_l_server_url"
return 1
fi
normalized_response=$(echo "$response" | _normalizeJson)
profiles_json=$(echo "$normalized_response" | _egrep_o '"profiles" *: *\{[^\}]*\}')
if [ -z "$profiles_json" ]; then
_info "The CA '$_l_server_name' does not publish certificate profiles via its directory endpoint."
return 0
fi
# Strip the outer layer to get the key-value pairs
profiles_kv=$(echo "$profiles_json" | sed 's/"profiles" *: *{//' | sed 's/}$//' | tr ',' '\n')
printf "\n%-15s %s\n" "name" "info"
printf -- "--------------------------------------------------------------------\n"
_old_IFS="$IFS"
IFS='
'
for pair in $profiles_kv; do
# Trim quotes and whitespace
_name=$(echo "$pair" | cut -d: -f1 | tr -d '" \t')
_info_url=$(echo "$pair" | cut -d: -f2- | sed 's/^ *//' | tr -d '"')
printf "%-15s %s\n" "$_name" "$_info_url"
done
IFS="$_old_IFS"
return 0
}
_deploy() {
_d="$1"
_hooks="$2"
@@ -6337,7 +6418,8 @@ _deactivate() {
fi
_debug "Trigger validation."
vtype="$(_getIdType "$_d_domain")"
entry="$(echo "$response" | _egrep_o '[^\{]*"type":"'$vtype'"[^\}]*')"
# Fix for empty error objects in response which mess up the original code, adapted from fix suggested here: https://github.com/acmesh-official/acme.sh/issues/4933#issuecomment-1870499018
entry="$(echo "$response" | sed s/'"error":{}'/'"error":null'/ | _egrep_o '[^\{]*"type":"'$vtype'"[^\}]*')"
_debug entry "$entry"
if [ -z "$entry" ]; then
_err "$d: Cannot get domain token"
@@ -6976,6 +7058,9 @@ Parameters:
If no match, the default offered chain will be used. (default: empty)
See: $_PREFERRED_CHAIN_WIKI
--cert-profile, --certificate-profile <profile> If the CA offers profiles, select the desired profile
See: $_PROFILESELECTION_WIKI
--valid-to <date-time> Request the NotAfter field of the cert.
See: $_VALIDITY_WIKI
--valid-from <date-time> Request the NotBefore field of the cert.
@@ -7351,6 +7436,7 @@ _process() {
_preferred_chain=""
_valid_from=""
_valid_to=""
_certificate_profile=""
while [ ${#} -gt 0 ]; do
case "${1}" in
@@ -7454,6 +7540,9 @@ _process() {
--set-default-chain)
_CMD="setdefaultchain"
;;
--list-profiles)
_CMD="list_profiles"
;;
-d | --domain)
_dvalue="$2"
@@ -7669,6 +7758,10 @@ _process() {
_valid_to="$2"
shift
;;
--certificate-profile | --cert-profile)
_certificate_profile="$2"
shift
;;
--httpport)
_httpport="$2"
Le_HTTPPort="$_httpport"
@@ -7944,7 +8037,7 @@ _process() {
uninstall) uninstall "$_nocron" ;;
upgrade) upgrade ;;
issue)
issue "$_webroot" "$_domain" "$_altdomains" "$_keylength" "$_cert_file" "$_key_file" "$_ca_file" "$_reloadcmd" "$_fullchain_file" "$_pre_hook" "$_post_hook" "$_renew_hook" "$_local_address" "$_challenge_alias" "$_preferred_chain" "$_valid_from" "$_valid_to"
issue "$_webroot" "$_domain" "$_altdomains" "$_keylength" "$_cert_file" "$_key_file" "$_ca_file" "$_reloadcmd" "$_fullchain_file" "$_pre_hook" "$_post_hook" "$_renew_hook" "$_local_address" "$_challenge_alias" "$_preferred_chain" "$_valid_from" "$_valid_to" "$_certificate_profile"
;;
deploy)
deploy "$_domain" "$_deploy_hook" "$_ecc"
@@ -8015,6 +8108,9 @@ _process() {
setdefaultchain)
setdefaultchain "$_preferred_chain"
;;
list_profiles)
list_profiles
;;
*)
if [ "$_CMD" ]; then
_err "Invalid command: $_CMD"

56
deploy/cachefly.sh Normal file
View File

@@ -0,0 +1,56 @@
#!/usr/bin/env sh
# Script to deploy certificate to CacheFly
# https://api.cachefly.com/api/2.5/docs#tag/Certificates/paths/~1certificates/post
# This deployment required following variables
# export CACHEFLY_TOKEN="Your CacheFly API Token"
# returns 0 means success, otherwise error.
######## Public functions #####################
#domain keyfile certfile cafile fullchain
CACHEFLY_API_BASE="https://api.cachefly.com/api/2.5"
cachefly_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 [ -z "$CACHEFLY_TOKEN" ]; then
_err "CACHEFLY_TOKEN is not defined."
return 1
else
_savedomainconf CACHEFLY_TOKEN "$CACHEFLY_TOKEN"
fi
_info "Deploying certificate to CacheFly..."
## upload certificate
string_fullchain=$(sed 's/$/\\n/' "$_cfullchain" | tr -d '\n')
string_key=$(sed 's/$/\\n/' "$_ckey" | tr -d '\n')
_request_body="{\"certificate\":\"$string_fullchain\",\"certificateKey\":\"$string_key\"}"
_debug _request_body "$_request_body"
_debug CACHEFLY_TOKEN "$CACHEFLY_TOKEN"
export _H1="Authorization: Bearer $CACHEFLY_TOKEN"
_response=$(_post "$_request_body" "$CACHEFLY_API_BASE/certificates" "" "POST" "application/json")
if _contains "$_response" "message"; then
_err "Error in deploying $_cdomain certificate to CacheFly."
_err "$_response"
return 1
fi
_debug response "$_response"
_info "Domain $_cdomain certificate successfully deployed to CacheFly."
return 0
}

86
deploy/directadmin.sh Normal file
View File

@@ -0,0 +1,86 @@
#!/usr/bin/env sh
# Script to deploy certificate to DirectAdmin
# https://docs.directadmin.com/directadmin/customizing-workflow/api-all-about.html#creating-a-login-key
# https://docs.directadmin.com/changelog/version-1.24.4.html#cmd-api-catch-all-pop-passwords-frontpage-protected-dirs-ssl-certs
# This deployment required following variables
# export DirectAdmin_SCHEME="https" # Optional, https or http, defaults to https
# export DirectAdmin_ENDPOINT="example.com:2222"
# export DirectAdmin_USERNAME="Your DirectAdmin Username"
# export DirectAdmin_KEY="Your DirectAdmin Login Key or Password"
# export DirectAdmin_MAIN_DOMAIN="Your DirectAdmin Main Domain, NOT Subdomain"
# returns 0 means success, otherwise error.
######## Public functions #####################
#domain keyfile certfile cafile fullchain
directadmin_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 [ -z "$DirectAdmin_ENDPOINT" ]; then
_err "DirectAdmin_ENDPOINT is not defined."
return 1
else
_savedomainconf DirectAdmin_ENDPOINT "$DirectAdmin_ENDPOINT"
fi
if [ -z "$DirectAdmin_USERNAME" ]; then
_err "DirectAdmin_USERNAME is not defined."
return 1
else
_savedomainconf DirectAdmin_USERNAME "$DirectAdmin_USERNAME"
fi
if [ -z "$DirectAdmin_KEY" ]; then
_err "DirectAdmin_KEY is not defined."
return 1
else
_savedomainconf DirectAdmin_KEY "$DirectAdmin_KEY"
fi
if [ -z "$DirectAdmin_MAIN_DOMAIN" ]; then
_err "DirectAdmin_MAIN_DOMAIN is not defined."
return 1
else
_savedomainconf DirectAdmin_MAIN_DOMAIN "$DirectAdmin_MAIN_DOMAIN"
fi
# Optional SCHEME
_getdeployconf DirectAdmin_SCHEME
# set default values for DirectAdmin_SCHEME
[ -n "${DirectAdmin_SCHEME}" ] || DirectAdmin_SCHEME="https"
_info "Deploying certificate to DirectAdmin..."
# upload certificate
string_cfullchain=$(sed 's/$/\\n/' "$_cfullchain" | tr -d '\n')
string_key=$(sed 's/$/\\n/' "$_ckey" | tr -d '\n')
_request_body="{\"domain\":\"$DirectAdmin_MAIN_DOMAIN\",\"action\":\"save\",\"type\":\"paste\",\"certificate\":\"$string_key\n$string_cfullchain\n\"}"
_debug _request_body "$_request_body"
_debug DirectAdmin_ENDPOINT "$DirectAdmin_ENDPOINT"
_debug DirectAdmin_USERNAME "$DirectAdmin_USERNAME"
_debug DirectAdmin_KEY "$DirectAdmin_KEY"
_debug DirectAdmin_MAIN_DOMAIN "$DirectAdmin_MAIN_DOMAIN"
_response=$(_post "$_request_body" "$DirectAdmin_SCHEME://$DirectAdmin_USERNAME:$DirectAdmin_KEY@$DirectAdmin_ENDPOINT/CMD_API_SSL" "" "POST" "application/json")
if _contains "$_response" "error=1"; then
_err "Error in deploying $_cdomain certificate to DirectAdmin Domain $DirectAdmin_MAIN_DOMAIN."
_err "$_response"
return 1
fi
_info "$_response"
_info "Domain $_cdomain certificate successfully deployed to DirectAdmin Domain $DirectAdmin_MAIN_DOMAIN."
return 0
}

86
deploy/edgio.sh Normal file
View File

@@ -0,0 +1,86 @@
#!/usr/bin/env sh
# Here is a script to deploy cert to edgio using its API
# https://docs.edg.io/guides/v7/develop/rest_api/authentication
# https://docs.edg.io/rest_api/#tag/tls-certs/operation/postConfigV01TlsCerts
# This deployment required following variables
# export EDGIO_CLIENT_ID="Your Edgio Client ID"
# export EDGIO_CLIENT_SECRET="Your Edgio Client Secret"
# export EDGIO_ENVIRONMENT_ID="Your Edgio Environment ID"
# If have more than one Environment ID
# export EDGIO_ENVIRONMENT_ID="ENVIRONMENT_ID_1 ENVIRONMENT_ID_2"
# returns 0 means success, otherwise error.
######## Public functions #####################
#domain keyfile certfile cafile fullchain
edgio_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 [ -z "$EDGIO_CLIENT_ID" ]; then
_err "EDGIO_CLIENT_ID is not defined."
return 1
else
_savedomainconf EDGIO_CLIENT_ID "$EDGIO_CLIENT_ID"
fi
if [ -z "$EDGIO_CLIENT_SECRET" ]; then
_err "EDGIO_CLIENT_SECRET is not defined."
return 1
else
_savedomainconf EDGIO_CLIENT_SECRET "$EDGIO_CLIENT_SECRET"
fi
if [ -z "$EDGIO_ENVIRONMENT_ID" ]; then
_err "EDGIO_ENVIRONMENT_ID is not defined."
return 1
else
_savedomainconf EDGIO_ENVIRONMENT_ID "$EDGIO_ENVIRONMENT_ID"
fi
_info "Getting access token"
_data="client_id=$EDGIO_CLIENT_ID&client_secret=$EDGIO_CLIENT_SECRET&grant_type=client_credentials&scope=app.config"
_debug Get_access_token_data "$_data"
_response=$(_post "$_data" "https://id.edgio.app/connect/token" "" "POST" "application/x-www-form-urlencoded")
_debug Get_access_token_response "$_response"
_access_token=$(echo "$_response" | _json_decode | _egrep_o '"access_token":"[^"]*' | cut -d : -f 2 | tr -d '"')
_debug _access_token "$_access_token"
if [ -z "$_access_token" ]; then
_err "Error in getting access token"
return 1
fi
_info "Uploading certificate"
string_ccert=$(sed 's/$/\\n/' "$_ccert" | tr -d '\n')
string_cca=$(sed 's/$/\\n/' "$_cca" | tr -d '\n')
string_key=$(sed 's/$/\\n/' "$_ckey" | tr -d '\n')
for ENVIRONMENT_ID in $EDGIO_ENVIRONMENT_ID; do
_data="{\"environment_id\":\"$ENVIRONMENT_ID\",\"primary_cert\":\"$string_ccert\",\"intermediate_cert\":\"$string_cca\",\"private_key\":\"$string_key\"}"
_debug Upload_certificate_data "$_data"
_H1="Authorization: Bearer $_access_token"
_response=$(_post "$_data" "https://edgioapis.com/config/v0.1/tls-certs" "" "POST" "application/json")
if _contains "$_response" "message"; then
_err "Error in deploying $_cdomain certificate to Edgio ENVIRONMENT_ID $ENVIRONMENT_ID."
_err "$_response"
return 1
fi
_debug Upload_certificate_response "$_response"
_info "Domain $_cdomain certificate successfully deployed to Edgio ENVIRONMENT_ID $ENVIRONMENT_ID."
done
return 0
}

98
deploy/kemplm.sh Executable file
View File

@@ -0,0 +1,98 @@
#!/usr/bin/env sh
#Here is a script to deploy cert to a Kemp Loadmaster.
#returns 0 means success, otherwise error.
#DEPLOY_KEMP_TOKEN="token"
#DEPLOY_KEMP_URL="https://kemplm.example.com"
######## Public functions #####################
#domain keyfile certfile cafile fullchain
kemplm_deploy() {
_domain="$1"
_key_file="$2"
_cert_file="$3"
_ca_file="$4"
_fullchain_file="$5"
_debug _domain "$_domain"
_debug _key_file "$_key_file"
_debug _cert_file "$_cert_file"
_debug _ca_file "$_ca_file"
_debug _fullchain_file "$_fullchain_file"
if ! _exists jq; then
_err "jq not found"
return 1
fi
# Rename wildcard certs, kemp accepts only alphanumeric names so we delete '*.' from filename
_kemp_domain=$(echo "${_domain}" | sed 's/\*\.//')
_debug _kemp_domain "$_kemp_domain"
# Read config from saved values or env
_getdeployconf DEPLOY_KEMP_TOKEN
_getdeployconf DEPLOY_KEMP_URL
_debug DEPLOY_KEMP_URL "$DEPLOY_KEMP_URL"
_secure_debug DEPLOY_KEMP_TOKEN "$DEPLOY_KEMP_TOKEN"
if [ -z "$DEPLOY_KEMP_TOKEN" ]; then
_err "Kemp Loadmaster token is not found, please define DEPLOY_KEMP_TOKEN."
return 1
fi
if [ -z "$DEPLOY_KEMP_URL" ]; then
_err "Kemp Loadmaster URL is not found, please define DEPLOY_KEMP_URL."
return 1
fi
# Save current values
_savedeployconf DEPLOY_KEMP_TOKEN "$DEPLOY_KEMP_TOKEN"
_savedeployconf DEPLOY_KEMP_URL "$DEPLOY_KEMP_URL"
# Check if certificate is already installed
_info "Check if certificate is already present"
_list_request="{\"cmd\": \"listcert\", \"apikey\": \"${DEPLOY_KEMP_TOKEN}\"}"
_debug3 _list_request "${_list_request}"
_kemp_cert_count=$(HTTPS_INSECURE=1 _post "${_list_request}" "${DEPLOY_KEMP_URL}/accessv2" | jq -r '.cert[] | .name' | grep -c "${_kemp_domain}")
_debug2 _kemp_cert_count "${_kemp_cert_count}"
_kemp_replace_cert=1
if [ "${_kemp_cert_count}" -eq 0 ]; then
_kemp_replace_cert=0
_info "Certificate does not exist on Kemp Loadmaster"
else
_info "Certificate already exists on Kemp Loadmaster"
fi
_debug _kemp_replace_cert "${_kemp_replace_cert}"
# Upload new certificate to Kemp Loadmaster
_kemp_upload_cert=$(_mktemp)
cat "${_fullchain_file}" "${_key_file}" | base64 | tr -d '\n' >"${_kemp_upload_cert}"
_info "Uploading certificate to Kemp Loadmaster"
_add_data=$(cat "${_kemp_upload_cert}")
_add_request="{\"cmd\": \"addcert\", \"apikey\": \"${DEPLOY_KEMP_TOKEN}\", \"replace\": ${_kemp_replace_cert}, \"cert\": \"${_kemp_domain}\", \"data\": \"${_add_data}\"}"
_debug3 _add_request "${_add_request}"
_kemp_post_result=$(HTTPS_INSECURE=1 _post "${_add_request}" "${DEPLOY_KEMP_URL}/accessv2")
_retval=$?
_debug2 _kemp_post_result "${_kemp_post_result}"
if [ "${_retval}" -eq 0 ]; then
_kemp_post_status=$(echo "${_kemp_post_result}" | jq -r '.status')
_kemp_post_message=$(echo "${_kemp_post_result}" | jq -r '.message')
if [ "${_kemp_post_status}" = "ok" ]; then
_info "Upload successful"
else
_err "Upload failed: ${_kemp_post_message}"
fi
else
_err "Upload failed"
_retval=1
fi
rm "${_kemp_upload_cert}"
return $_retval
}

131
deploy/keyhelp.sh Normal file
View File

@@ -0,0 +1,131 @@
#!/usr/bin/env sh
# Script to deploy certificate to KeyHelp
# This deployment required following variables
# export DEPLOY_KEYHELP_BASEURL="https://keyhelp.example.com"
# export DEPLOY_KEYHELP_USERNAME="Your KeyHelp Username"
# export DEPLOY_KEYHELP_PASSWORD="Your KeyHelp Password"
# export DEPLOY_KEYHELP_DOMAIN_ID="Depoly certificate to this Domain ID"
# Open the 'Edit domain' page, and you will see id=xxx at the end of the URL. This is the Domain ID.
# https://DEPLOY_KEYHELP_BASEURL/index.php?page=domains&action=edit&id=xxx
# If have more than one domain name
# export DEPLOY_KEYHELP_DOMAIN_ID="111 222 333"
keyhelp_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 [ -z "$DEPLOY_KEYHELP_BASEURL" ]; then
_err "DEPLOY_KEYHELP_BASEURL is not defined."
return 1
else
_savedomainconf DEPLOY_KEYHELP_BASEURL "$DEPLOY_KEYHELP_BASEURL"
fi
if [ -z "$DEPLOY_KEYHELP_USERNAME" ]; then
_err "DEPLOY_KEYHELP_USERNAME is not defined."
return 1
else
_savedomainconf DEPLOY_KEYHELP_USERNAME "$DEPLOY_KEYHELP_USERNAME"
fi
if [ -z "$DEPLOY_KEYHELP_PASSWORD" ]; then
_err "DEPLOY_KEYHELP_PASSWORD is not defined."
return 1
else
_savedomainconf DEPLOY_KEYHELP_PASSWORD "$DEPLOY_KEYHELP_PASSWORD"
fi
if [ -z "$DEPLOY_KEYHELP_DOMAIN_ID" ]; then
_err "DEPLOY_KEYHELP_DOMAIN_ID is not defined."
return 1
else
_savedomainconf DEPLOY_KEYHELP_DOMAIN_ID "$DEPLOY_KEYHELP_DOMAIN_ID"
fi
# Optional DEPLOY_KEYHELP_ENFORCE_HTTPS
_getdeployconf DEPLOY_KEYHELP_ENFORCE_HTTPS
# set default values for DEPLOY_KEYHELP_ENFORCE_HTTPS
[ -n "${DEPLOY_KEYHELP_ENFORCE_HTTPS}" ] || DEPLOY_KEYHELP_ENFORCE_HTTPS="1"
_info "Logging in to keyhelp panel"
username_encoded="$(printf "%s" "${DEPLOY_KEYHELP_USERNAME}" | _url_encode)"
password_encoded="$(printf "%s" "${DEPLOY_KEYHELP_PASSWORD}" | _url_encode)"
_H1="Content-Type: application/x-www-form-urlencoded"
_response=$(_get "$DEPLOY_KEYHELP_BASEURL/index.php?submit=1&username=$username_encoded&password=$password_encoded" "TRUE")
_cookie="$(grep -i '^set-cookie:' "$HTTP_HEADER" | _head_n 1 | cut -d " " -f 2)"
# If cookies is not empty then logon successful
if [ -z "$_cookie" ]; then
_err "Fail to get cookie."
return 1
fi
_debug "cookie" "$_cookie"
_info "Uploading certificate"
_date=$(date +"%Y%m%d")
encoded_key="$(_url_encode <"$_ckey")"
encoded_ccert="$(_url_encode <"$_ccert")"
encoded_cca="$(_url_encode <"$_cca")"
certificate_name="$_cdomain-$_date"
_request_body="submit=1&certificate_name=$certificate_name&add_type=upload&text_private_key=$encoded_key&text_certificate=$encoded_ccert&text_ca_certificate=$encoded_cca"
_H1="Cookie: $_cookie"
_response=$(_post "$_request_body" "$DEPLOY_KEYHELP_BASEURL/index.php?page=ssl_certificates&action=add" "" "POST")
_message=$(echo "$_response" | grep -A 2 'message-body' | sed -n '/<div class="message-body ">/,/<\/div>/{//!p;}' | sed 's/<[^>]*>//g' | sed 's/^ *//;s/ *$//')
_info "_message" "$_message"
if [ -z "$_message" ]; then
_err "Fail to upload certificate."
return 1
fi
for DOMAIN_ID in $DEPLOY_KEYHELP_DOMAIN_ID; do
_info "Apply certificate to domain id $DOMAIN_ID"
_response=$(_get "$DEPLOY_KEYHELP_BASEURL/index.php?page=domains&action=edit&id=$DOMAIN_ID")
cert_value=$(echo "$_response" | grep "$certificate_name" | sed -n 's/.*value="\([^"]*\).*/\1/p')
target_type=$(echo "$_response" | grep 'target_type' | grep 'checked' | sed -n 's/.*value="\([^"]*\).*/\1/p')
if [ "$target_type" = "directory" ]; then
path=$(echo "$_response" | awk '/name="path"/{getline; print}' | sed -n 's/.*value="\([^"]*\).*/\1/p')
fi
echo "$_response" | grep "is_prefer_https" | grep "checked" >/dev/null
if [ $? -eq 0 ]; then
is_prefer_https=1
else
is_prefer_https=0
fi
echo "$_response" | grep "hsts_enabled" | grep "checked" >/dev/null
if [ $? -eq 0 ]; then
hsts_enabled=1
else
hsts_enabled=0
fi
_debug "cert_value" "$cert_value"
if [ -z "$cert_value" ]; then
_err "Fail to get certificate id."
return 1
fi
_request_body="submit=1&id=$DOMAIN_ID&target_type=$target_type&path=$path&is_prefer_https=$is_prefer_https&hsts_enabled=$hsts_enabled&certificate_type=custom&certificate_id=$cert_value&enforce_https=$DEPLOY_KEYHELP_ENFORCE_HTTPS"
_response=$(_post "$_request_body" "$DEPLOY_KEYHELP_BASEURL/index.php?page=domains&action=edit" "" "POST")
_message=$(echo "$_response" | grep -A 2 'message-body' | sed -n '/<div class="message-body ">/,/<\/div>/{//!p;}' | sed 's/<[^>]*>//g' | sed 's/^ *//;s/ *$//')
_info "_message" "$_message"
if [ -z "$_message" ]; then
_err "Fail to apply certificate."
return 1
fi
done
_info "Domain $_cdomain certificate successfully deployed to KeyHelp Domain ID $DEPLOY_KEYHELP_DOMAIN_ID."
return 0
}

69
deploy/netlify.sh Normal file
View File

@@ -0,0 +1,69 @@
#!/usr/bin/env sh
# Script to deploy certificate to Netlify
# https://docs.netlify.com/api/get-started/#authentication
# https://open-api.netlify.com/#tag/sniCertificate
# This deployment required following variables
# export Netlify_ACCESS_TOKEN="Your Netlify Access Token"
# export Netlify_SITE_ID="Your Netlify Site ID"
# If have more than one SITE ID
# export Netlify_SITE_ID="SITE_ID_1 SITE_ID_2"
# returns 0 means success, otherwise error.
######## Public functions #####################
#domain keyfile certfile cafile fullchain
netlify_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 [ -z "$Netlify_ACCESS_TOKEN" ]; then
_err "Netlify_ACCESS_TOKEN is not defined."
return 1
else
_savedomainconf Netlify_ACCESS_TOKEN "$Netlify_ACCESS_TOKEN"
fi
if [ -z "$Netlify_SITE_ID" ]; then
_err "Netlify_SITE_ID is not defined."
return 1
else
_savedomainconf Netlify_SITE_ID "$Netlify_SITE_ID"
fi
_info "Deploying certificate to Netlify..."
## upload certificate
string_ccert=$(sed 's/$/\\n/' "$_ccert" | tr -d '\n')
string_cca=$(sed 's/$/\\n/' "$_cca" | tr -d '\n')
string_key=$(sed 's/$/\\n/' "$_ckey" | tr -d '\n')
for SITE_ID in $Netlify_SITE_ID; do
_request_body="{\"certificate\":\"$string_ccert\",\"key\":\"$string_key\",\"ca_certificates\":\"$string_cca\"}"
_debug _request_body "$_request_body"
_debug Netlify_ACCESS_TOKEN "$Netlify_ACCESS_TOKEN"
export _H1="Authorization: Bearer $Netlify_ACCESS_TOKEN"
_response=$(_post "$_request_body" "https://api.netlify.com/api/v1/sites/$SITE_ID/ssl" "" "POST" "application/json")
if _contains "$_response" "\"error\""; then
_err "Error in deploying $_cdomain certificate to Netlify SITE_ID $SITE_ID."
_err "$_response"
return 1
fi
_debug response "$_response"
_info "Domain $_cdomain certificate successfully deployed to Netlify SITE_ID $SITE_ID."
done
return 0
}

View File

@@ -7,20 +7,26 @@
#
# Firewall admin with superuser and IP address is required.
#
# REQURED:
# REQUIRED:
# export PANOS_HOST=""
# export PANOS_USER="" #User *MUST* have Commit and Import Permissions in XML API for Admin Role
# export PANOS_PASS=""
#
# OPTIONAL
# export PANOS_TEMPLATE="" #Template Name of panorama managed devices
# export PANOS_TEMPLATE="" # Template Name of panorama managed devices
# export PANOS_TEMPLATE_STACK="" # set a Template Stack if certificate should also be pushed automatically
# export PANOS_VSYS="Shared" # name of the vsys to import the certificate
#
# The script will automatically generate a new API key if
# no key is found, or if a saved key has expired or is invalid.
_COMMIT_WAIT_INTERVAL=30 # query commit status every 30 seconds
_COMMIT_WAIT_ITERATIONS=20 # query commit status 20 times (20*30 = 600 seconds = 10 minutes)
# This function is to parse the XML response from the firewall
parse_response() {
type=$2
_debug "API Response: $1"
if [ "$type" = 'keygen' ]; then
status=$(echo "$1" | sed 's/^.*\(['\'']\)\([a-z]*\)'\''.*/\2/g')
if [ "$status" = "success" ]; then
@@ -30,6 +36,13 @@ parse_response() {
message="PAN-OS Key could not be set."
fi
else
if [ "$type" = 'commit' ]; then
job_id=$(echo "$1" | sed 's/^.*\(<job>\)\(.*\)<\/job>.*/\2/g')
_commit_job_id=$job_id
elif [ "$type" = 'job_status' ]; then
job_status=$(echo "$1" | tr -d '\n' | sed 's/^.*<result>\([^<]*\)<\/result>.*/\1/g')
_commit_job_status=$job_status
fi
status=$(echo "$1" | tr -d '\n' | sed 's/^.*"\([a-z]*\)".*/\1/g')
message=$(echo "$1" | tr -d '\n' | sed 's/.*\(<result>\|<msg>\|<line>\)\([^<]*\).*/\2/g')
_debug "Firewall message: $message"
@@ -44,13 +57,13 @@ parse_response() {
#This function is used to deploy to the firewall
deployer() {
content=""
type=$1 # Types are keytest, keygen, cert, key, commit
type=$1 # Types are keytest, keygen, cert, key, commit, job_status, push
panos_url="https://$_panos_host/api/"
export _H1="Content-Type: application/x-www-form-urlencoded"
#Test API Key by performing a lookup
if [ "$type" = 'keytest' ]; then
_debug "**** Testing saved API Key ****"
_H1="Content-Type: application/x-www-form-urlencoded"
# Get Version Info to test key
content="type=version&key=$_panos_key"
## Exclude all scopes for the empty commit
@@ -61,7 +74,6 @@ deployer() {
# Generate API Key
if [ "$type" = 'keygen' ]; then
_debug "**** Generating new API Key ****"
_H1="Content-Type: application/x-www-form-urlencoded"
content="type=keygen&user=$_panos_user&password=$_panos_pass"
# content="$content${nl}--$delim${nl}Content-Disposition: form-data; type=\"keygen\"; user=\"$_panos_user\"; password=\"$_panos_pass\"${nl}Content-Type: application/octet-stream${nl}${nl}"
fi
@@ -84,6 +96,9 @@ deployer() {
if [ "$_panos_template" ]; then
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"target-tpl\"\r\n\r\n$_panos_template"
fi
if [ "$_panos_vsys" ]; then
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"target-tpl-vsys\"\r\n\r\n$_panos_vsys"
fi
fi
if [ "$type" = 'key' ]; then
panos_url="${panos_url}?type=import"
@@ -96,6 +111,9 @@ deployer() {
if [ "$_panos_template" ]; then
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"target-tpl\"\r\n\r\n$_panos_template"
fi
if [ "$_panos_vsys" ]; then
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"target-tpl-vsys\"\r\n\r\n$_panos_vsys"
fi
fi
#Close multipart
content="$content${nl}--$delim--${nl}${nl}"
@@ -106,7 +124,6 @@ deployer() {
# Commit changes
if [ "$type" = 'commit' ]; then
_debug "**** Committing changes ****"
export _H1="Content-Type: application/x-www-form-urlencoded"
#Check for force commit - will commit ALL uncommited changes to the firewall. Use with caution!
if [ "$FORCE" ]; then
_debug "Force switch detected. Committing ALL changes to the firewall."
@@ -118,6 +135,20 @@ deployer() {
content="type=commit&action=partial&key=$_panos_key&cmd=$cmd"
fi
# Query job status
if [ "$type" = 'job_status' ]; then
echo "**** Querying job $_commit_job_id status ****"
cmd=$(printf "%s" "<show><jobs><id>$_commit_job_id</id></jobs></show>" | _url_encode)
content="type=op&key=$_panos_key&cmd=$cmd"
fi
# Push changes
if [ "$type" = 'push' ]; then
echo "**** Pushing changes ****"
cmd=$(printf "%s" "<commit-all><template-stack><name>$_panos_template_stack</name><admin><member>$_panos_user</member></admin></template-stack></commit-all>" | _url_encode)
content="type=commit&action=all&key=$_panos_key&cmd=$cmd"
fi
response=$(_post "$content" "$panos_url" "" "POST")
parse_response "$response" "$type"
# Saving response to variables
@@ -126,6 +157,8 @@ deployer() {
if [ "$response_status" = "success" ]; then
_debug "Successfully deployed $type"
return 0
elif [ "$_commit_job_status" ]; then
_debug "Commit Job Status = $_commit_job_status"
else
_err "Deploy of type $type failed. Try deploying with --debug to troubleshoot."
_debug "$message"
@@ -191,11 +224,31 @@ panos_deploy() {
_getdeployconf PANOS_TEMPLATE
fi
# PANOS_TEMPLATE_STACK
if [ "$PANOS_TEMPLATE_STACK" ]; then
_debug "Detected ENV variable PANOS_TEMPLATE_STACK. Saving to file."
_savedeployconf PANOS_TEMPLATE_STACK "$PANOS_TEMPLATE_STACK" 1
else
_debug "Attempting to load variable PANOS_TEMPLATE_STACK from file."
_getdeployconf PANOS_TEMPLATE_STACK
fi
# PANOS_TEMPLATE_STACK
if [ "$PANOS_VSYS" ]; then
_debug "Detected ENV variable PANOS_VSYS. Saving to file."
_savedeployconf PANOS_VSYS "$PANOS_VSYS" 1
else
_debug "Attempting to load variable PANOS_VSYS from file."
_getdeployconf PANOS_VSYS
fi
#Store variables
_panos_host=$PANOS_HOST
_panos_user=$PANOS_USER
_panos_pass=$PANOS_PASS
_panos_template=$PANOS_TEMPLATE
_panos_template_stack=$PANOS_TEMPLATE_STACK
_panos_vsys=$PANOS_VSYS
#Test API Key if found. If the key is invalid, the variable _panos_key will be unset.
if [ "$_panos_host" ] && [ "$_panos_key" ]; then
@@ -229,6 +282,20 @@ panos_deploy() {
deployer cert
deployer key
deployer commit
if [ "$_panos_template_stack" ]; then
# try to get job status for 20 times in 30 sec interval
i=0
while [ "$i" -lt $_COMMIT_WAIT_ITERATIONS ]; do
deployer job_status
if [ "$_commit_job_status" = "OK" ]; then
echo "Commit finished!"
break
fi
sleep $_COMMIT_WAIT_INTERVAL
i=$((i + 1))
done
deployer push
fi
fi
fi
}

View File

@@ -115,6 +115,16 @@ HEREDOC
_info "Push certificates to server"
export HTTPS_INSECURE=1
export _H1="Authorization: PBSAPIToken=${_proxmoxbs_header_api_token}"
_post "$_json_payload" "$_target_url" "" POST "application/json"
response=$(_post "$_json_payload" "$_target_url" "" POST "application/json")
_retval=$?
if [ "${_retval}" -eq 0 ]; then
_debug3 response "$response"
_info "Certificate successfully deployed"
return 0
else
_err "Certificate deployment failed"
_debug "Response" "$response"
return 1
fi
}

View File

@@ -127,6 +127,16 @@ HEREDOC
_info "Push certificates to server"
export HTTPS_INSECURE=1
export _H1="Authorization: PVEAPIToken=${_proxmoxve_header_api_token}"
_post "$_json_payload" "$_target_url" "" POST "application/json"
response=$(_post "$_json_payload" "$_target_url" "" POST "application/json")
_retval=$?
if [ "${_retval}" -eq 0 ]; then
_debug3 response "$response"
_info "Certificate successfully deployed"
return 0
else
_err "Certificate deployment failed"
_debug "Response" "$response"
return 1
fi
}

View File

@@ -39,19 +39,52 @@ _ws_call() {
_debug "_ws_call arg2" "$2"
_debug "_ws_call arg3" "$3"
if [ $# -eq 3 ]; then
_ws_response=$(midclt -K "$DEPLOY_TRUENAS_APIKEY" call "$1" "$2" "$3")
_ws_response=$(midclt --uri "$_ws_uri" -K "$DEPLOY_TRUENAS_APIKEY" call "$1" "$2" "$3")
fi
if [ $# -eq 2 ]; then
_ws_response=$(midclt -K "$DEPLOY_TRUENAS_APIKEY" call "$1" "$2")
_ws_response=$(midclt --uri "$_ws_uri" -K "$DEPLOY_TRUENAS_APIKEY" call "$1" "$2")
fi
if [ $# -eq 1 ]; then
_ws_response=$(midclt -K "$DEPLOY_TRUENAS_APIKEY" call "$1")
_ws_response=$(midclt --uri "$_ws_uri" -K "$DEPLOY_TRUENAS_APIKEY" call "$1")
fi
_debug "_ws_response" "$_ws_response"
printf "%s" "$_ws_response"
return 0
}
# Upload certificate with webclient api
_ws_upload_cert() {
/usr/bin/env python - <<EOF
import sys
from truenas_api_client import Client
with Client(uri="$_ws_uri") as c:
### Login with API key
print("I:Trying to upload new certificate...")
ret = c.call("auth.login_with_api_key", "${DEPLOY_TRUENAS_APIKEY}")
if ret:
### upload certificate
with open('$1', 'r') as file:
fullchain = file.read()
with open('$2', 'r') as file:
privatekey = file.read()
ret = c.call("certificate.create", {"name": "$3", "create_type": "CERTIFICATE_CREATE_IMPORTED", "certificate": fullchain, "privatekey": privatekey, "passphrase": ""}, job=True)
print("R:" + str(ret["id"]))
sys.exit(0)
else:
print("R:0")
print("E:_ws_upload_cert error!")
sys.exit(7)
EOF
return $?
}
# Check argument is a number
# Usage:
#
@@ -88,7 +121,7 @@ _ws_check_jobid() {
# n/a
_ws_get_job_result() {
while true; do
sleep 2
_sleep 2
_ws_response=$(_ws_call "core.get_jobs" "[[\"id\", \"=\", $1]]")
if [ "$(printf "%s" "$_ws_response" | jq -r '.[]."state"')" != "RUNNING" ]; then
_ws_result="$(printf "%s" "$_ws_response" | jq '.[]."result"')"
@@ -129,7 +162,6 @@ _ws_get_job_result() {
# 5: WebUI cert error
# 6: Job error
# 7: WS call error
# 10: No CORE or SCALE detected
#
truenas_ws_deploy() {
_domain="$1"
@@ -147,11 +179,27 @@ truenas_ws_deploy() {
_info "Checking environment variables..."
_getdeployconf DEPLOY_TRUENAS_APIKEY
_getdeployconf DEPLOY_TRUENAS_HOSTNAME
_getdeployconf DEPLOY_TRUENAS_PROTOCOL
# Check API Key
if [ -z "$DEPLOY_TRUENAS_APIKEY" ]; then
_err "TrueNAS API key not found, please set the DEPLOY_TRUENAS_APIKEY environment variable."
return 1
fi
# Check Hostname, default to localhost if not set
if [ -z "$DEPLOY_TRUENAS_HOSTNAME" ]; then
_info "TrueNAS hostname not set. Using 'localhost'."
DEPLOY_TRUENAS_HOSTNAME="localhost"
fi
# Check protocol, default to ws if not set
if [ -z "$DEPLOY_TRUENAS_PROTOCOL" ]; then
_info "TrueNAS protocol not set. Using 'ws'."
DEPLOY_TRUENAS_PROTOCOL="ws"
fi
_ws_uri="$DEPLOY_TRUENAS_PROTOCOL://$DEPLOY_TRUENAS_HOSTNAME/websocket"
_debug2 DEPLOY_TRUENAS_HOSTNAME "$DEPLOY_TRUENAS_HOSTNAME"
_debug2 DEPLOY_TRUENAS_PROTOCOL "$DEPLOY_TRUENAS_PROTOCOL"
_debug _ws_uri "$_ws_uri"
_secure_debug2 DEPLOY_TRUENAS_APIKEY "$DEPLOY_TRUENAS_APIKEY"
_info "Environment variables: OK"
@@ -173,20 +221,16 @@ truenas_ws_deploy() {
return 2
fi
_savedeployconf DEPLOY_TRUENAS_APIKEY "$DEPLOY_TRUENAS_APIKEY"
_savedeployconf DEPLOY_TRUENAS_HOSTNAME "$DEPLOY_TRUENAS_HOSTNAME"
_savedeployconf DEPLOY_TRUENAS_PROTOCOL "$DEPLOY_TRUENAS_PROTOCOL"
_info "TrueNAS health: OK"
########## System info
_info "Gather system info..."
_ws_response=$(_ws_call "system.info")
_truenas_system=$(printf "%s" "$_ws_response" | jq -r '."version"' | cut -d '-' -f 2 | tr '[:lower:]' '[:upper:]')
_truenas_version=$(printf "%s" "$_ws_response" | jq -r '."version"' | cut -d '-' -f 3)
_info "TrueNAS system: $_truenas_system"
_truenas_version=$(printf "%s" "$_ws_response" | jq -r '."version"')
_info "TrueNAS version: $_truenas_version"
if [ "$_truenas_system" != "SCALE" ] && [ "$_truenas_system" != "CORE" ]; then
_err "Cannot gather TrueNAS system. Nor CORE oder SCALE detected."
return 10
fi
########## Gather current certificate
@@ -203,19 +247,26 @@ truenas_ws_deploy() {
_certname="acme_$(_utc_date | tr -d '\-\:' | tr ' ' '_')"
_info "New WebUI certificate name: $_certname"
_debug _certname "$_certname"
_ws_jobid=$(_ws_call "certificate.create" "{\"name\": \"${_certname}\", \"create_type\": \"CERTIFICATE_CREATE_IMPORTED\", \"certificate\": \"$(_json_encode <"$_file_fullchain")\", \"privatekey\": \"$(_json_encode <"$_file_key")\", \"passphrase\": \"\"}")
_debug "_ws_jobid" "$_ws_jobid"
if ! _ws_check_jobid "$_ws_jobid"; then
_err "No JobID returned from websocket method."
return 3
fi
_ws_result=$(_ws_get_job_result "$_ws_jobid")
_ws_ret=$?
if [ $_ws_ret -gt 0 ]; then
return $_ws_ret
fi
_debug "_ws_result" "$_ws_result"
_new_certid=$(printf "%s" "$_ws_result" | jq -r '."id"')
_ws_out=$(_ws_upload_cert "$_file_fullchain" "$_file_key" "$_certname")
echo "$_ws_out" | while IFS= read -r LINE; do
case "$LINE" in
I:*)
_info "${LINE#I:}"
;;
D:*)
_debug "${LINE#D:}"
;;
E*)
_err "${LINE#E:}"
;;
*) ;;
esac
done
_new_certid=$(echo "$_ws_out" | grep 'R:' | cut -d ':' -f 2)
_info "New certificate ID: $_new_certid"
########## FTP
@@ -231,33 +282,31 @@ truenas_ws_deploy() {
########## ix Apps (SCALE only)
if [ "$_truenas_system" = "SCALE" ]; then
_info "Replace app certificates..."
_ws_response=$(_ws_call "app.query")
for _app_name in $(printf "%s" "$_ws_response" | jq -r '.[]."name"'); do
_info "Checking app $_app_name..."
_ws_response=$(_ws_call "app.config" "$_app_name")
if [ "$(printf "%s" "$_ws_response" | jq -r '."network" | has("certificate_id")')" = "true" ]; then
_info "App has certificate option, setup new certificate..."
_info "App will be redeployed after updating the certificate."
_ws_jobid=$(_ws_call "app.update" "$_app_name" "{\"values\": {\"network\": {\"certificate_id\": $_new_certid}}}")
_debug "_ws_jobid" "$_ws_jobid"
if ! _ws_check_jobid "$_ws_jobid"; then
_err "No JobID returned from websocket method."
return 3
fi
_ws_result=$(_ws_get_job_result "$_ws_jobid")
_ws_ret=$?
if [ $_ws_ret -gt 0 ]; then
return $_ws_ret
fi
_debug "_ws_result" "$_ws_result"
_info "App certificate replaced."
else
_info "App has no certificate option, skipping..."
_info "Replace app certificates..."
_ws_response=$(_ws_call "app.query")
for _app_name in $(printf "%s" "$_ws_response" | jq -r '.[]."name"'); do
_info "Checking app $_app_name..."
_ws_response=$(_ws_call "app.config" "$_app_name")
if [ "$(printf "%s" "$_ws_response" | jq -r '."network" | has("certificate_id")')" = "true" ]; then
_info "App has certificate option, setup new certificate..."
_info "App will be redeployed after updating the certificate."
_ws_jobid=$(_ws_call "app.update" "$_app_name" "{\"values\": {\"network\": {\"certificate_id\": $_new_certid}}}")
_debug "_ws_jobid" "$_ws_jobid"
if ! _ws_check_jobid "$_ws_jobid"; then
_err "No JobID returned from websocket method."
return 3
fi
done
fi
_ws_result=$(_ws_get_job_result "$_ws_jobid")
_ws_ret=$?
if [ $_ws_ret -gt 0 ]; then
return $_ws_ret
fi
_debug "_ws_result" "$_ws_result"
_info "App certificate replaced."
else
_info "App has no certificate option, skipping..."
fi
done
########## WebUI
@@ -273,7 +322,7 @@ truenas_ws_deploy() {
_info "Restarting WebUI..."
_ws_response=$(_ws_call "system.general.ui_restart")
_info "Waiting for UI restart..."
sleep 6
_sleep 15
########## Certificates

500
deploy/zyxel_gs1900.sh Normal file
View File

@@ -0,0 +1,500 @@
#!/usr/bin/env sh
# Deploy certificates to Zyxel GS1900 series switches
#
# This script uses the https web administration interface in order
# to upload updated certificates to Zyxel GS1900 series switches.
# Only a few models have been tested but untested switches from the
# same model line may work as well. If you test and confirm a switch
# as working please submit a pull request updating this compatibility
# list!
#
# Known Issues:
# 1. This is a consumer grade switch and is a bit underpowered
# the longer the RSA key size the slower your switch web UI
# will be. RSA 2048 will work, RSA 4096 will work but you may
# experience performance problems.
# 2. You must use RSA certificates. The switch will reject EC-256
# and EC-384 certificates in firmware 2.80
# See: https://community.zyxel.com/en/discussion/21506/bug-cannot-import-ssl-cert-on-gs1900-8-and-gs1900-24e-firmware-v2-80/
#
# Current GS1900 Switch Compatibility:
# GS1900-8 - Working as of firmware V2.80
# GS1900-8HP - Untested
# GS1900-10HP - Untested
# GS1900-16 - Untested
# GS1900-24 - Untested
# GS1900-24E - Working as of firmware V2.80
# GS1900-24EP - Untested
# GS1900-24HP - Untested
# GS1900-48 - Untested
# GS1900-48HP - Untested
#
# Prerequisite Setup Steps:
# 1. Install at least firmware V2.80 on your switch
# 2. Enable HTTPS web management on your switch
#
# Usage:
# 1. Ensure the switch has firmware V2.80 or later.
# 2. Ensure the switch has HTTPS management enabled.
# 3. Set the appropriate environment variables for your environment.
#
# DEPLOY_ZYXEL_SWITCH - The switch hostname. (Default: _cdomain)
# DEPLOY_ZYXEL_SWITCH_USER - The webadmin user. (Default: admin)
# DEPLOY_ZYXEL_SWITCH_PASSWORD - The webadmin password for the switch.
# DEPLOY_ZYXEL_SWITCH_REBOOT - If "1" reboot after update. (Default: "0")
#
# 4. Run the deployment plugin:
# acme.sh --deploy --deploy-hook zyxel_gs1900 -d example.com
#
# returns 0 means success, otherwise error.
#domain keyfile certfile cafile fullchain
zyxel_gs1900_deploy() {
_zyxel_gs1900_minimum_firmware_version="v2.80"
_cdomain="$1"
_ckey="$2"
_ccert="$3"
_cca="$4"
_cfullchain="$5"
_debug _cdomain "$_cdomain"
_debug2 _ckey "$_ckey"
_debug _ccert "$_ccert"
_debug _cca "$_cca"
_debug _cfullchain "$_cfullchain"
_getdeployconf DEPLOY_ZYXEL_SWITCH
_getdeployconf DEPLOY_ZYXEL_SWITCH_USER
_getdeployconf DEPLOY_ZYXEL_SWITCH_PASSWORD
_getdeployconf DEPLOY_ZYXEL_SWITCH_REBOOT
if [ -z "$DEPLOY_ZYXEL_SWITCH" ]; then
DEPLOY_ZYXEL_SWITCH="$_cdomain"
fi
if [ -z "$DEPLOY_ZYXEL_SWITCH_USER" ]; then
DEPLOY_ZYXEL_SWITCH_USER="admin"
fi
if [ -z "$DEPLOY_ZYXEL_SWITCH_PASSWORD" ]; then
DEPLOY_ZYXEL_SWITCH_PASSWORD="1234"
fi
if [ -z "$DEPLOY_ZYXEL_SWITCH_REBOOT" ]; then
DEPLOY_ZYXEL_SWITCH_REBOOT="0"
fi
_savedeployconf DEPLOY_ZYXEL_SWITCH "$DEPLOY_ZYXEL_SWITCH"
_savedeployconf DEPLOY_ZYXEL_SWITCH_USER "$DEPLOY_ZYXEL_SWITCH_USER"
_savedeployconf DEPLOY_ZYXEL_SWITCH_PASSWORD "$DEPLOY_ZYXEL_SWITCH_PASSWORD"
_savedeployconf DEPLOY_ZYXEL_SWITCH_REBOOT "$DEPLOY_ZYXEL_SWITCH_REBOOT"
_debug DEPLOY_ZYXEL_SWITCH "$DEPLOY_ZYXEL_SWITCH"
_debug DEPLOY_ZYXEL_SWITCH_USER "$DEPLOY_ZYXEL_SWITCH_USER"
_secure_debug DEPLOY_ZYXEL_SWITCH_PASSWORD "$DEPLOY_ZYXEL_SWITCH_PASSWORD"
_debug DEPLOY_ZYXEL_SWITCH_REBOOT "$DEPLOY_ZYXEL_SWITCH_REBOOT"
_zyxel_switch_base_uri="https://${DEPLOY_ZYXEL_SWITCH}"
_info "Beginning to deploy to a Zyxel GS1900 series switch at ${_zyxel_switch_base_uri}."
_zyxel_gs1900_deployment_precheck || return $?
_zyxel_gs1900_should_update
if [ "$?" != "0" ]; then
_info "The switch already has our certificate installed. No update required."
return 0
else
_info "The switch does not yet have our certificate installed."
fi
_info "Logging into the switch web interface."
_zyxel_gs1900_login || return $?
_info "Validating the switch is compatible with this deployment process."
_zyxel_gs1900_validate_device_compatibility || return $?
_info "Uploading the certificate."
_zyxel_gs1900_upload_certificate || return $?
if [ "$DEPLOY_ZYXEL_SWITCH_REBOOT" = "1" ]; then
_info "Rebooting the switch."
_zyxel_gs1900_trigger_reboot || return $?
fi
return 0
}
_zyxel_gs1900_deployment_precheck() {
# Initialize the keylength if it isn't already
if [ -z "$Le_Keylength" ]; then
Le_Keylength=""
fi
if _isEccKey "$Le_Keylength"; then
_info "Warning: Zyxel GS1900 switches are not currently known to work with ECC keys!"
_info "You can continue, but your switch may reject your key."
elif [ -n "$Le_Keylength" ] && [ "$Le_Keylength" -gt "2048" ]; then
_info "Warning: Your RSA key length is greater than 2048!"
_info "You can continue, but you may experience performance issues in the web administration interface."
fi
# Check the server for some common failure modes prior to authentication and certificate upload in order to avoid
# sending a certificate when we may not want to.
test_login_response=$(_post "username=test&password=test&login=true;" "${_zyxel_switch_base_uri}/cgi-bin/dispatcher.cgi?cmd=0.html" '' "POST" "application/x-www-form-urlencoded" 2>&1)
test_login_page_exitcode="$?"
_debug3 "Test Login Response: ${test_login_response}"
if [ "$test_login_page_exitcode" -ne "0" ]; then
if { [ "${ACME_USE_WGET:-0}" = "0" ] && [ "$test_login_page_exitcode" = "60" ]; } || { [ "${ACME_USE_WGET:-0}" = "1" ] && [ "$test_login_page_exitcode" = "5" ]; }; then
_err "The SSL certificate at $_zyxel_switch_base_uri could not be validated."
_err "Please double check your hostname, port, and that you are actually connecting to your switch."
_err "If the problem persists then please ensure that the certificate is not self-signed, has not"
_err "expired, and matches the switch hostname. If you expect validation to fail then you can disable"
_err "certificate validation by running with --insecure."
return 1
elif [ "${ACME_USE_WGET:-0}" = "0" ] && [ "$test_login_page_exitcode" = "56" ]; then
_debug3 "Intentionally ignore curl exit code 56 in our precheck"
else
_err "Failed to submit the initial login attempt to $_zyxel_switch_base_uri."
return 1
fi
fi
}
_zyxel_gs1900_login() {
# Login to the switch and set the appropriate auth cookie in _H1
username_encoded=$(printf "%s" "$DEPLOY_ZYXEL_SWITCH_USER" | _url_encode)
password_encoded=$(_zyxel_gs1900_password_obfuscate "$DEPLOY_ZYXEL_SWITCH_PASSWORD" | _url_encode)
login_response=$(_post "username=${username_encoded}&password=${password_encoded}&login=true;" "${_zyxel_switch_base_uri}/cgi-bin/dispatcher.cgi?cmd=0.html" '' "POST" "application/x-www-form-urlencoded" | tr -d '\n')
auth_response=$(_post "authId=${login_response}&login_chk=true" "${_zyxel_switch_base_uri}/cgi-bin/dispatcher.cgi?cmd=0.html" '' "POST" "application/x-www-form-urlencoded" | tr -d '\n')
if [ "$auth_response" != "OK" ]; then
_err "Login failed due to invalid credentials."
_err "Please double check the configured username and password and try again."
return 1
fi
sessionid=$(grep -i '^set-cookie:' "$HTTP_HEADER" | _egrep_o 'HTTPS_XSSID=[^;]*;' | tr -d ';')
_secure_debug2 "sessionid" "$sessionid"
export _H1="Cookie: $sessionid"
_secure_debug2 "_H1" "$_H1"
return 0
}
_zyxel_gs1900_validate_device_compatibility() {
# Check the switches model and firmware version and throw errors
# if this script isn't compatible.
device_info_html=$(_get "${_zyxel_switch_base_uri}/cgi-bin/dispatcher.cgi?cmd=12" | tr -d '\n')
model_name=$(_zyxel_gs1900_get_model "$device_info_html")
_debug2 "model_name" "$model_name"
if [ -z "$model_name" ]; then
_err "Could not find the switch model name."
_err "Please re-run with --debug and report a bug."
return $?
fi
if ! expr "$model_name" : "GS1900-" >/dev/null; then
_err "Switch is an unsupported model: $model_name"
return 1
fi
firmware_version=$(_zyxel_gs1900_get_firmware_version "$device_info_html")
_debug2 "firmware_version" "$firmware_version"
if [ -z "$firmware_version" ]; then
_err "Could not find the switch firmware version."
_err "Please re-run with --debug and report a bug."
return $?
fi
_debug2 "_zyxel_gs1900_minimum_firmware_version" "$_zyxel_gs1900_minimum_firmware_version"
minimum_major_version=$(_zyxel_gs1900_parse_major_version "$_zyxel_gs1900_minimum_firmware_version")
_debug2 "minimum_major_version" "$minimum_major_version"
minimum_minor_version=$(_zyxel_gs1900_parse_minor_version "$_zyxel_gs1900_minimum_firmware_version")
_debug2 "minimum_minor_version" "$minimum_minor_version"
_debug2 "firmware_version" "$firmware_version"
firmware_major_version=$(_zyxel_gs1900_parse_major_version "$firmware_version")
_debug2 "firmware_major_version" "$firmware_major_version"
firmware_minor_version=$(_zyxel_gs1900_parse_minor_version "$firmware_version")
_debug2 "firmware_minor_version" "$firmware_minor_version"
_ret=0
if [ "$firmware_major_version" -lt "$minimum_major_version" ]; then
_ret=1
elif [ "$firmware_major_version" -eq "$minimum_major_version" ] && [ "$firmware_minor_version" -lt "$minimum_minor_version" ]; then
_ret=1
fi
if [ "$_ret" != "0" ]; then
_err "Unsupported firmware version $firmware_version. Please upgrade to at least version $_zyxel_gs1900_minimum_firmware_version."
fi
return $?
}
_zyxel_gs1900_should_update() {
# Get the remote certificate serial number
_remote_cert=$(${ACME_OPENSSL_BIN:-openssl} s_client -showcerts -connect "${DEPLOY_ZYXEL_SWITCH}:443" 2>/dev/null </dev/null | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p')
_debug3 "_remote_cert" "$_remote_cert"
_remote_cert_serial=$(printf "%s" "${_remote_cert}" | ${ACME_OPENSSL_BIN:-openssl} x509 -noout -serial)
_debug2 "_remote_cert_serial" "$_remote_cert_serial"
# Get our certificate serial number
_our_cert_serial=$(${ACME_OPENSSL_BIN:-openssl} x509 -noout -serial <"${_ccert}")
_debug2 "_our_cert_serial" "$_our_cert_serial"
[ "${_remote_cert_serial}" != "${_our_cert_serial}" ]
}
_zyxel_gs1900_upload_certificate() {
# Generate a PKCS12 certificate with a temporary password since the web interface
# requires a password be present. Then upload that certificate.
temp_cert_password=$(head /dev/urandom | tr -dc 'A-Za-z0-9' | head -c 64)
_secure_debug2 "temp_cert_password" "$temp_cert_password"
temp_pkcs12="$(_mktemp)"
_debug2 "temp_pkcs12" "$temp_pkcs12"
_toPkcs "$temp_pkcs12" "$_ckey" "$_ccert" "$_cca" "$temp_cert_password"
if [ "$?" != "0" ]; then
_err "Failed to generate a pkcs12 certificate."
_err "Please re-run with --debug and report a bug."
# ensure the temporary certificate file is cleaned up
[ -f "${temp_pkcs12}" ] && rm -f "${temp_pkcs12}"
return $?
fi
# Load the upload page
upload_page_html=$(_get "${_zyxel_switch_base_uri}/cgi-bin/dispatcher.cgi?cmd=5914" | tr -d '\n')
# Get the first instance of XSSID from the upload page
form_xss_value=$(printf "%s" "$upload_page_html" | _egrep_o 'name="XSSID"\s*value="[^"]+"' | sed 's/^.*="\([^"]\{1,\}\)"$/\1/g' | head -n 1)
_secure_debug2 "form_xss_value" "$form_xss_value"
_info "Generating the certificate upload request"
upload_post_request="$(_mktemp)"
upload_post_boundary="---------------------------$(date +%Y%m%d%H%M%S)"
{
printf -- "--%s\r\n" "${upload_post_boundary}"
printf "Content-Disposition: form-data; name=\"XSSID\"\r\n\r\n%s\r\n" "${form_xss_value}"
printf -- "--%s\r\n" "${upload_post_boundary}"
printf "Content-Disposition: form-data; name=\"http_file\"; filename=\"temp_pkcs12.pfx\"\r\n"
printf "Content-Type: application/pkcs12\r\n\r\n"
cat "${temp_pkcs12}"
printf "\r\n"
printf -- "--%s\r\n" "${upload_post_boundary}"
printf "Content-Disposition: form-data; name=\"pwd\"\r\n\r\n%s\r\n" "${temp_cert_password}"
printf -- "--%s\r\n" "${upload_post_boundary}"
printf "Content-Disposition: form-data; name=\"cmd\"\r\n\r\n%s\r\n" "31"
printf -- "--%s\r\n" "${upload_post_boundary}"
printf "Content-Disposition: form-data; name=\"sysSubmit\"\r\n\r\n%s\r\n" "Import"
printf -- "--%s--\r\n" "${upload_post_boundary}"
} >"${upload_post_request}"
_info "Upload certificate to the switch"
# Unfortunately we cannot rely upon the switch response across switch models
# to return a consistent body return - so we cannot inspect the result of this
# upload to determine success.
upload_response=$(_zyxel_upload_pkcs12 "${upload_post_request}" "${upload_post_boundary}" 2>&1)
_debug3 "Upload response: ${upload_response}"
rm "${upload_post_request}"
# Pause for a few seconds to give the switch a chance to process the certificate
# For some reason I've found this to be necessary on my GS1900-24E
_debug2 "Waiting 4 seconds for the switch to process the newly uploaded certificate."
sleep "4"
# Check to see whether or not our update was successful
_ret=0
_zyxel_gs1900_should_update
if [ "$?" != "0" ]; then
_info "The certificate was updated successfully"
else
_ret=1
_err "The certificate upload does not appear to have worked."
_err "The remote certificate does not match the certificate we tried to upload."
_err "Please re-run with --debug 2 and review for unexpected errors. If none can be found please submit a bug."
fi
# ensure the temporary files are cleaned up
[ -f "${temp_pkcs12}" ] && rm -f "${temp_pkcs12}"
return $_ret
}
# make the certificate upload request using either
# --data binary with @ for file access in CURL
# or using --post-file for wget to ensure we upload
# the pkcs12 without getting tripped up on null bytes
#
# Usage _zyxel_upload_pkcs12 [body file name] [post boundary marker]
_zyxel_upload_pkcs12() {
bodyfilename="$1"
multipartformmarker="$2"
_post_url="${_zyxel_switch_base_uri}/cgi-bin/httpuploadcert.cgi"
httpmethod="POST"
_postContentType="multipart/form-data; boundary=${multipartformmarker}"
if [ -z "$httpmethod" ]; then
httpmethod="POST"
fi
_debug $httpmethod
_debug "_post_url" "$_post_url"
_debug2 "bodyfilename" "$bodyfilename"
_debug2 "_postContentType" "$_postContentType"
_inithttp
if [ "$_ACME_CURL" ] && [ "${ACME_USE_WGET:-0}" = "0" ]; then
_CURL="$_ACME_CURL"
if [ "$HTTPS_INSECURE" ]; then
_CURL="$_CURL --insecure "
fi
if [ "$httpmethod" = "HEAD" ]; then
_CURL="$_CURL -I "
fi
_debug "_CURL" "$_CURL"
response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data-binary "@${bodyfilename}" "$_post_url")"
_ret="$?"
if [ "$_ret" != "0" ]; then
_err "Please refer to https://curl.haxx.se/libcurl/c/libcurl-errors.html for error code: $_ret"
if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then
_err "Here is the curl dump log:"
_err "$(cat "$_CURL_DUMP")"
fi
fi
elif [ "$_ACME_WGET" ]; then
_WGET="$_ACME_WGET"
if [ "$HTTPS_INSECURE" ]; then
_WGET="$_WGET --no-check-certificate "
fi
_debug "_WGET" "$_WGET"
response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --post-file="${bodyfilename}" "$_post_url" 2>"$HTTP_HEADER")"
_ret="$?"
if [ "$_ret" = "8" ]; then
_ret=0
_debug "wget returned 8 as the server returned a 'Bad Request' response. Let's process the response later."
fi
if [ "$_ret" != "0" ]; then
_err "Please refer to https://www.gnu.org/software/wget/manual/html_node/Exit-Status.html for error code: $_ret"
fi
if _contains "$_WGET" " -d "; then
# Demultiplex wget debug output
cat "$HTTP_HEADER" >&2
_sed_i '/^[^ ][^ ]/d; /^ *$/d' "$HTTP_HEADER"
fi
# remove leading whitespaces from header to match curl format
_sed_i 's/^ //g' "$HTTP_HEADER"
else
_ret="$?"
_err "Neither curl nor wget have been found, cannot make $httpmethod request."
fi
_debug "_ret" "$_ret"
printf "%s" "$response"
return $_ret
}
_zyxel_gs1900_trigger_reboot() {
# Trigger a reboot via the management reboot page in the web ui
reboot_page_html=$(_get "${_zyxel_switch_base_uri}/cgi-bin/dispatcher.cgi?cmd=5888" | tr -d '\n')
reboot_xss_value=$(printf "%s" "$reboot_page_html" | _egrep_o 'name="XSSID"\s*value="[^"]+"' | sed 's/^.*="\([^"]\{1,\}\)"$/\1/g')
_secure_debug2 "reboot_xss_value" "$reboot_xss_value"
reboot_response_html=$(_post "XSSID=${reboot_xss_value}&cmd=5889&sysSubmit=Reboot" "${_zyxel_switch_base_uri}/cgi-bin/dispatcher.cgi" '' "POST" "application/x-www-form-urlencoded")
reboot_message=$(printf "%s" "$reboot_response_html" | tr -d '\t\r\n\v\f' | _egrep_o "Rebooting now...")
if [ -z "$reboot_message" ]; then
_err "Failed to trigger switch reboot!"
return 1
fi
return 0
}
# password
_zyxel_gs1900_password_obfuscate() {
# Return the password obfuscated via the same method used by the
# switch's web UI login process
echo "$1" | awk '{
encoded = "";
password = $1;
allowed = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
len = length($1);
pwi = length($1);
for (i=1; i <= (321 - pwi); i++)
{
if (0 == i % 5 && pwi > 0)
{
encoded = (encoded)(substr(password, pwi--, 1));
}
else if (i == 123)
{
if (len < 10)
{
encoded = (encoded)(0);
}
else
{
encoded = (encoded)(int(len / 10));
}
}
else if (i == 289)
{
encoded = (encoded)(len % 10)
}
else
{
encoded = (encoded)(substr(allowed, int(rand() * length(allowed)), 1))
}
}
printf("%s", encoded);
}'
}
# html label
_zyxel_html_table_lookup() {
# Look up a value in the html representing the status page of the switch
# when provided with the html of the page and the label (i.e. "Model Name:")
html="$1"
label=$(printf "%s" "$2" | tr -d ' ')
lookup_result=$(printf "%s" "$html" | tr -d "\t\r\n\v\f" | sed 's/<tr>/\n<tr>/g' | sed 's/<td[^>]*>/<td>/g' | tr -d ' ' | grep -i "$label" | sed "s/<tr><td>$label<\/td><td>\([^<]\{1,\}\)<\/td><\/tr>/\1/i")
printf "%s" "$lookup_result"
return 0
}
# html
_zyxel_gs1900_get_model() {
html="$1"
model_name=$(_zyxel_html_table_lookup "$html" "Model Name:")
printf "%s" "$model_name"
}
# html
_zyxel_gs1900_get_firmware_version() {
html="$1"
firmware_version=$(_zyxel_html_table_lookup "$html" "Firmware Version:" | _egrep_o "V[^.]+.[^(]+")
printf "%s" "$firmware_version"
}
# version_number
_zyxel_gs1900_parse_major_version() {
printf "%s" "$1" | sed 's/^V\([0-9]\{1,\}\).\{1,\}$/\1/gi'
}
# version_number
_zyxel_gs1900_parse_minor_version() {
printf "%s" "$1" | sed 's/^.\{1,\}\.\([0-9]\{1,\}\)$/\1/gi'
}

View File

@@ -128,7 +128,7 @@ _1984hosting_login() {
_get "https://1984.hosting/accounts/login/" | grep "csrfmiddlewaretoken"
csrftoken="$(grep -i '^set-cookie:' "$HTTP_HEADER" | _egrep_o 'csrftoken=[^;]*;' | tr -d ';')"
sessionid="$(grep -i '^set-cookie:' "$HTTP_HEADER" | _egrep_o 'sessionid=[^;]*;' | tr -d ';')"
sessionid="$(grep -i '^set-cookie:' "$HTTP_HEADER" | _egrep_o 'cookie1984nammnamm=[^;]*;' | tr -d ';')"
if [ -z "$csrftoken" ] || [ -z "$sessionid" ]; then
_err "One or more cookies are empty: '$csrftoken', '$sessionid'."
@@ -145,7 +145,7 @@ _1984hosting_login() {
_debug2 response "$response"
if _contains "$response" '"loggedin": true'; then
One984HOSTING_SESSIONID_COOKIE="$(grep -i '^set-cookie:' "$HTTP_HEADER" | _egrep_o 'sessionid=[^;]*;' | tr -d ';')"
One984HOSTING_SESSIONID_COOKIE="$(grep -i '^set-cookie:' "$HTTP_HEADER" | _egrep_o 'cookie1984nammnamm=[^;]*;' | tr -d ';')"
One984HOSTING_CSRFTOKEN_COOKIE="$(grep -i '^set-cookie:' "$HTTP_HEADER" | _egrep_o 'csrftoken=[^;]*;' | tr -d ';')"
export One984HOSTING_SESSIONID_COOKIE
export One984HOSTING_CSRFTOKEN_COOKIE

View File

@@ -1,17 +1,17 @@
#!/usr/bin/env sh
# shellcheck disable=SC2034
dns_active24_info='Active24.com
Site: Active24.com
dns_active24_info='Active24.cz
Site: Active24.cz
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_active24
Options:
ACTIVE24_Token API Token
Active24_ApiKey API Key. Called "Identifier" in the Active24 Admin
Active24_ApiSecret API Secret. Called "Secret key" in the Active24 Admin
Issues: github.com/acmesh-official/acme.sh/issues/2059
Author: Milan Pála
'
ACTIVE24_Api="https://api.active24.com"
######## Public functions #####################
Active24_Api="https://rest.active24.cz"
# export Active24_ApiKey=ak48l3h7-ak5d-qn4t-p8gc-b6fs8c3l
# export Active24_ApiSecret=ajvkeo3y82ndsu2smvxy3o36496dcascksldncsq
# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
# Used to add txt record
@@ -22,8 +22,8 @@ dns_active24_add() {
_active24_init
_info "Adding txt record"
if _active24_rest POST "dns/$_domain/txt/v1" "{\"name\":\"$_sub_domain\",\"text\":\"$txtvalue\",\"ttl\":0}"; then
if _contains "$response" "errors"; then
if _active24_rest POST "/v2/service/$_service_id/dns/record" "{\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"content\":\"$txtvalue\",\"ttl\":300}"; then
if _contains "$response" "error"; then
_err "Add txt record error."
return 1
else
@@ -31,6 +31,7 @@ dns_active24_add() {
return 0
fi
fi
_err "Add txt record error."
return 1
}
@@ -44,19 +45,25 @@ dns_active24_rm() {
_active24_init
_debug "Getting txt records"
_active24_rest GET "dns/$_domain/records/v1"
# The API needs to send data in body in order the filter to work
# TODO: web can also add content $txtvalue to filter and then get the id from response
_active24_rest GET "/v2/service/$_service_id/dns/record" "{\"page\":1,\"descending\":true,\"sortBy\":\"name\",\"rowsPerPage\":100,\"totalRecords\":0,\"filters\":{\"type\":[\"TXT\"],\"name\":\"${_sub_domain}\"}}"
#_active24_rest GET "/v2/service/$_service_id/dns/record?rowsPerPage=100"
if _contains "$response" "errors"; then
if _contains "$response" "error"; then
_err "Error"
return 1
fi
hash_ids=$(echo "$response" | _egrep_o "[^{]+${txtvalue}[^}]+" | _egrep_o "hashId\":\"[^\"]+" | cut -c10-)
# Note: it might never be more than one record actually, NEEDS more INVESTIGATION
record_ids=$(printf "%s" "$response" | _egrep_o "[^{]+${txtvalue}[^}]+" | _egrep_o '"id" *: *[^,]+' | cut -d ':' -f 2)
_debug2 record_ids "$record_ids"
for hash_id in $hash_ids; do
_debug "Removing hash_id" "$hash_id"
if _active24_rest DELETE "dns/$_domain/$hash_id/v1" ""; then
if _contains "$response" "errors"; then
for redord_id in $record_ids; do
_debug "Removing record_id" "$redord_id"
_debug "txtvalue" "$txtvalue"
if _active24_rest DELETE "/v2/service/$_service_id/dns/record/$redord_id" ""; then
if _contains "$response" "error"; then
_err "Unable to remove txt record."
return 1
else
@@ -70,21 +77,15 @@ dns_active24_rm() {
return 1
}
#################### 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
if ! _active24_rest GET "dns/domains/v1"; then
if ! _active24_rest GET "/v1/user/self/service"; then
return 1
fi
i=1
p=1
while true; do
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
_debug "h" "$h"
@@ -104,21 +105,98 @@ _get_root() {
return 1
}
_active24_rest() {
m=$1
ep="$2"
data="$3"
_debug "$ep"
_active24_init() {
Active24_ApiKey="${Active24_ApiKey:-$(_readaccountconf_mutable Active24_ApiKey)}"
Active24_ApiSecret="${Active24_ApiSecret:-$(_readaccountconf_mutable Active24_ApiSecret)}"
#Active24_ServiceId="${Active24_ServiceId:-$(_readaccountconf_mutable Active24_ServiceId)}"
export _H1="Authorization: Bearer $ACTIVE24_Token"
if [ "$m" != "GET" ]; then
_debug "data" "$data"
response="$(_post "$data" "$ACTIVE24_Api/$ep" "" "$m" "application/json")"
else
response="$(_get "$ACTIVE24_Api/$ep")"
if [ -z "$Active24_ApiKey" ] || [ -z "$Active24_ApiSecret" ]; then
Active24_ApiKey=""
Active24_ApiSecret=""
_err "You don't specify Active24 api key and ApiSecret yet."
_err "Please create your key and try again."
return 1
fi
#save the credentials to the account conf file.
_saveaccountconf_mutable Active24_ApiKey "$Active24_ApiKey"
_saveaccountconf_mutable Active24_ApiSecret "$Active24_ApiSecret"
_debug "A24 API CHECK"
if ! _active24_rest GET "/v2/check"; then
_err "A24 API check failed with: $response"
return 1
fi
if ! echo "$response" | tr -d " " | grep \"verified\":true >/dev/null; then
_err "A24 API check failed with: $response"
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"
_active24_get_service_id "$_domain"
_debug _service_id "$_service_id"
}
_active24_get_service_id() {
_d=$1
if ! _active24_rest GET "/v1/user/self/zone/${_d}"; then
return 1
else
response=$(echo "$response" | _json_decode)
_service_id=$(echo "$response" | _egrep_o '"id" *: *[^,]+' | cut -d ':' -f 2)
fi
}
_active24_rest() {
m=$1
ep_qs=$2 # with query string
# ep=$2
ep=$(printf "%s" "$ep_qs" | cut -d '?' -f1) # no query string
data="$3"
_debug "A24 $ep"
_debug "A24 $Active24_ApiKey"
_debug "A24 $Active24_ApiSecret"
timestamp=$(_time)
datez=$(date -u +"%Y%m%dT%H%M%SZ")
canonicalRequest="${m} ${ep} ${timestamp}"
signature=$(printf "%s" "$canonicalRequest" | _hmac sha1 "$(printf "%s" "$Active24_ApiSecret" | _hex_dump | tr -d " ")" hex)
authorization64="$(printf "%s:%s" "$Active24_ApiKey" "$signature" | _base64)"
export _H1="Date: ${datez}"
export _H2="Accept: application/json"
export _H3="Content-Type: application/json"
export _H4="Authorization: Basic ${authorization64}"
_debug2 H1 "$_H1"
_debug2 H2 "$_H2"
_debug2 H3 "$_H3"
_debug2 H4 "$_H4"
# _sleep 1
if [ "$m" != "GET" ]; then
_debug2 "${m} $Active24_Api${ep_qs}"
_debug "data" "$data"
response="$(_post "$data" "$Active24_Api${ep_qs}" "" "$m" "application/json")"
else
if [ -z "$data" ]; then
_debug2 "GET $Active24_Api${ep_qs}"
response="$(_get "$Active24_Api${ep_qs}")"
else
_debug2 "GET $Active24_Api${ep_qs} with data: ${data}"
response="$(_post "$data" "$Active24_Api${ep_qs}" "" "$m" "application/json")"
fi
fi
if [ "$?" != "0" ]; then
_err "error $ep"
return 1
@@ -126,23 +204,3 @@ _active24_rest() {
_debug2 response "$response"
return 0
}
_active24_init() {
ACTIVE24_Token="${ACTIVE24_Token:-$(_readaccountconf_mutable ACTIVE24_Token)}"
if [ -z "$ACTIVE24_Token" ]; then
ACTIVE24_Token=""
_err "You didn't specify a Active24 api token yet."
_err "Please create the token and try again."
return 1
fi
_saveaccountconf_mutable ACTIVE24_Token "$ACTIVE24_Token"
_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"
}

View File

@@ -340,8 +340,17 @@ _azure_getaccess_token() {
if [ "$managedIdentity" = true ]; then
# https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/how-to-use-vm-token#get-a-token-using-http
export _H1="Metadata: true"
response="$(_get http://169.254.169.254/metadata/identity/oauth2/token\?api-version=2018-02-01\&resource=https://management.azure.com/)"
if [ -n "$IDENTITY_ENDPOINT" ]; then
# Some Azure environments may set IDENTITY_ENDPOINT (formerly MSI_ENDPOINT) to have an alternative metadata endpoint
url="$IDENTITY_ENDPOINT?api-version=2019-08-01&resource=https://management.azure.com/"
headers="X-IDENTITY-HEADER: $IDENTITY_HEADER"
else
url="http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/"
headers="Metadata: true"
fi
export _H1="$headers"
response="$(_get "$url")"
response="$(echo "$response" | _normalizeJson)"
accesstoken=$(echo "$response" | _egrep_o "\"access_token\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \")
expires_on=$(echo "$response" | _egrep_o "\"expires_on\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \")

View File

@@ -7,7 +7,7 @@ Options:
BEGET_User API user
BEGET_Password API password
Issues: github.com/acmesh-official/acme.sh/issues/6200
Author: ARNik arnik@arnik.ru
Author: ARNik <arnik@arnik.ru>
'
Beget_Api="https://api.beget.com/api"

View File

@@ -7,7 +7,7 @@ Options:
BOOKMYNAME_USERNAME Username
BOOKMYNAME_PASSWORD Password
Issues: github.com/acmesh-official/acme.sh/issues/3209
Author: Neilpang
Author: @Neilpang
'
######## Public functions #####################

View File

@@ -197,10 +197,11 @@ _dns_cloudns_http_api_call() {
auth_user="auth-id=$CLOUDNS_AUTH_ID"
fi
encoded_password=$(echo "$CLOUDNS_AUTH_PASSWORD" | tr -d "\n\r" | _url_encode)
if [ -z "$2" ]; then
data="$auth_user&auth-password=$CLOUDNS_AUTH_PASSWORD"
data="$auth_user&auth-password=$encoded_password"
else
data="$auth_user&auth-password=$CLOUDNS_AUTH_PASSWORD&$2"
data="$auth_user&auth-password=$encoded_password&$2"
fi
response="$(_get "$CLOUDNS_API/$method?$data")"

View File

@@ -117,7 +117,7 @@ dns_constellix_rm() {
#################### Private functions below ##################################
_get_root() {
domain=$1
domain=$(echo "$1" | _lower_case)
i=2
p=1
_debug "Detecting root zone"
@@ -156,6 +156,9 @@ _constellix_rest() {
data="$3"
_debug "$ep"
# Prevent rate limit
_sleep 2
rdate=$(date +"%s")"000"
hmac=$(printf "%s" "$rdate" | _hmac sha1 "$(printf "%s" "$CONSTELLIX_Secret" | _hex_dump | tr -d ' ')" | _base64)

View File

@@ -6,7 +6,7 @@ Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_ddnss
Options:
DDNSS_Token API Token
Issues: github.com/acmesh-official/acme.sh/issues/2230
Author: RaidenII, helbgd, mod242
Author: @helbgd, @mod242
'
DDNSS_DNS_API="https://ddnss.de/upd.php"

View File

@@ -7,7 +7,7 @@ Options:
DNSHOME_Subdomain Subdomain
DNSHOME_SubdomainPassword Subdomain Password
Issues: github.com/acmesh-official/acme.sh/issues/3819
Author: dnsHome.de https://github.com/dnsHome-de
Author: @dnsHome-de
'
# Usage: add subdomain.ddnsdomain.tld "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"

View File

@@ -5,7 +5,7 @@ Site: www.DuckDNS.org
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_duckdns
Options:
DuckDNS_Token API Token
Author: RaidenII
Author: @RaidenII
'
DuckDNS_API="https://www.duckdns.org/update"

View File

@@ -8,7 +8,7 @@ Options:
DYN_Customer Customer
DYN_Username API Username
DYN_Password Secret
Author: Gerd Naschenweng <https://github.com/magicdude4eva>
Author: Gerd Naschenweng <@magicdude4eva>
'
# Dyn Managed DNS API

View File

@@ -8,7 +8,7 @@ Options:
OptionsAlt:
KEY Path to SSH private key file. E.g. "/root/.ssh/dynv6"
Issues: github.com/acmesh-official/acme.sh/issues/2702
Author: StefanAbl
Author: @StefanAbl
'
dynv6_api="https://dynv6.com/api/v2"

View File

@@ -7,7 +7,7 @@ Options:
EASYDNS_Token API Token
EASYDNS_Key API Key
Issues: github.com/acmesh-official/acme.sh/issues/2647
Author: Neilpang, wurzelpanzer <wurzelpanzer@maximolider.net>
Author: @Neilpang, wurzelpanzer <wurzelpanzer@maximolider.net>
'
# API Documentation: https://sandbox.rest.easydns.net:3001/

View File

@@ -1,13 +1,13 @@
#!/usr/bin/env sh
# shellcheck disable=SC2034
# EdgeCenter DNS API integration for acme.sh
# Author: Konstantin Ruchev <konstantin.ruchev@edgecenter.ru>
dns_edgecenter_info='edgecenter DNS API
Site: https://edgecenter.ru
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_edgecenter
dns_edgecenter_info='EdgeCenter.ru
Site: EdgeCenter.ru
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_edgecenter
Options:
EDGECENTER_API_KEY auth APIKey'
EDGECENTER_API_KEY API Key
Issues: github.com/acmesh-official/acme.sh/issues/6313
Author: Konstantin Ruchev <konstantin.ruchev@edgecenter.ru>
'
EDGECENTER_API="https://api.edgecenter.ru"
DOMAIN_TYPE=

View File

@@ -95,7 +95,7 @@ _get_root() {
return 1
fi
if ! _rest GET "dns/domain/"; then
if ! _rest GET "dns/domain/?q=$h"; then
return 1
fi

View File

@@ -7,7 +7,7 @@ Options:
FREEDNS_User Username
FREEDNS_Password Password
Issues: github.com/acmesh-official/acme.sh/issues/2305
Author: David Kerr <https://github.com/dkerr64>
Author: David Kerr <@dkerr64>
'
######## Public functions #####################

View File

@@ -1,11 +1,11 @@
#!/usr/bin/env sh
# shellcheck disable=SC2034
dns_freemyip_info='FreeMyIP.com
Site: freemyip.com
Site: FreeMyIP.com
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_freemyip
Options:
FREEMYIP_Token API Token
Issues: github.com/acmesh-official/acme.sh/issues/{XXXX}
Issues: github.com/acmesh-official/acme.sh/issues/6247
Author: Recolic Keghart <root@recolic.net>, @Giova96
'

View File

@@ -5,6 +5,7 @@ Site: dns.he.net
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_he_ddns
Options:
HE_DDNS_KEY The DDNS key
Issues: https://github.com/acmesh-official/acme.sh/issues/5238
Author: Markku Leiniö
'

View File

@@ -7,7 +7,7 @@ Options:
JOKER_USERNAME Username
JOKER_PASSWORD Password
Issues: github.com/acmesh-official/acme.sh/issues/2840
Author: <https://github.com/aattww/>
Author: @aattww
'
JOKER_API="https://svc.joker.com/nic/replace"

View File

@@ -1,14 +1,17 @@
#!/usr/bin/env sh
# LA_Id="123"
# LA_Sk="456"
# shellcheck disable=SC2034
dns_la_info='dns.la
Site: dns.la
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_la
Options:
LA_Id API ID
LA_Key API key
LA_Id APIID
LA_Sk APISecret
LA_Token 用冒号连接 APIID APISecret 再base64生成
Issues: github.com/acmesh-official/acme.sh/issues/4257
'
LA_Api="https://api.dns.la/api"
######## Public functions #####################
@@ -19,18 +22,23 @@ dns_la_add() {
txtvalue=$2
LA_Id="${LA_Id:-$(_readaccountconf_mutable LA_Id)}"
LA_Key="${LA_Key:-$(_readaccountconf_mutable LA_Key)}"
LA_Sk="${LA_Sk:-$(_readaccountconf_mutable LA_Sk)}"
_log "LA_Id=$LA_Id"
_log "LA_Sk=$LA_Sk"
if [ -z "$LA_Id" ] || [ -z "$LA_Key" ]; then
if [ -z "$LA_Id" ] || [ -z "$LA_Sk" ]; then
LA_Id=""
LA_Key=""
LA_Sk=""
_err "You didn't specify a dnsla api id and key yet."
return 1
fi
#save the api key and email to the account conf file.
_saveaccountconf_mutable LA_Id "$LA_Id"
_saveaccountconf_mutable LA_Key "$LA_Key"
_saveaccountconf_mutable LA_Sk "$LA_Sk"
# generate dnsla token
_la_token
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
@@ -42,11 +50,13 @@ dns_la_add() {
_debug _domain "$_domain"
_info "Adding record"
if _la_rest "record.ashx?cmd=create&apiid=$LA_Id&apipass=$LA_Key&rtype=json&domainid=$_domain_id&host=$_sub_domain&recordtype=TXT&recorddata=$txtvalue&recordline="; then
if _contains "$response" '"resultid":'; then
# record type is enum in new api, 16 for TXT
if _la_post "{\"domainId\":\"$_domain_id\",\"type\":16,\"host\":\"$_sub_domain\",\"data\":\"$txtvalue\",\"ttl\":600}" "record"; then
if _contains "$response" '"id":'; then
_info "Added, OK"
return 0
elif _contains "$response" '"code":532'; then
elif _contains "$response" '"msg":"与已有记录冲突"'; then
_info "Already exists, OK"
return 0
else
@@ -54,7 +64,7 @@ dns_la_add() {
return 1
fi
fi
_err "Add txt record error."
_err "Add txt record failed."
return 1
}
@@ -65,7 +75,9 @@ dns_la_rm() {
txtvalue=$2
LA_Id="${LA_Id:-$(_readaccountconf_mutable LA_Id)}"
LA_Key="${LA_Key:-$(_readaccountconf_mutable LA_Key)}"
LA_Sk="${LA_Sk:-$(_readaccountconf_mutable LA_Sk)}"
_la_token
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
@@ -77,27 +89,29 @@ dns_la_rm() {
_debug _domain "$_domain"
_debug "Getting txt records"
if ! _la_rest "record.ashx?cmd=listn&apiid=$LA_Id&apipass=$LA_Key&rtype=json&domainid=$_domain_id&domain=$_domain&host=$_sub_domain&recordtype=TXT&recorddata=$txtvalue"; then
# record type is enum in new api, 16 for TXT
if ! _la_get "recordList?pageIndex=1&pageSize=10&domainId=$_domain_id&host=$_sub_domain&type=16&data=$txtvalue"; then
_err "Error"
return 1
fi
if ! _contains "$response" '"recordid":'; then
if ! _contains "$response" '"id":'; then
_info "Don't need to remove."
return 0
fi
record_id=$(printf "%s" "$response" | grep '"recordid":' | cut -d : -f 2 | cut -d , -f 1 | tr -d '\r' | tr -d '\n')
record_id=$(printf "%s" "$response" | grep '"id":' | _head_n 1 | sed 's/.*"id": *"\([^"]*\)".*/\1/')
_debug "record_id" "$record_id"
if [ -z "$record_id" ]; then
_err "Can not get record id to remove."
return 1
fi
if ! _la_rest "record.ashx?cmd=remove&apiid=$LA_Id&apipass=$LA_Key&rtype=json&domainid=$_domain_id&domain=$_domain&recordid=$record_id"; then
# remove record in new api is RESTful
if ! _la_post "" "record?id=$record_id" "DELETE"; then
_err "Delete record error."
return 1
fi
_contains "$response" '"code":300'
_contains "$response" '"code":200'
}
@@ -119,12 +133,13 @@ _get_root() {
return 1
fi
if ! _la_rest "domain.ashx?cmd=get&apiid=$LA_Id&apipass=$LA_Key&rtype=json&domain=$h"; then
if ! _la_get "domain?domain=$h"; then
return 1
fi
if _contains "$response" '"domainid":'; then
_domain_id=$(printf "%s" "$response" | grep '"domainid":' | cut -d : -f 2 | cut -d , -f 1 | tr -d '\r' | tr -d '\n')
if _contains "$response" '"domain":'; then
_domain_id=$(echo "$response" | sed -n 's/.*"id":"\([^"]*\)".*/\1/p')
_log "_domain_id" "$_domain_id"
if [ "$_domain_id" ]; then
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain="$h"
@@ -143,6 +158,21 @@ _la_rest() {
url="$LA_Api/$1"
_debug "$url"
if ! response="$(_get "$url" "Authorization: Basic $LA_Token" | tr -d ' ' | tr "}" ",")"; then
_err "Error: $url"
return 1
fi
_debug2 response "$response"
return 0
}
_la_get() {
url="$LA_Api/$1"
_debug "$url"
export _H1="Authorization: Basic $LA_Token"
if ! response="$(_get "$url" | tr -d ' ' | tr "}" ",")"; then
_err "Error: $url"
return 1
@@ -151,3 +181,29 @@ _la_rest() {
_debug2 response "$response"
return 0
}
# Usage: _la_post body url [POST|PUT|DELETE]
_la_post() {
body=$1
url="$LA_Api/$2"
http_method=$3
_debug "$body"
_debug "$url"
export _H1="Authorization: Basic $LA_Token"
if ! response="$(_post "$body" "$url" "" "$http_method")"; then
_err "Error: $url"
return 1
fi
_debug2 response "$response"
return 0
}
_la_token() {
LA_Token=$(printf "%s:%s" "$LA_Id" "$LA_Sk" | _base64)
_debug "$LA_Token"
return 0
}

View File

@@ -1,16 +1,15 @@
#!/usr/bin/env sh
# shellcheck disable=SC2034
dns_mijnhost_info='mijn.host
Domains: mijn.host
Site: mijn.host
Docs: https://mijn.host/api/doc/
Issues: https://github.com/acmesh-official/acme.sh/issues/6177
Author: peterv99
Docs: https://github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_mijnhost
Options:
MIJNHOST_API_KEY API Key
Issues: github.com/acmesh-official/acme.sh/issues/6177
Author: @peterv99
'
######## Public functions ###################### Constants for your mijn-host API
######## Public functions ######################
MIJNHOST_API="https://mijn.host/api/v2"
# Add TXT record for domain verification

View File

@@ -6,7 +6,7 @@ Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_mydnsjp
Options:
MYDNSJP_MasterID Master ID
MYDNSJP_Password Password
Author: epgdatacapbon
Author: @tkmsst
'
######## Public functions #####################

View File

@@ -6,7 +6,7 @@ Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_namecom
Options:
Namecom_Username Username
Namecom_Token API Token
Author: RaidenII
Author: @RaidenII
'
######## Public functions #####################

View File

@@ -5,7 +5,7 @@ Site: NameSilo.com
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_namesilo
Options:
Namesilo_Key API Key
Author: meowthink
Author: @meowthink
'
#Utilize API to finish dns-01 verifications.

View File

@@ -27,8 +27,16 @@ dns_nanelo_add() {
fi
_saveaccountconf_mutable NANELO_TOKEN "$NANELO_TOKEN"
_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 TXT record to ${fulldomain}"
response="$(_get "$NANELO_API$NANELO_TOKEN/dns/addrecord?type=TXT&ttl=60&name=${fulldomain}&value=${txtvalue}")"
response="$(_post "" "$NANELO_API$NANELO_TOKEN/dns/addrecord?domain=${_domain}&type=TXT&ttl=60&name=${_sub_domain}&value=${txtvalue}" "" "" "")"
if _contains "${response}" 'success'; then
return 0
fi
@@ -51,8 +59,16 @@ dns_nanelo_rm() {
fi
_saveaccountconf_mutable NANELO_TOKEN "$NANELO_TOKEN"
_debug "First, let's 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 resource record $fulldomain"
response="$(_get "$NANELO_API$NANELO_TOKEN/dns/deleterecord?type=TXT&ttl=60&name=${fulldomain}&value=${txtvalue}")"
response="$(_post "" "$NANELO_API$NANELO_TOKEN/dns/deleterecord?domain=${_domain}&type=TXT&ttl=60&name=${_sub_domain}&value=${txtvalue}" "" "" "")"
if _contains "${response}" 'success'; then
return 0
fi
@@ -60,3 +76,45 @@ dns_nanelo_rm() {
_err "${response}"
return 1
}
#################### Private functions below ##################################
#_acme-challenge.www.domain.com
#returns
# _sub_domain=_acme-challenge.www
# _domain=domain.com
_get_root() {
fulldomain=$1
# Fetch all zones from Nanelo
response="$(_get "$NANELO_API$NANELO_TOKEN/dns/getzones")" || return 1
# Extract "zones" array into space-separated list
zones=$(echo "$response" |
tr -d ' \n' |
sed -n 's/.*"zones":\[\([^]]*\)\].*/\1/p' |
tr -d '"' |
tr , ' ')
_debug zones "$zones"
bestzone=""
for z in $zones; do
case "$fulldomain" in
*."$z" | "$z")
if [ ${#z} -gt ${#bestzone} ]; then
bestzone=$z
fi
;;
esac
done
if [ -z "$bestzone" ]; then
_err "No matching zone found for $fulldomain"
return 1
fi
_domain="$bestzone"
_sub_domain=$(printf "%s" "$fulldomain" | sed "s/\\.$_domain\$//")
return 0
}

View File

@@ -0,0 +1,186 @@
#!/usr/bin/env sh
# shellcheck disable=SC2034
dns_openprovider_rest_info='OpenProvider (REST)
Domains: OpenProvider.com
Site: OpenProvider.eu
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_openprovider_rest
Options:
OPENPROVIDER_REST_USERNAME Openprovider Account Username
OPENPROVIDER_REST_PASSWORD Openprovider Account Password
Issues: github.com/acmesh-official/acme.sh/issues/6122
Author: Lambiek12
'
OPENPROVIDER_API_URL="https://api.openprovider.eu/v1beta"
######## Public functions #####################
# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
# Used to add txt record
dns_openprovider_rest_add() {
fulldomain=$1
txtvalue=$2
_openprovider_prepare_credentials || return 1
_debug "Try fetch OpenProvider DNS zone details"
if ! _get_dns_zone "$fulldomain"; then
_err "DNS zone not found within configured OpenProvider account."
return 1
fi
if [ -n "$_domain_id" ]; then
addzonerecordrequestparameters="dns/zones/$_domain_name"
addzonerecordrequestbody="{\"id\":$_domain_id,\"name\":\"$_domain_name\",\"records\":{\"add\":[{\"name\":\"$_sub_domain\",\"ttl\":900,\"type\":\"TXT\",\"value\":\"$txtvalue\"}]}}"
if _openprovider_rest PUT "$addzonerecordrequestparameters" "$addzonerecordrequestbody"; then
if _contains "$response" "\"success\":true"; then
return 0
elif _contains "$response" "\"Duplicate record\""; then
_debug "Record already existed"
return 0
else
_err "Adding TXT record failed due to errors."
return 1
fi
fi
fi
_err "Adding TXT record failed due to errors."
return 1
}
# Usage: rm _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
# Used to remove the txt record after validation
dns_openprovider_rest_rm() {
fulldomain=$1
txtvalue=$2
_openprovider_prepare_credentials || return 1
_debug "Try fetch OpenProvider DNS zone details"
if ! _get_dns_zone "$fulldomain"; then
_err "DNS zone not found within configured OpenProvider account."
return 1
fi
if [ -n "$_domain_id" ]; then
removezonerecordrequestparameters="dns/zones/$_domain_name"
removezonerecordrequestbody="{\"id\":$_domain_id,\"name\":\"$_domain_name\",\"records\":{\"remove\":[{\"name\":\"$_sub_domain\",\"ttl\":900,\"type\":\"TXT\",\"value\":\"\\\"$txtvalue\\\"\"}]}}"
if _openprovider_rest PUT "$removezonerecordrequestparameters" "$removezonerecordrequestbody"; then
if _contains "$response" "\"success\":true"; then
return 0
else
_err "Removing TXT record failed due to errors."
return 1
fi
fi
fi
_err "Removing TXT record failed due to errors."
return 1
}
#################### OpenProvider API common functions ####################
_openprovider_prepare_credentials() {
OPENPROVIDER_REST_USERNAME="${OPENPROVIDER_REST_USERNAME:-$(_readaccountconf_mutable OPENPROVIDER_REST_USERNAME)}"
OPENPROVIDER_REST_PASSWORD="${OPENPROVIDER_REST_PASSWORD:-$(_readaccountconf_mutable OPENPROVIDER_REST_PASSWORD)}"
if [ -z "$OPENPROVIDER_REST_USERNAME" ] || [ -z "$OPENPROVIDER_REST_PASSWORD" ]; then
OPENPROVIDER_REST_USERNAME=""
OPENPROVIDER_REST_PASSWORD=""
_err "You didn't specify the Openprovider username or password yet."
return 1
fi
#save the credentials to the account conf file.
_saveaccountconf_mutable OPENPROVIDER_REST_USERNAME "$OPENPROVIDER_REST_USERNAME"
_saveaccountconf_mutable OPENPROVIDER_REST_PASSWORD "$OPENPROVIDER_REST_PASSWORD"
}
_openprovider_rest() {
httpmethod=$1
queryparameters=$2
requestbody=$3
_openprovider_rest_login
if [ -z "$openproviderauthtoken" ]; then
_err "Unable to fetch authentication token from Openprovider API."
return 1
fi
export _H1="Content-Type: application/json"
export _H2="Accept: application/json"
export _H3="Authorization: Bearer $openproviderauthtoken"
if [ "$httpmethod" != "GET" ]; then
response="$(_post "$requestbody" "$OPENPROVIDER_API_URL/$queryparameters" "" "$httpmethod")"
else
response="$(_get "$OPENPROVIDER_API_URL/$queryparameters")"
fi
if [ "$?" != "0" ]; then
_err "No valid parameters supplied for Openprovider API: Error $queryparameters"
return 1
fi
_debug2 response "$response"
return 0
}
_openprovider_rest_login() {
export _H1="Content-Type: application/json"
export _H2="Accept: application/json"
loginrequesturl="$OPENPROVIDER_API_URL/auth/login"
loginrequestbody="{\"ip\":\"0.0.0.0\",\"password\":\"$OPENPROVIDER_REST_PASSWORD\",\"username\":\"$OPENPROVIDER_REST_USERNAME\"}"
loginresponse="$(_post "$loginrequestbody" "$loginrequesturl" "" "POST")"
openproviderauthtoken="$(printf "%s\n" "$loginresponse" | _egrep_o '"token" *: *"[^"]*' | _head_n 1 | sed 's#^"token" *: *"##')"
export openproviderauthtoken
}
#################### Private functions ##################################
# Usage: _get_dns_zone _acme-challenge.www.domain.com
# Returns:
# _domain_id=123456789
# _domain_name=domain.com
# _sub_domain=_acme-challenge.www
_get_dns_zone() {
domain=$1
i=1
p=1
while true; do
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
if [ -z "$h" ]; then
# Empty value not allowed
return 1
fi
if ! _openprovider_rest GET "dns/zones/$h" ""; then
return 1
fi
if _contains "$response" "\"name\":\"$h\""; then
_domain_id="$(printf "%s\n" "$response" | _egrep_o '"id" *: *[^,]*' | _head_n 1 | sed 's#^"id" *: *##')"
_debug _domain_id "$_domain_id"
_domain_name="$h"
_debug _domain_name "$_domain_name"
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_debug _sub_domain "$_sub_domain"
return 0
fi
p=$i
i=$(_math "$i" + 1)
done
return 1
}

View File

@@ -110,15 +110,16 @@ rm_record() {
if _existingchallenge "$_domain" "$_host" "$new_challenge"; then
# Delete
if _opns_rest "POST" "/record/delRecord/${_uuid}" "\{\}"; then
if echo "$_return_str" | _egrep_o "\"result\":\"deleted\"" >/dev/null; then
_opns_rest "POST" "/service/reconfigure" "{}"
if echo "$response" | _egrep_o "\"result\":\"deleted\"" >/dev/null; then
_debug "Record deleted"
_opns_rest "POST" "/service/reconfigure" "{}"
_debug "Service reconfigured"
else
_err "Error deleting record $_host from domain $fulldomain"
return 1
fi
else
_err "Error deleting record $_host from domain $fulldomain"
_err "Error requesting deletion of record $_host from domain $fulldomain"
return 1
fi
else
@@ -150,14 +151,17 @@ _get_root() {
return 1
fi
_debug h "$h"
id=$(echo "$_domain_response" | _egrep_o "\"uuid\":\"[a-z0-9\-]*\",\"enabled\":\"1\",\"type\":\"primary\",\"domainname\":\"${h}\"" | cut -d ':' -f 2 | cut -d '"' -f 2)
if [ -n "$id" ]; then
_debug id "$id"
_host=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain="${h}"
_domainid="${id}"
return 0
fi
lines=$(echo "$_domain_response" | sed 's/{/\n/g')
for line in $lines; do
id=$(echo "$line" | _egrep_o "\"uuid\":\"[a-z0-9\-]*\",\"enabled\":\"1\",\"type\":\"primary\",.*\"domainname\":\"${h}\"" | cut -d ':' -f 2 | cut -d '"' -f 2)
if [ -n "$id" ]; then
_debug id "$id"
_host=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain="${h}"
_domainid="${id}"
return 0
fi
done
p=$i
i=$(_math "$i" + 1)
done
@@ -206,13 +210,13 @@ _existingchallenge() {
return 1
fi
_uuid=""
_uuid=$(echo "$_record_response" | _egrep_o "\"uuid\":\"[^\"]*\",\"enabled\":\"[01]\",\"domain\":\"$1\",\"name\":\"$2\",\"type\":\"TXT\",\"value\":\"$3\"" | cut -d ':' -f 2 | cut -d '"' -f 2)
_uuid=$(echo "$_record_response" | _egrep_o "\"uuid\":\"[a-z0-9\-]*\",\"enabled\":\"[01]\",\"domain\":\"[a-z0-9\-]*\",\"%domain\":\"$1\",\"name\":\"$2\",\"type\":\"TXT\",\"value\":\"$3\"" | cut -d ':' -f 2 | cut -d '"' -f 2)
if [ -n "$_uuid" ]; then
_debug uuid "$_uuid"
return 0
fi
_debug "${2}.$1{1} record not found"
_debug "${2}.${1} record not found"
return 1
}

View File

@@ -8,7 +8,7 @@ Options:
pleskxml_user Username
pleskxml_pass Password
Issues: github.com/acmesh-official/acme.sh/issues/2577
Author: Stilez, <https://github.com/romanlum>
Author: @Stilez, @romanlum
'
## Plesk XML API described at:

View File

@@ -42,6 +42,14 @@ dns_rage4_add() {
_debug _domain_id "$_domain_id"
_rage4_rest "createrecord/?id=$_domain_id&name=$fulldomain&content=$unquotedtxtvalue&type=TXT&active=true&ttl=1"
# Response after adding a TXT record should be something like this:
# {"status":true,"id":28160443,"error":null}
if ! _contains "$response" '"error":null' >/dev/null; then
_err "Error while adding TXT record: '$response'"
return 1
fi
return 0
}
@@ -63,7 +71,12 @@ dns_rage4_rm() {
_debug "Getting txt records"
_rage4_rest "getrecords/?id=${_domain_id}"
_record_id=$(echo "$response" | sed -rn 's/.*"id":([[:digit:]]+)[^\}]*'"$txtvalue"'.*/\1/p')
_record_id=$(echo "$response" | tr '{' '\n' | grep '"TXT"' | grep "\"$txtvalue" | sed -rn 's/.*"id":([[:digit:]]+),.*/\1/p')
if [ -z "$_record_id" ]; then
_err "error retrieving the record_id of the new TXT record in order to delete it, got: '$_record_id'."
return 1
fi
_rage4_rest "deleterecord/?id=${_record_id}"
return 0
}
@@ -105,8 +118,7 @@ _rage4_rest() {
token_trimmed=$(echo "$RAGE4_TOKEN" | tr -d '"')
auth=$(printf '%s:%s' "$username_trimmed" "$token_trimmed" | _base64)
export _H1="Content-Type: application/json"
export _H2="Authorization: Basic $auth"
export _H1="Authorization: Basic $auth"
response="$(_get "$RAGE4_Api$ep")"

View File

@@ -7,7 +7,7 @@ Options:
SCHLUNDTECH_USER Username
SCHLUNDTECH_PASSWORD Password
Issues: github.com/acmesh-official/acme.sh/issues/2246
Author: <https://github.com/mod242>
Author: @mod242
'
SCHLUNDTECH_API="https://gateway.schlundtech.de"

View File

@@ -1,27 +1,21 @@
#!/usr/bin/env sh
# shellcheck disable=SC2034
# dns_selectel_info='Selectel.com
# Domains: Selectel.ru
# Site: Selectel.com
# Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_selectel
# Options:
# Variables that must be defined before running
# SL_Ver can take one of the values 'v1' or 'v2', default is 'v1'
# SL_Ver='v1', when using version API legacy (v1)
# SL_Ver='v2', when using version API actual (v2)
# when using API version v1, i.e. SL_Ver is 'v1' or not defined:
# SL_Key - API Key, required
# when using API version v2:
# SL_Ver - required as 'v2'
# SL_Login_ID - account ID, required
# SL_Project_Name - name project, required
# SL_Login_Name - service user name, required
# SL_Pswd - service user password, required
# SL_Expire - token lifetime in minutes (0-1440), default 1400 minutes
#
# Issues: github.com/acmesh-official/acme.sh/issues/5126
#
dns_selectel_info='Selectel.com
Domains: Selectel.ru
Site: Selectel.com
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_selectel
Options: For old API version v1 (deprecated)
SL_Ver API version. Use "v1".
SL_Key API Key
OptionsAlt: For the current API version v2
SL_Ver API version. Use "v2".
SL_Login_ID Account ID
SL_Project_Name Project name
SL_Login_Name Service user name
SL_Pswd Service user password
SL_Expire Token lifetime. In minutes (0-1440). Default "1400"
Issues: github.com/acmesh-official/acme.sh/issues/5126
'
SL_Api="https://api.selectel.ru/domains"
auth_uri="https://cloud.api.selcloud.ru/identity/v3/auth/tokens"

212
dnsapi/dns_spaceship.sh Normal file
View File

@@ -0,0 +1,212 @@
#!/usr/bin/env sh
# shellcheck disable=SC2034
dns_spaceship_info='Spaceship.com
Site: Spaceship.com
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_spaceship
Options:
SPACESHIP_API_KEY API Key
SPACESHIP_API_SECRET API Secret
SPACESHIP_ROOT_DOMAIN Root domain. Manually specify the root domain if auto-detection fails. Optional.
Issues: github.com/acmesh-official/acme.sh/issues/6304
Author: Meow <@Meo597>
'
# Spaceship API
# https://docs.spaceship.dev/
######## Public functions #####################
SPACESHIP_API_BASE="https://spaceship.dev/api/v1"
# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
# Used to add txt record
dns_spaceship_add() {
fulldomain="$1"
txtvalue="$2"
_info "Adding TXT record for $fulldomain with value $txtvalue"
# Initialize API credentials and headers
if ! _spaceship_init; then
return 1
fi
# Detect root zone
if ! _get_root "$fulldomain"; then
return 1
fi
# Extract subdomain part relative to root domain
subdomain=$(echo "$fulldomain" | sed "s/\.$_domain$//")
if [ "$subdomain" = "$fulldomain" ]; then
_err "Failed to extract subdomain from $fulldomain relative to root domain $_domain"
return 1
fi
_debug "Extracted subdomain: $subdomain for root domain: $_domain"
# Escape txtvalue to prevent JSON injection (e.g., quotes in txtvalue)
escaped_txtvalue=$(echo "$txtvalue" | sed 's/"/\\"/g')
# Prepare payload and URL for adding TXT record
# Note: 'name' in payload uses subdomain (e.g., _acme-challenge.sub) as required by Spaceship API
payload="{\"force\": true, \"items\": [{\"type\": \"TXT\", \"name\": \"$subdomain\", \"value\": \"$escaped_txtvalue\", \"ttl\": 600}]}"
url="$SPACESHIP_API_BASE/dns/records/$_domain"
# Send API request
if _spaceship_api_request "PUT" "$url" "$payload"; then
_info "Successfully added TXT record for $fulldomain"
return 0
else
_err "Failed to add TXT record. If the domain $_domain is incorrect, set SPACESHIP_ROOT_DOMAIN to the correct root domain."
return 1
fi
}
# Usage: fulldomain txtvalue
# Used to remove the txt record after validation
dns_spaceship_rm() {
fulldomain="$1"
txtvalue="$2"
_info "Removing TXT record for $fulldomain with value $txtvalue"
# Initialize API credentials and headers
if ! _spaceship_init; then
return 1
fi
# Detect root zone
if ! _get_root "$fulldomain"; then
return 1
fi
# Extract subdomain part relative to root domain
subdomain=$(echo "$fulldomain" | sed "s/\.$_domain$//")
if [ "$subdomain" = "$fulldomain" ]; then
_err "Failed to extract subdomain from $fulldomain relative to root domain $_domain"
return 1
fi
_debug "Extracted subdomain: $subdomain for root domain: $_domain"
# Escape txtvalue to prevent JSON injection
escaped_txtvalue=$(echo "$txtvalue" | sed 's/"/\\"/g')
# Prepare payload and URL for deleting TXT record
# Note: 'name' in payload uses subdomain (e.g., _acme-challenge.sub) as required by Spaceship API
payload="[{\"type\": \"TXT\", \"name\": \"$subdomain\", \"value\": \"$escaped_txtvalue\"}]"
url="$SPACESHIP_API_BASE/dns/records/$_domain"
# Send API request
if _spaceship_api_request "DELETE" "$url" "$payload"; then
_info "Successfully deleted TXT record for $fulldomain"
return 0
else
_err "Failed to delete TXT record. If the domain $_domain is incorrect, set SPACESHIP_ROOT_DOMAIN to the correct root domain."
return 1
fi
}
#################### Private functions below ##################################
_spaceship_init() {
SPACESHIP_API_KEY="${SPACESHIP_API_KEY:-$(_readaccountconf_mutable SPACESHIP_API_KEY)}"
SPACESHIP_API_SECRET="${SPACESHIP_API_SECRET:-$(_readaccountconf_mutable SPACESHIP_API_SECRET)}"
if [ -z "$SPACESHIP_API_KEY" ] || [ -z "$SPACESHIP_API_SECRET" ]; then
_err "Spaceship API credentials are not set. Please set SPACESHIP_API_KEY and SPACESHIP_API_SECRET."
_err "Ensure \"$LE_CONFIG_HOME\" directory has restricted permissions (chmod 700 \"$LE_CONFIG_HOME\") to protect credentials."
return 1
fi
# Save credentials to account config for future renewals
_saveaccountconf_mutable SPACESHIP_API_KEY "$SPACESHIP_API_KEY"
_saveaccountconf_mutable SPACESHIP_API_SECRET "$SPACESHIP_API_SECRET"
# Set common headers for API requests
export _H1="X-API-Key: $SPACESHIP_API_KEY"
export _H2="X-API-Secret: $SPACESHIP_API_SECRET"
export _H3="Content-Type: application/json"
return 0
}
_get_root() {
domain="$1"
# Check manual override
SPACESHIP_ROOT_DOMAIN="${SPACESHIP_ROOT_DOMAIN:-$(_readdomainconf SPACESHIP_ROOT_DOMAIN)}"
if [ -n "$SPACESHIP_ROOT_DOMAIN" ]; then
_domain="$SPACESHIP_ROOT_DOMAIN"
_debug "Using manually specified or saved root domain: $_domain"
_savedomainconf SPACESHIP_ROOT_DOMAIN "$SPACESHIP_ROOT_DOMAIN"
return 0
fi
_debug "Detecting root zone for '$domain'"
i=1
p=1
while true; do
_cutdomain=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
_debug "Attempt i=$i: Checking if '$_cutdomain' is root zone (cut ret=$?)"
if [ -z "$_cutdomain" ]; then
_debug "Cut resulted in empty string, root zone not found."
break
fi
# Call the API to check if this _cutdomain is a manageable zone
if _spaceship_api_request "GET" "$SPACESHIP_API_BASE/dns/records/$_cutdomain?take=1&skip=0"; then
# API call succeeded (HTTP 200 OK for GET /dns/records)
_domain="$_cutdomain"
_debug "Root zone found: '$_domain'"
# Save the detected root domain
_savedomainconf SPACESHIP_ROOT_DOMAIN "$_domain"
_info "Root domain '$_domain' saved to configuration for future use."
return 0
fi
_debug "API check failed for '$_cutdomain'. Continuing search."
p=$i
i=$((i + 1))
done
_err "Could not detect root zone for '$domain'. Please set SPACESHIP_ROOT_DOMAIN manually."
return 1
}
_spaceship_api_request() {
method="$1"
url="$2"
payload="$3"
_debug2 "Sending $method request to $url with payload $payload"
if [ "$method" = "GET" ]; then
response="$(_get "$url")"
else
response="$(_post "$payload" "$url" "" "$method")"
fi
if [ "$?" != "0" ]; then
_err "API request failed. Response: $response"
return 1
fi
_debug2 "API response body: $response"
if [ "$method" = "GET" ]; then
if _contains "$(_head_n 1 <"$HTTP_HEADER")" '200'; then
return 0
fi
else
if _contains "$(_head_n 1 <"$HTTP_HEADER")" '204'; then
return 0
fi
fi
_debug2 "API response header: $HTTP_HEADER"
return 1
}

View File

@@ -6,7 +6,7 @@ Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#tele3
Options:
TELE3_Key API Key
TELE3_Secret API Secret
Author: Roman Blizik <https://github.com/par-pa>
Author: Roman Blizik <@par-pa>
'
TELE3_API="https://www.tele3.cz/acme/"

View File

@@ -2,7 +2,7 @@
# shellcheck disable=SC2034
dns_tencent_info='Tencent.com
Site: cloud.Tencent.com
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_tencent
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_tencent
Options:
Tencent_SecretId Secret ID
Tencent_SecretKey Secret Key

View File

@@ -6,7 +6,7 @@ Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_timeweb
Options:
TW_Token API JWT token. Get it from the control panel at https://timeweb.cloud/my/api-keys
Issues: github.com/acmesh-official/acme.sh/issues/5140
Author: Nikolay Pronchev <https://github.com/nikolaypronchev>
Author: Nikolay Pronchev <@nikolaypronchev>
'
TW_Api="https://api.timeweb.cloud/api/v1"

View File

@@ -24,7 +24,7 @@ dns_transip_add() {
_debug txtvalue="$txtvalue"
_transip_setup "$fulldomain" || return 1
_info "Creating TXT record."
if ! _transip_rest POST "domains/$_domain/dns" "{\"dnsEntry\":{\"name\":\"$_sub_domain\",\"type\":\"TXT\",\"content\":\"$txtvalue\",\"expire\":300}}"; then
if ! _transip_rest POST "domains/$_domain/dns" "{\"dnsEntry\":{\"name\":\"$_sub_domain\",\"type\":\"TXT\",\"content\":\"$txtvalue\",\"expire\":60}}"; then
_err "Could not add TXT record."
return 1
fi
@@ -38,7 +38,7 @@ dns_transip_rm() {
_debug txtvalue="$txtvalue"
_transip_setup "$fulldomain" || return 1
_info "Removing TXT record."
if ! _transip_rest DELETE "domains/$_domain/dns" "{\"dnsEntry\":{\"name\":\"$_sub_domain\",\"type\":\"TXT\",\"content\":\"$txtvalue\",\"expire\":300}}"; then
if ! _transip_rest DELETE "domains/$_domain/dns" "{\"dnsEntry\":{\"name\":\"$_sub_domain\",\"type\":\"TXT\",\"content\":\"$txtvalue\",\"expire\":60}}"; then
_err "Could not remove TXT record $_sub_domain for $domain"
return 1
fi

View File

@@ -7,7 +7,7 @@ Options:
UDR_USER Username
UDR_PASS Password
Issues: github.com/acmesh-official/acme.sh/issues/3923
Author: Andreas Scherer <https://github.com/andischerer>
Author: Andreas Scherer <@andischerer>
'
UDR_API="https://api.domainreselling.de/api/call.cgi"

View File

@@ -74,7 +74,7 @@ dns_variomedia_rm() {
return 1
fi
_record_id="$(echo "$response" | sed -E 's/,"tags":\[[^]]*\]//g' | cut -d '[' -f2 | cut -d']' -f1 | sed 's/},[ \t]*{/\},§\{/g' | tr § '\n' | grep "$_sub_domain" | grep -- "$txtvalue" | sed 's/^{//;s/}[,]?$//' | tr , '\n' | tr -d '\"' | grep ^id | cut -d : -f2 | tr -d ' ')"
_record_id="$(echo "$response" | sed -E 's/,"tags":\[[^]]*\]//g' | cut -d '[' -f3 | cut -d']' -f1 | sed 's/},[ \t]*{/\},§\{/g' | tr § '\n' | grep -i "$_sub_domain" | grep -- "$txtvalue" | sed 's/^{//;s/}[,]?$//' | tr , '\n' | tr -d '\"' | grep ^id | cut -d : -f2 | tr -d ' ')"
_debug _record_id "$_record_id"
if [ "$_record_id" ]; then
_info "Successfully retrieved the record id for ACME challenge."

View File

@@ -5,7 +5,7 @@ Site: vscale.io
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_vscale
Options:
VSCALE_API_KEY API Key
Author: Alex Loban <https://github.com/LAV45>
Author: Alex Loban <@LAV45>
'
VSCALE_API_URL="https://api.vscale.io/v1"

View File

@@ -6,7 +6,6 @@ Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_vultr
Options:
VULTR_API_KEY API Key
Issues: github.com/acmesh-official/acme.sh/issues/2374
Author:
'
VULTR_Api="https://api.vultr.com/v2"

View File

@@ -7,7 +7,7 @@ Options:
WS_ApiKey API Key. Called "Identifier" in the WS Admin
WS_ApiSecret API Secret. Called "Secret key" in the WS Admin
Issues: github.com/acmesh-official/acme.sh/issues/3486
Author: trgo.sk <https://github.com/trgosk>, akulumbeg <https://github.com/akulumbeg>
Author: trgo.sk <@trgosk>, @akulumbeg
'
# Requirements: API Key and Secret from https://admin.websupport.sk/en/auth/apiKey

View File

@@ -7,7 +7,7 @@ Options:
WORLD4YOU_USERNAME Username
WORLD4YOU_PASSWORD Password
Issues: github.com/acmesh-official/acme.sh/issues/3269
Author: Lorenz Stechauner <https://www.github.com/NerLOR>
Author: Lorenz Stechauner <@NerLOR>
'
WORLD4YOU_API="https://my.world4you.com/en"

View File

@@ -34,8 +34,8 @@ telegram_send() {
fi
_saveaccountconf_mutable TELEGRAM_BOT_URLBASE "$TELEGRAM_BOT_URLBASE"
_subject="$(printf "%s" "$_subject" | sed 's/\\/\\\\\\\\/g' | sed 's/\]/\\\\\]/g' | sed 's/\([_*[()~`>#+--=|{}.!]\)/\\\\\1/g')"
_content="$(printf "%s" "$_content" | sed 's/\\/\\\\\\\\/g' | sed 's/\]/\\\\\]/g' | sed 's/\([_*[()~`>#+--=|{}.!]\)/\\\\\1/g')"
_subject="$(printf "%s" "$_subject" | sed 's/\\/\\\\\\\\/g' | sed 's/\]/\\\\\]/g' | sed 's/\([-_*[()~`>#+\-=|{}.!]\)/\\\\\1/g')"
_content="$(printf "%s" "$_content" | sed 's/\\/\\\\\\\\/g' | sed 's/\]/\\\\\]/g' | sed 's/\([-_*[()~`>#+\-=|{}.!]\)/\\\\\1/g')"
_content="$(printf "*%s*\n%s" "$_subject" "$_content" | _json_encode)"
_data="{\"text\": \"$_content\", "
_data="$_data\"chat_id\": \"$TELEGRAM_BOT_CHATID\", "