Compare commits

...

207 Commits

Author SHA1 Message Date
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
Adrian Fedoreanu
184cb0b9a8
dns_1984.hosting.sh: fix session cookie name 2025-05-15 16:01:24 +02:00
neil
c2ccc1f980
Merge pull request #6347 from acmesh-official/dev 2025-05-08 15:18:55 -04: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
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
neil
676d76696b
Merge pull request #6328 from acmesh-official/dev
sync
2025-04-23 20:46:32 +02:00
neil
1d18432f8d
Merge pull request #6315 from alindt/fix-vault-curl
Fix error detection in vault deploy hook
2025-04-23 20:34:29 +02:00
neil
277f13ff9d
Merge pull request #6317 from ruuuch/edgecenter-dns
Add Edgecenter DNS API
2025-04-23 20:33:24 +02:00
rka
2d365efe56 Add Edgecenter DNS API 2025-04-18 02:48:48 +03:00
Alin Trăistaru
e81635f23d
check curl vault http return code 2025-04-18 00:25:10 +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
neil
9b267bb572
Merge pull request #6299 from Gontier-Julien/dev
Bump Alpine version from 3.17 to 3.21
2025-04-12 15:20:05 +01:00
Gontier Julien
20c7bfaff2
Bump Alpine version from 3.17 to 3.21
Signed-off-by: Gontier Julien <52894530+Gontier-Julien@users.noreply.github.com>
2025-04-11 20:09:03 +02: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
neil
1d87d9b370
Merge pull request #6287 from acmesh-official/dev
sync
2025-03-29 21:18:22 +01:00
neil
40b6db6a27 persist-credentials: false 2025-03-29 21:14:56 +01:00
neil
9ba662a53e
Merge pull request #6285 from ms264556/bugfix/deploy_ruckus_checkecc
Don't deploy ECC certs to legacy Ruckus
2025-03-29 21:08:25 +01:00
neil
dc228c3dbb
Merge pull request #6282 from magyarsz/dev
Fix error message variable when reload cmd returns with an error
2025-03-29 21:03:22 +01:00
magyarsz
dd5dd422d1
Merge branch 'dev' into dev 2025-03-28 21:19:12 +01:00
neil
73fdf4b957
Merge pull request #6286 from acmesh-official/dev
sync
2025-03-28 20:51:39 +01:00
neil
a1de13657e minor, just copy necessary files 2025-03-28 20:50:08 +01:00
ms264556
93c19356d0 Don't deploy ECC certs to legacy Ruckus 2025-03-27 14:03:08 +13:00
Szabolcs Magyar
8a27c902ff
Fix error message variable when reload cmd returns with an error 2025-03-25 19:29:12 +01:00
neil
be35b55444
Merge pull request #623 from kevinoid/fix-well-known-perms
Avoid permissions errors for chown .well-known
2025-03-23 21:20:23 +01:00
neil
a16ccf40c2
Merge pull request #6198 from lindsayzhou/fix-dns-netzner-error-regexp
fix regex of matching hetzner dns api error responses
2025-03-23 21:07:24 +01:00
Sikro
5c7b1cd5f2 add environment variable for notify/telegram.sh
Since the server in mainland China cannot connect to Telegram, add the TELEGRAM_BOT_URLBASE environment variable
Please add the proxy address you set. If this variable is not added, it will be the default value (https://api.telegram.org)
2025-03-23 21:00:34 +01:00
neil
fc5e593e2d
Merge pull request #6277 from beedaddy/ntfy_token
support NTFY_TOKEN
2025-03-21 20:07:34 +01:00
Martin Brodbeck
f1dc79f0b5 support NTFY_TOKEN 2025-03-20 10:13:12 +01:00
neil
34ea97dcea
Merge pull request #4384 from fraenki/cert_home
update documentation for --cert-home parameter
2025-03-19 11:04:06 +01:00
neil
f2931163aa
Merge pull request #6268 from wlallemand/haproxy-fix-secure-debug
haproxy: don't leak private keys when doing --debug
2025-03-15 11:09:55 +01:00
William Lallemand
7e320d2748 haproxy: don't leak private keys when doing --debug
It was reported in issue #6267 that the private key was leaked when
using the DEPLOY_HAPROXY_HOT_UPDATE=yes feature.

Indeed, the debugging code which sends commands to HAProxy was using
_debug even when passing the private key.

This patch fixes the issue by using _secure_debug when doing that.
2025-03-11 12:04:59 +01:00
neil
45660902ed
Merge pull request #6262 from copyliu/copyliu-routeros-dev
routeros: file-name should in quotes in newer (7.18) version
2025-03-09 19:45:30 +01:00
neil
72318cb687
Merge pull request #6263 from markkuleinio/dev
dns_he_ddns: Add empty dns_he_ddns_rm() to remove warnings
2025-03-09 19:41:59 +01:00
neil
f277223a65
Merge pull request #6257 from metaforget/master
cqhttp notify-hook:Compatibility with QQ Headless Project Napcat
2025-03-09 19:40:46 +01:00
Markku Leiniö
262fa74f5e dns_he_ddns: Add empty dns_he_ddns_rm() to remove warnings 2025-03-05 19:45:01 +02:00
copyliu
ebdd221c72
routeros: file-name should in quotes in newer (7.18) version 2025-03-05 11:33:41 +08:00
metaforget
2b8d162c9e
Merge branch 'dev' into master 2025-03-03 13:51:08 +08:00
metaforget
dc8cf0e02e
cqhttp notify-hook:Compatibility with QQ Headless Project Napcat 2025-03-03 10:37:38 +08:00
neil
a054f6a97b
Merge pull request #6240 from Giova96/master
Add FreeMyIP DNS Api Support
2025-03-02 13:06:25 +01:00
neil
63d11c3eea
Merge pull request #6245 from aafbsd/patch-1
Support FreeBSD's sed -i
2025-02-28 19:48:05 +01:00
Giovannimaria Gandossi
dd2199c472 Adding support for FreeMyIP dns API 2025-02-24 17:03:22 +01:00
aafbsd
661c3bbdb0
Support FreeBSD's sed -i
FreeBSD's sed requires an extension for the backup file when -i is used. However, it may be zero-length to indicate that no backup is desired.
2025-02-24 12:33:42 +00:00
neil
5236e81068 version 3.1.1 2025-02-23 09:28:14 +01:00
neil
c459b5eb9d
Merge pull request #6243 from acmesh-official/dev
sync
2025-02-23 09:26:46 +01:00
neil
384f0fb89a
Merge pull request #6242 from NerLOR/master
dns_world4you: Adapt to change in world4you.com
2025-02-23 09:25:10 +01:00
Lorenz Stechauner
1d628bcf9e dns_world4you: Adapt to change in world4you.com 2025-02-22 18:56:15 +01:00
neil
b8644f49ec
Merge pull request #6239 from scruel/patch-1
chore(synology): remove personal mail
2025-02-20 10:38:47 +01:00
Scruel Tao
091c1f7ef1
chore(synology): remove personal mail 2025-02-20 17:16:23 +08:00
neil
c838c3fa73
Merge pull request #6234 from tomo2403/deployhook-docker
Allow deploying of PKCS to docker containers and other hooks
2025-02-19 21:31:09 +01:00
neil
8bb9106b42
Merge pull request #6226 from davidha/fix-saved-local-hostname
Fix bug saving SYNO_LOCAL_HOSTNAME
2025-02-19 21:29:24 +01:00
tomo
a7e3b0d184
Merge branch 'dev' into deployhook-docker 2025-02-15 20:24:05 +01:00
tomo
449c542d5a
fixed env warning while building 2025-02-15 17:16:07 +01:00
tomo
7f19f8cf0e
implemented deploying pfx to docker container 2025-02-15 15:18:10 +01:00
tomo
b2eb704394
allowed deploying of pfx cert 2025-02-15 15:16:52 +01:00
David Ha
899da0c6ea Update synology_dsm.sh
fix check logic
2025-02-09 23:43:55 +00:00
David Ha
def41a7878 Fix bug saving SYNO_LOCAL_HOSTNAME
Exporting SYNO_LOCAL_HOSTNAME=1 only works on the first deploy invocation. Subsequent deploys using the saved config fails because it saves $SYNO_HOSTNAME instead of $SYNO_LOCAL_HOSTNAME.
2025-02-09 22:56:46 +00:00
neil
6dbe49089e minor 2025-01-25 21:00:34 +01:00
neil
5883e5159f
Merge pull request #6202 from ARNik/dns_beget
Add Beget.com DNS API support
2025-01-18 19:46:24 +01:00
ARNik
26a5d48f63 dnsapi: Beget.com fix case-sensitive domain names 2025-01-16 21:57:41 +03:00
ARNik
aa6feb4b62 dnsapi: Beget.com fix rm record 2025-01-16 20:55:56 +03:00
ARNik
2fef0ebea8 dnsapi: Beget.com subdomain preparation rework 2025-01-16 20:55:56 +03:00
ARNik
66dd51a09a dnsapi: check Beget.com API replies 2025-01-16 19:45:26 +03:00
ARNik
077868604d dnsapi: Add Beget.com DNS API 2025-01-15 09:40:31 +03:00
Lindsay Zhou
6c555cb666
fix regex of matching hetzner dns api error responses 2025-01-13 11:11:36 +08:00
neil
0ae80272fb
Merge pull request #5237 from markkuleinio/add-he-ddns
Add dnsapi script for HE DDNS
2025-01-11 10:48:12 +01:00
neil
7bef7cf9e2
Merge pull request #6108 from Attackwave/Truenas-Deploy-Websocket
Truenas deploy websocket
2025-01-11 10:46:40 +01:00
neil
6fa24cad18
Merge pull request #6195 from 3VAbdAVE/dev
Fixed cloudkey gen 2 changes
2025-01-10 20:40:28 +01:00
neil
6313020ba9
Merge pull request #6194 from ghen2/hostname
Portability: use `uname -n` instead of `hostname`.
2025-01-10 20:39:45 +01:00
3VAbdAVE
b2eead386d Merge remote-tracking branch 'upstream/dev' into dev 2025-01-10 09:19:29 -05:00
Geert Hendrickx
b826123d98
Portability: use uname -n instead of hostname.
`hostname` is not available by default on some modern Linux distributions
(eg. Arch Linux), whereas `uname -n` is defined by POSIX, so guaranteed.
2025-01-10 12:39:30 +01:00
neil
cf537070d8
Merge pull request #5128 from vlad-kms/master
Feature: Add new version API dns SELECTEL dns_selectel.sh (actual v2)
2025-01-09 22:44:16 +01:00
neil
7a00a1dced
Merge pull request #6191 from HTDemony/master
Create script for deploy certificate on Proxmox Backup Server
2025-01-09 22:42:01 +01:00
Demony
80c37ebab2
Merge branch 'dev' into master 2025-01-08 21:37:21 +01:00
neil
5fb4136bcd
Merge pull request #6190 from jpmens/patch-2
Correct typo in acme.sh
2025-01-08 19:23:22 +01:00
Yann DEMOULIN
6a249ca1dd Create script for deploy certificate on Proxmox Backup Server 2025-01-07 22:23:27 +01:00
Jan-Piet Mens
5610d4782f
Correct typo in acme.sh 2025-01-07 13:13:27 +01:00
Markku Leiniö
fef4574c3c Fix option description 2025-01-04 21:34:14 +02:00
neil
65239c7c25
Merge pull request #6184 from JazzMalar/master
Update dns_cyon plugin
2025-01-04 10:43:54 +01:00
Markus Schenk
1b123054b3 also updated the other validation functions 2025-01-03 13:08:18 +01:00
Markus Schenk
b29d17f522 changed char class for compatibility with openbsd 2025-01-03 13:04:04 +01:00
Markus Schenk
04aefbf28d moved the env change check to its own function 2025-01-03 00:31:27 +01:00
Attackwave
f8fcf02448
Merge branch 'acmesh-official:master' into Truenas-Deploy-Websocket 2025-01-02 19:44:24 +01:00
Markus Schenk
0e4fd5269b run tests 2025-01-02 00:49:20 +01:00
Markus Schenk
6328496bfb run tests 2025-01-02 00:41:59 +01:00
Markus Schenk
524b40b75f adhere to shell formatting 2025-01-02 00:36:28 +01:00
Markus Schenk
0241552c0d update dns_cyon for updated field names and add validation 2025-01-02 00:26:47 +01:00
neil
fd3f6687ce
Merge pull request #6099 from catap/openbsd
Fix syntax for OpenBSD sh
2025-01-01 15:14:25 +01:00
neil
93d63749e6
Merge pull request #6178 from peterv99/dev
Add mijn.host DNS API Support
2025-01-01 15:10:45 +01:00
neil
41bf5450d9
Merge pull request #6181 from M0NsTeRRR/master
fix: remove control characters
2025-01-01 11:24:21 +01:00
Ludovic Ortega
d37553e7b8
fix: remove control characters
Signed-off-by: Ludovic Ortega <ludovic.ortega@adminafk.fr>
2024-12-31 18:04:39 +01:00
neil
101d7138c2
Merge pull request #6176 from stokito/dnsapi_struct_info_missing
dnsapi: Add structured info for lima-city and west.cn
2024-12-31 11:24:02 +01:00
Sergey Ponomarev
11de3aed51 dnsapi: structured info minor fixes
Extend dns_myapi.sh info with samples.
omglol: Split Address option by a dot to avoid incorrect parsing with a dot from omg.lol

Signed-off-by: Sergey Ponomarev <stokito@gmail.com>
2024-12-31 11:45:29 +02:00
3VAbdAVE
4bb41cf886 Fixed bad variable. 2024-12-29 12:52:09 -05:00
peterv99
1ae7dd9b11
Updated info block 2024-12-29 09:30:48 +01:00
Sergey Ponomarev
a3250fac6d dnsapi: Add structured info for lima-city and west.cn
Signed-off-by: Sergey Ponomarev <stokito@gmail.com>
2024-12-28 22:31:24 +02:00
neil
fabe6317ef
Merge pull request #6174 from stokito/dns_api_fix
DNS API: fix structural info for new providers
2024-12-28 20:05:44 +01:00
3VAbdAVE
23a4af9a52
Merge branch 'acmesh-official:master' into master 2024-12-28 13:19:17 -05:00
peterv99
9526dbadad mijn.host API unreliable, upped retry times to 15 2024-12-28 17:26:52 +01:00
Sergey Ponomarev
6ad469c637 dnsapi: Fix structured info
Signed-off-by: Sergey Ponomarev <stokito@gmail.com>
2024-12-28 17:42:58 +02:00
peterv99
b30cdf0873 Merge branch 'dev' of github.com:peterv99/acme.sh into dev 2024-12-28 13:35:53 +01:00
peterv99
234bc93ddb Removed superfluous debug message 2024-12-28 12:42:50 +01:00
peterv99
42862852b8 Corrected sleep message 2024-12-28 12:41:26 +01:00
peterv99
7a6101c417 Corrected sleep time message 2024-12-28 10:22:28 +01:00
peterv99
588123ed11 Updated backoff algorithm 2024-12-27 23:56:13 +01:00
peterv99
ac9852f9df Added fix for specific API error that mijn.host sometimes throws. 2024-12-27 16:47:02 +01:00
peterv99
d093476da5 Refactored REST calls to add generic retries,
as mijn.host API times out at times.
2024-12-27 12:55:12 +01:00
peterv99
9ad794f2cc Name change, function name change to mijnhost 2024-12-27 08:56:16 +01:00
peterv99
b0f566a80d Name change to be in line with other API scripts
Added time-out to _get calls at 120s to fix API timeouts
2024-12-26 23:36:55 +01:00
Vladimir Alexeev
d3f3a70350 keep content:
# 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
2024-12-26 08:45:03 +10:00
Vladimir Alexeev
3f262fe872 keep content:
# 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
2024-12-26 08:12:36 +10:00
peterv99
150c708726 Better debug messages for root domain detection 2024-12-25 14:11:52 +01:00
peterv99
7512dbffbb Fixed yet another error in dns_rm 2024-12-25 09:50:27 +01:00
peterv99
3cfa882fe1 Fixed error in dns_mijn_host_rm 2024-12-25 08:52:09 +01:00
peterv99
35f3b7088d Updated PUT request to hold only fqdn domain name values# 2024-12-25 00:00:19 +01:00
peterv99
07220a324d Removed all jq references 2024-12-24 23:21:50 +01:00
peterv99
c7cecd5b4f Removed "." from _sub_domain to create a valid domain. 2024-12-24 17:30:50 +01:00
peterv99
ab1a2045d9 Made string removal in root domain detection posix compliant 2024-12-24 17:10:30 +01:00
peterv99
5e9a067e87 Fixed root domain detection and processing. 2024-12-24 16:55:26 +01:00
Peter Vos
4cc460be64 Added dns challenge for mijn.host 2024-12-24 13:33:15 +01:00
Vladimir Alexeev
112bfccfd5 fix _get_root() make it start from 1. because we need to support --domain-alias (only version api actual v2) 2024-12-24 16:00:01 +10:00
Владимир Алексеев
b29cb08956
Merge branch 'acmesh-official:master' into master 2024-12-23 14:51:57 +10:00
Vladimir Alexeev
e42ad9be24 fix
don't use debug3, use debug2
  add # shellcheck disable=SC2034
2024-12-23 14:50:28 +10:00
neil
02da1700e0 fix format 2024-12-22 14:19:58 +01:00
neil
a9f97e1fe2 fix format 2024-12-22 14:17:33 +01:00
neil
d75077c6f9 fix format 2024-12-22 13:36:43 +01:00
neil
b81939f02d
Merge pull request #6168 from adn77/master
Fix keystore ownership in Unifi deployment - unifi.sh
2024-12-22 13:26:26 +01:00
Alex
0e1d90dd0c
Properly guess system.properties location 2024-12-20 10:36:45 +01:00
Alex
03e9c612b9
Correct file ownership according to keystore directory 2024-12-20 10:34:51 +01:00
neil
f981c782bb
Merge pull request #6160 from acmesh-official/dev
sync
2024-12-10 21:00:22 +01:00
neil
3fbdb7a007 fix format 2024-12-10 20:58:02 +01:00
neil
2997a15ba8
Merge pull request #6136 from lukavia/dev
Add support for ZoneEdit.com
2024-12-09 09:22:54 +01:00
neil
b4c02eca16
Merge pull request #6114 from NerLOR/master
dns_world4you: Adapt to change in world4you.com DeleteDnsRecordForm
2024-12-07 23:01:16 +01:00
neil
223dc87cef usage 2024-12-07 22:50:12 +01:00
neil
3ccdae0634
Merge pull request #4832 from kchiem/dev
deploy hook for Ruckus ZoneDirector / Unleashed
2024-12-07 22:24:04 +01:00
Anton Avramov
11af6f49c6 Add support for ZoneEdit.com 2024-12-05 22:12:04 -05:00
Kevin Locke
22dfedf6ae Avoid permissions errors for chown .well-known
When acme.sh is run as a non-root user different from the owner of the
webroot directory it is unable to change the owner of the files in
.well-known to that user, causing permissions errors.  Avoid this by
making the files world-readable.

These files should pose no disclosure risk since they are sent in
cleartext during the HTTP Identifier Validation Challenge[1] and may
already be exposed by directory enumeration, depending on server
settings.  AFAIK they should be safe to expose as world-readable in all
cases.

1.  https://ietf-wg-acme.github.io/acme/#rfc.section.7.2

Fixes Neilpang/acme.sh#32
Signed-off-by: Kevin Locke <kevin@kevinlocke.name>
2024-11-30 16:34:52 -07:00
Lorenz Stechauner
157d3f9dc9 dns_word4you: Fix for OpenBSD 2024-11-30 20:25:55 +01:00
3VAbdAVE
f3b2ab5e60 Fix gen1 cloudkey's symlinked keystore 2024-11-30 10:32:24 -05:00
neil
fca53a0c2c
Merge pull request #6117 from qupfer/technitum
add dns_api for Technitum dns server
2024-11-28 12:22:52 +01:00
Henning Reich
6d302889fa small cleanup 2024-11-27 10:11:50 +01:00
Henning Reich
a94ac63e74 add issue link 2024-11-27 09:20:57 +01:00
Henning Reich
19e73f8889 add account to rm function 2024-11-26 12:29:07 +01:00
Henning Reich
aacc26179e remove comments 2024-11-26 12:17:11 +01:00
Henning Reich
36f060dcf6 add author 2024-11-26 11:42:05 +01:00
Henning Reich
cb12c8dfd5 working example 2024-11-26 11:21:17 +01:00
Lorenz Stechauner
9cd1d1a9dc dns_world4you: Adapt to change in world4you.com DeleteDnsRecordForm 2024-11-26 09:20:18 +01:00
Attackwave
ebaf4c9c01
Update truenas_ws.sh (Output new certificate name) 2024-11-25 21:23:59 +01:00
Attackwave
44240339d9
Update truenas_ws.sh (Interpreter changed from bash to sh) 2024-11-25 21:13:43 +01:00
Attackwave
9110375173
Update truenas_ws.sh (fixed shfmt) 2024-11-25 20:50:40 +01:00
qupfer
c3557bbe3f
1 2024-11-25 20:26:23 +01:00
Henning Reich
cd924099e4 add template 2024-11-25 17:46:59 +00:00
Attackwave
f2a311bb81
Update truenas_ws.sh (added return instead exit) 2024-11-25 14:44:52 +01:00
Attackwave
d7855e8fe5
Update truenas_ws.sh (shfmt and shellcheck) 2024-11-24 14:59:51 +01:00
Attackwave
1e52efa867
Merge branch 'acmesh-official:master' into Truenas-Deploy-Websocket 2024-11-24 14:39:58 +01:00
Kirill A. Korinsky
3b8608761b
Fix syntax for OpenBSD sh 2024-11-17 20:58:06 +01:00
Attackwave
413a91646c
Create truenas_ws.sh 2024-11-16 19:15:39 +01:00
kchiem
4299c6a802
Merge pull request #5 from ms264556/dev
Remove awk usage & refuse host redirects
2024-11-15 00:32:43 -08:00
ms264556
4232923641 Remove awk usage and refuse redirect to new host 2024-11-15 12:39:41 +13:00
kchiem
412e14a41c
Merge pull request #4 from ms264556/dev
fix acme.sh PR shfmt failure
2024-11-13 12:04:16 -08:00
ms264556
2bb5fbdee5 Remove HTTPS_INSECURE 2024-11-14 07:21:19 +13:00
ms264556
38c41b72d6 fix acme.sh PR shfmt failure 2024-11-14 07:16:38 +13:00
kchiem
b665014707
Merge pull request #3 from ms264556/dev
Fix info logging
2024-11-12 21:11:22 -08:00
ms264556
e98e7a232f Fix info logging 2024-11-13 17:27:36 +13:00
kchiem
5f7ad72d45
Merge pull request #2 from ms264556/dev
fix insecure password debug and _info typo
2024-11-12 18:19:03 -08:00
ms264556
0cc74b7cfe fix insecure password debug and _info typo 2024-11-13 12:50:51 +13:00
kchiem
d7bafa6dfa
Merge pull request #1 from ms264556/dev
Rewrite deploy/ruckus.sh to use _get() and _post()
2024-11-12 00:38:37 -08:00
ms264556
717802611a remove dead code 2024-11-10 22:43:57 +13:00
ms264556
b6a77e0231 Ruckus - use _get() and _post() 2024-11-10 22:12:38 +13:00
Владимир Алексеев
e8ed8cbfcd
Merge branch 'dev' into master 2024-10-22 05:25:27 +10:00
Markku Leiniö
833632eee3 Add shellcheck disable=SC2034 for the info variable 2024-08-04 19:15:11 +03:00
Markku Leiniö
abc76299c0 Fix documentation link 2024-08-04 18:58:59 +03:00
Markku Leiniö
c96fcf319a Remove dns_he_ddns_rm(), not used 2024-08-04 18:25:20 +03:00
Markku Leiniö
5214a7c3ec Add dnsapi script for HE DDNS 2024-08-04 18:19:21 +03:00
Владимир Алексеев
51151293d7
Remove date -d on macOS 2024-07-06 21:01:25 +10:00
Владимир Алексеев
a42fef0905
Merge branch 'dev' into master 2024-05-20 07:05:06 +10:00
Владимир Алексеев
95b51cf9e6
Merge branch 'acmesh-official:master' into master 2024-05-05 09:42:54 +10:00
Vladimir Alexeev
a375e924b4 translation of comments into English 2024-05-05 07:42:22 +10:00
Vladimir Alexeev
d989617825 set default SL_Ver to v1 2024-05-04 20:42:38 +10:00
Vladimir Alexeev
177d9b7cb0 set default SL_Ver to v1 2024-05-04 20:38:42 +10:00
Vladimir Alexeev
7a1305c1bb fix del record for v1, delete one entry at a time 2024-05-04 19:12:42 +10:00
Vladimir Alexeev
8bb29f53d1 test DNS for v1 (legacy) 003 2024-04-30 16:15:45 +10:00
Vladimir Alexeev
2e3c1ef4ac test DNS for v1 (legacy) 003 2024-04-30 13:49:53 +10:00
Vladimir Alexeev
ada7e12b5a test DNS for v1 (legacy) 003 2024-04-30 11:03:53 +10:00
Vladimir Alexeev
b8949ba3dd test DNS for v1 (legacy) 002 2024-04-30 10:01:50 +10:00
Vladimir Alexeev
73fe47ba79 test DNS for v1 (legacy) 001 2024-04-30 09:57:49 +10:00
Vladimir Alexeev
577920de86 test DNS for v2 (actual) 001 2024-04-30 08:36:36 +10:00
Vladimir Alexeev
517baa3235 test DNS for v2 (actual) 2024-04-29 22:54:31 +10:00
Keith Chiem
ed72b090af deploy hook for Ruckus ZoneDirector / Unleashed 2023-10-18 20:32:39 -07:00
Frank Wall
778ee02803 update documentation for --cert-home
Although the main use-case may be the --install command, this command
also proves to be useful for the --signcsr and --issue commands.
2022-11-07 23:43:37 +01:00
42 changed files with 2666 additions and 239 deletions

View File

@ -44,6 +44,8 @@ jobs:
steps:
- name: checkout code
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Extract Docker metadata

View File

@ -20,11 +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.
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, 都必须确保通过这个测试.
`
})

View File

@ -1,4 +1,4 @@
FROM alpine:3.17
FROM alpine:3.21
RUN apk --no-cache add -f \
openssl \
@ -15,14 +15,18 @@ RUN apk --no-cache add -f \
jq \
cronie
ENV LE_CONFIG_HOME /acme.sh
ENV LE_CONFIG_HOME=/acme.sh
ARG AUTO_UPGRADE=1
ENV AUTO_UPGRADE $AUTO_UPGRADE
ENV AUTO_UPGRADE=$AUTO_UPGRADE
#Install
COPY ./ /install_acme.sh/
COPY ./acme.sh /install_acme.sh/acme.sh
COPY ./deploy /install_acme.sh/deploy
COPY ./dnsapi /install_acme.sh/dnsapi
COPY ./notify /install_acme.sh/notify
RUN cd /install_acme.sh && ([ -f /install_acme.sh/acme.sh ] && /install_acme.sh/acme.sh --install || curl https://get.acme.sh | sh) && rm -rf /install_acme.sh/

30
acme.sh
View File

@ -1,6 +1,6 @@
#!/usr/bin/env sh
VER=3.1.0
VER=3.1.2
PROJECT_NAME="acme.sh"
@ -921,6 +921,9 @@ _sed_i() {
if sed -h 2>&1 | grep "\-i\[SUFFIX]" >/dev/null 2>&1; then
_debug "Using sed -i"
sed -i "$options" "$filename"
elif sed -h 2>&1 | grep "\-i extension" >/dev/null 2>&1; then
_debug "Using FreeBSD sed -i"
sed -i "" "$options" "$filename"
else
_debug "No -i support in sed"
text="$(cat "$filename")"
@ -5002,9 +5005,11 @@ $_authorizations_map"
_debug "Writing token: $token to $wellknown_path/$token"
mkdir -p "$wellknown_path"
if ! printf "%s" "$keyauthorization" >"$wellknown_path/$token"; then
# Ensure .well-known is visible to web server user/group
# https://github.com/Neilpang/acme.sh/pull/32
if ! (umask ugo+rx &&
mkdir -p "$wellknown_path" &&
printf "%s" "$keyauthorization" >"$wellknown_path/$token"); then
_err "$d: Cannot write token to file: $wellknown_path/$token"
_clearupwebbroot "$_currentRoot" "$removelevel" "$token"
_clearup
@ -5499,6 +5504,13 @@ renew() {
if [ -z "$Le_Keylength" ]; then
Le_Keylength=2048
fi
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"
res="$?"
if [ "$res" != "0" ]; then
@ -5818,7 +5830,7 @@ _deploy() {
return 1
fi
if ! $d_command "$_d" "$CERT_KEY_PATH" "$CERT_PATH" "$CA_CERT_PATH" "$CERT_FULLCHAIN_PATH"; then
if ! $d_command "$_d" "$CERT_KEY_PATH" "$CERT_PATH" "$CA_CERT_PATH" "$CERT_FULLCHAIN_PATH" "$CERT_PFX_PATH"; then
_err "Error deploying for domain: $_d"
return 1
fi
@ -5981,7 +5993,7 @@ _installcert() {
); then
_info "$(__green "Reload successful")"
else
_err "Reload error for: $Le_Domain"
_err "Reload error for: $_main_domain"
fi
fi
@ -6061,7 +6073,7 @@ installcronjob() {
_script="$(_readlink "$_SCRIPT_")"
_debug _script "$_script"
if [ -f "$_script" ]; then
_info "Usinging the current script from: $_script"
_info "Using the current script from: $_script"
lesh="$_script"
else
_err "Cannot install cronjob, $PROJECT_ENTRY not found."
@ -6813,7 +6825,7 @@ _send_notify() {
_nsource="$NOTIFY_SOURCE"
if [ -z "$_nsource" ]; then
_nsource="$(hostname)"
_nsource="$(uname -n)"
fi
_nsubject="$_nsubject by $_nsource"
@ -7015,7 +7027,7 @@ Parameters:
--accountconf <file> Specifies a customized account config file.
--home <directory> Specifies the home dir for $PROJECT_NAME.
--cert-home <directory> Specifies the home dir to save all the certs, only valid for '--install' command.
--cert-home <directory> Specifies the home dir to save all the certs.
--config-home <directory> Specifies the home dir to save all the configurations.
--useragent <string> Specifies the user agent string. it will be saved for future use too.
-m, --email <email> Specifies the account email, only valid for the '--install' and '--update-account' command.

View File

@ -18,6 +18,7 @@ docker_deploy() {
_ccert="$3"
_cca="$4"
_cfullchain="$5"
_cpfx="$6"
_debug _cdomain "$_cdomain"
_getdeployconf DEPLOY_DOCKER_CONTAINER_LABEL
_debug2 DEPLOY_DOCKER_CONTAINER_LABEL "$DEPLOY_DOCKER_CONTAINER_LABEL"
@ -88,6 +89,12 @@ docker_deploy() {
_savedeployconf DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE "$DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE"
fi
_getdeployconf DEPLOY_DOCKER_CONTAINER_PFX_FILE
_debug2 DEPLOY_DOCKER_CONTAINER_PFX_FILE "$DEPLOY_DOCKER_CONTAINER_PFX_FILE"
if [ "$DEPLOY_DOCKER_CONTAINER_PFX_FILE" ]; then
_savedeployconf DEPLOY_DOCKER_CONTAINER_PFX_FILE "$DEPLOY_DOCKER_CONTAINER_PFX_FILE"
fi
_getdeployconf DEPLOY_DOCKER_CONTAINER_RELOAD_CMD
_debug2 DEPLOY_DOCKER_CONTAINER_RELOAD_CMD "$DEPLOY_DOCKER_CONTAINER_RELOAD_CMD"
if [ "$DEPLOY_DOCKER_CONTAINER_RELOAD_CMD" ]; then
@ -125,6 +132,12 @@ docker_deploy() {
fi
fi
if [ "$DEPLOY_DOCKER_CONTAINER_PFX_FILE" ]; then
if ! _docker_cp "$_cid" "$_cpfx" "$DEPLOY_DOCKER_CONTAINER_PFX_FILE"; then
return 1
fi
fi
if [ "$DEPLOY_DOCKER_CONTAINER_RELOAD_CMD" ]; then
_info "Reloading: $DEPLOY_DOCKER_CONTAINER_RELOAD_CMD"
if ! _docker_exec "$_cid" "$DEPLOY_DOCKER_CONTAINER_RELOAD_CMD"; then

View File

@ -357,7 +357,7 @@ haproxy_deploy() {
_info "Update existing certificate '${_pem}' over HAProxy ${_socketname}."
fi
_socat_cert_set_cmd="echo -e '${_cmdpfx}set ssl cert ${_pem} <<\n$(cat "${_pem}")\n' | socat '${_statssock}' - | grep -q 'Transaction created'"
_debug _socat_cert_set_cmd "${_socat_cert_set_cmd}"
_secure_debug _socat_cert_set_cmd "${_socat_cert_set_cmd}"
eval "${_socat_cert_set_cmd}"
_ret=$?
if [ "${_ret}" != "0" ]; then

120
deploy/proxmoxbs.sh Normal file
View File

@ -0,0 +1,120 @@
#!/usr/bin/env sh
# Deploy certificates to a proxmox backup server using the API.
#
# Environment variables that can be set are:
# `DEPLOY_PROXMOXBS_SERVER`: The hostname of the proxmox backup server. Defaults to
# _cdomain.
# `DEPLOY_PROXMOXBS_SERVER_PORT`: The port number the management interface is on.
# Defaults to 8007.
# `DEPLOY_PROXMOXBS_USER`: The user we'll connect as. Defaults to root.
# `DEPLOY_PROXMOXBS_USER_REALM`: The authentication realm the user authenticates
# with. Defaults to pam.
# `DEPLOY_PROXMOXBS_API_TOKEN_NAME`: The name of the API token created for the
# user account. Defaults to acme.
# `DEPLOY_PROXMOXBS_API_TOKEN_KEY`: The API token. Required.
proxmoxbs_deploy() {
_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"
# "Sane" defaults.
_getdeployconf DEPLOY_PROXMOXBS_SERVER
if [ -z "$DEPLOY_PROXMOXBS_SERVER" ]; then
_target_hostname="$_cdomain"
else
_target_hostname="$DEPLOY_PROXMOXBS_SERVER"
_savedeployconf DEPLOY_PROXMOXBS_SERVER "$DEPLOY_PROXMOXBS_SERVER"
fi
_debug2 DEPLOY_PROXMOXBS_SERVER "$_target_hostname"
_getdeployconf DEPLOY_PROXMOXBS_SERVER_PORT
if [ -z "$DEPLOY_PROXMOXBS_SERVER_PORT" ]; then
_target_port="8007"
else
_target_port="$DEPLOY_PROXMOXBS_SERVER_PORT"
_savedeployconf DEPLOY_PROXMOXBS_SERVER_PORT "$DEPLOY_PROXMOXBS_SERVER_PORT"
fi
_debug2 DEPLOY_PROXMOXBS_SERVER_PORT "$_target_port"
# Complete URL.
_target_url="https://${_target_hostname}:${_target_port}/api2/json/nodes/localhost/certificates/custom"
_debug TARGET_URL "$_target_url"
# More "sane" defaults.
_getdeployconf DEPLOY_PROXMOXBS_USER
if [ -z "$DEPLOY_PROXMOXBS_USER" ]; then
_proxmoxbs_user="root"
else
_proxmoxbs_user="$DEPLOY_PROXMOXBS_USER"
_savedeployconf DEPLOY_PROXMOXBS_USER "$DEPLOY_PROXMOXBS_USER"
fi
_debug2 DEPLOY_PROXMOXBS_USER "$_proxmoxbs_user"
_getdeployconf DEPLOY_PROXMOXBS_USER_REALM
if [ -z "$DEPLOY_PROXMOXBS_USER_REALM" ]; then
_proxmoxbs_user_realm="pam"
else
_proxmoxbs_user_realm="$DEPLOY_PROXMOXBS_USER_REALM"
_savedeployconf DEPLOY_PROXMOXBS_USER_REALM "$DEPLOY_PROXMOXBS_USER_REALM"
fi
_debug2 DEPLOY_PROXMOXBS_USER_REALM "$_proxmoxbs_user_realm"
_getdeployconf DEPLOY_PROXMOXBS_API_TOKEN_NAME
if [ -z "$DEPLOY_PROXMOXBS_API_TOKEN_NAME" ]; then
_proxmoxbs_api_token_name="acme"
else
_proxmoxbs_api_token_name="$DEPLOY_PROXMOXBS_API_TOKEN_NAME"
_savedeployconf DEPLOY_PROXMOXBS_API_TOKEN_NAME "$DEPLOY_PROXMOXBS_API_TOKEN_NAME"
fi
_debug2 DEPLOY_PROXMOXBS_API_TOKEN_NAME "$_proxmoxbs_api_token_name"
# This is required.
_getdeployconf DEPLOY_PROXMOXBS_API_TOKEN_KEY
if [ -z "$DEPLOY_PROXMOXBS_API_TOKEN_KEY" ]; then
_err "API key not provided."
return 1
else
_proxmoxbs_api_token_key="$DEPLOY_PROXMOXBS_API_TOKEN_KEY"
_savedeployconf DEPLOY_PROXMOXBS_API_TOKEN_KEY "$DEPLOY_PROXMOXBS_API_TOKEN_KEY"
fi
_debug2 DEPLOY_PROXMOXBS_API_TOKEN_KEY "$_proxmoxbs_api_token_key"
# PBS API Token header value. Used in "Authorization: PBSAPIToken".
_proxmoxbs_header_api_token="${_proxmoxbs_user}@${_proxmoxbs_user_realm}!${_proxmoxbs_api_token_name}:${_proxmoxbs_api_token_key}"
_debug2 "Auth Header" "$_proxmoxbs_header_api_token"
# Ugly. I hate putting heredocs inside functions because heredocs don't
# account for whitespace correctly but it _does_ work and is several times
# cleaner than anything else I had here.
#
# This dumps the json payload to a variable that should be passable to the
# _psot function.
_json_payload=$(
cat <<HEREDOC
{
"certificates": "$(tr '\n' ':' <"$_cfullchain" | sed 's/:/\\n/g')",
"key": "$(tr '\n' ':' <"$_ckey" | sed 's/:/\\n/g')",
"node":"localhost",
"restart":true,
"force":true
}
HEREDOC
)
_debug2 Payload "$_json_payload"
_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"
}

View File

@ -144,8 +144,8 @@ source=\"/certificate remove [ find name=$_cdomain.cer_0 ];\
\n/certificate remove [ find name=$_cdomain.cer_1 ];\
\n/certificate remove [ find name=$_cdomain.cer_2 ];\
\ndelay 1;\
\n/certificate import file-name=$_cdomain.cer passphrase=\\\"\\\";\
\n/certificate import file-name=$_cdomain.key passphrase=\\\"\\\";\
\n/certificate import file-name=\\\"$_cdomain.cer\\\" passphrase=\\\"\\\";\
\n/certificate import file-name=\\\"$_cdomain.key\\\" passphrase=\\\"\\\";\
\ndelay 1;\
\n:do {/file remove $_cdomain.cer; } on-error={ }\
\n:do {/file remove $_cdomain.key; } on-error={ }\

200
deploy/ruckus.sh Executable file
View File

@ -0,0 +1,200 @@
#!/usr/bin/env sh
# Here is a script to deploy cert to Ruckus ZoneDirector / Unleashed.
#
# Public domain, 2024, Tony Rielly <https://github.com/ms264556>
#
# ```sh
# acme.sh --deploy -d ruckus.example.com --deploy-hook ruckus
# ```
#
# Then you need to set the environment variables for the
# deploy script to work.
#
# ```sh
# export RUCKUS_HOST=myruckus.example.com
# export RUCKUS_USER=myruckususername
# export RUCKUS_PASS=myruckuspassword
#
# acme.sh --deploy -d myruckus.example.com --deploy-hook ruckus
# ```
#
# returns 0 means success, otherwise error.
######## Public functions #####################
#domain keyfile certfile cafile fullchain
ruckus_deploy() {
_cdomain="$1"
_ckey="$2"
_ccert="$3"
_cca="$4"
_cfullchain="$5"
_err_code=0
_debug _cdomain "$_cdomain"
_debug _ckey "$_ckey"
_debug _ccert "$_ccert"
_debug _cca "$_cca"
_debug _cfullchain "$_cfullchain"
_getdeployconf RUCKUS_HOST
_getdeployconf RUCKUS_USER
_getdeployconf RUCKUS_PASS
if [ -z "$RUCKUS_HOST" ]; then
_debug "Using _cdomain as RUCKUS_HOST, please set if not correct."
RUCKUS_HOST="$_cdomain"
fi
if [ -z "$RUCKUS_USER" ]; then
_err "Need to set the env variable RUCKUS_USER"
return 1
fi
if [ -z "$RUCKUS_PASS" ]; then
_err "Need to set the env variable RUCKUS_PASS"
return 1
fi
_savedeployconf RUCKUS_HOST "$RUCKUS_HOST"
_savedeployconf RUCKUS_USER "$RUCKUS_USER"
_savedeployconf RUCKUS_PASS "$RUCKUS_PASS"
_debug RUCKUS_HOST "$RUCKUS_HOST"
_debug RUCKUS_USER "$RUCKUS_USER"
_secure_debug RUCKUS_PASS "$RUCKUS_PASS"
export ACME_HTTP_NO_REDIRECTS=1
_info "Discovering the login URL"
_get "https://$RUCKUS_HOST" >/dev/null
_login_url="$(_response_header 'Location')"
if [ -n "$_login_url" ]; then
_login_path=$(echo "$_login_url" | sed 's|https\?://[^/]\+||')
if [ -z "$_login_path" ]; then
# redirect was to a different host
_err "Connection failed: redirected to a different host. Configure Unleashed with a Preferred Master or Management Interface."
return 1
fi
fi
if [ -z "${_login_url}" ]; then
_err "Connection failed: couldn't find login page."
return 1
fi
_base_url=$(dirname "$_login_url")
_login_page=$(basename "$_login_url")
if [ "$_login_page" = "index.html" ]; then
_err "Connection temporarily unavailable: Unleashed Rebuilding."
return 1
fi
if [ "$_login_page" = "wizard.jsp" ]; then
_err "Connection failed: Setup Wizard not complete."
return 1
fi
_info "Login"
_username_encoded="$(printf "%s" "$RUCKUS_USER" | _url_encode)"
_password_encoded="$(printf "%s" "$RUCKUS_PASS" | _url_encode)"
_login_query="$(printf "%s" "username=${_username_encoded}&password=${_password_encoded}&ok=Log+In")"
_post "$_login_query" "$_login_url" >/dev/null
_login_code="$(_response_code)"
if [ "$_login_code" = "200" ]; then
_err "Login failed: incorrect credentials."
return 1
fi
_info "Collect Session Cookie"
_H1="Cookie: $(_response_cookie)"
export _H1
_info "Collect CSRF Token"
_H2="X-CSRF-Token: $(_response_header 'HTTP_X_CSRF_TOKEN')"
export _H2
if _isRSA "$_ckey" >/dev/null 2>&1; then
_debug "Using RSA certificate."
else
_info "Verifying ECC certificate support."
_ul_version="$(_get_unleashed_version)"
if [ -z "$_ul_version" ]; then
_err "Your controller doesn't support ECC certificates. Please deploy an RSA certificate."
return 1
fi
_ul_version_major="$(echo "$_ul_version" | cut -d . -f 1)"
_ul_version_minor="$(echo "$_ul_version" | cut -d . -f 2)"
if [ "$_ul_version_major" -lt "200" ]; then
_err "ZoneDirector doesn't support ECC certificates. Please deploy an RSA certificate."
return 1
elif [ "$_ul_version_minor" -lt "13" ]; then
_err "Unleashed $_ul_version_major.$_ul_version_minor doesn't support ECC certificates. Please deploy an RSA certificate or upgrade to Unleashed 200.13+."
return 1
fi
_debug "ECC certificates OK for Unleashed $_ul_version_major.$_ul_version_minor."
fi
_info "Uploading certificate"
_post_upload "uploadcert" "$_cfullchain"
_info "Uploading private key"
_post_upload "uploadprivatekey" "$_ckey"
_info "Replacing certificate"
_replace_cert_ajax='<ajax-request action="docmd" comp="system" updater="rid.0.5" xcmd="replace-cert" checkAbility="6" timeout="-1"><xcmd cmd="replace-cert" cn="'$RUCKUS_HOST'"/></ajax-request>'
_post "$_replace_cert_ajax" "$_base_url/_cmdstat.jsp" >/dev/null
_info "Rebooting"
_cert_reboot_ajax='<ajax-request action="docmd" comp="worker" updater="rid.0.5" xcmd="cert-reboot" checkAbility="6"><xcmd cmd="cert-reboot" action="undefined"/></ajax-request>'
_post "$_cert_reboot_ajax" "$_base_url/_cmdstat.jsp" >/dev/null
return 0
}
_response_code() {
_egrep_o <"$HTTP_HEADER" "^HTTP[^ ]* .*$" | cut -d " " -f 2-100 | tr -d "\f\n" | _egrep_o "^[0-9]*"
}
_response_header() {
grep <"$HTTP_HEADER" -i "^$1:" | cut -d ':' -f 2- | tr -d "\r\n\t "
}
_response_cookie() {
_response_header 'Set-Cookie' | sed 's/;.*//'
}
_get_unleashed_version() {
_post '<ajax-request action="getstat" comp="system"><sysinfo/></ajax-request>' "$_base_url/_cmdstat.jsp" | _egrep_o "version-num=\"[^\"]*\"" | cut -d '"' -f 2
}
_post_upload() {
_post_action="$1"
_post_file="$2"
_post_boundary="----FormBoundary$(date "+%s%N")"
_post_data="$({
printf -- "--%s\r\n" "$_post_boundary"
printf -- "Content-Disposition: form-data; name=\"u\"; filename=\"%s\"\r\n" "$_post_action"
printf -- "Content-Type: application/octet-stream\r\n\r\n"
printf -- "%s\r\n" "$(cat "$_post_file")"
printf -- "--%s\r\n" "$_post_boundary"
printf -- "Content-Disposition: form-data; name=\"action\"\r\n\r\n"
printf -- "%s\r\n" "$_post_action"
printf -- "--%s\r\n" "$_post_boundary"
printf -- "Content-Disposition: form-data; name=\"callback\"\r\n\r\n"
printf -- "%s\r\n" "uploader_$_post_action"
printf -- "--%s--\r\n\r\n" "$_post_boundary"
})"
_post "$_post_data" "$_base_url/_upload.jsp?request_type=xhr" "" "" "multipart/form-data; boundary=$_post_boundary" >/dev/null
}

View File

@ -186,8 +186,8 @@ synology_dsm_deploy() {
if [ -n "$SYNO_USE_TEMP_ADMIN" ]; then
_getdeployconf SYNO_LOCAL_HOSTNAME
_debug SYNO_LOCAL_HOSTNAME "${SYNO_LOCAL_HOSTNAME:-}"
if [ "$SYNO_LOCAL_HOSTNAME" != "1" ] && [ "$SYNO_LOCAL_HOSTNAME" == "$SYNO_HOSTNAME" ]; then
if [ "$SYNO_HOSTNAME" != "localhost" ] && [ "$SYNO_HOSTNAME" != "127.0.0.1" ]; then
if [ "$SYNO_HOSTNAME" != "localhost" ] && [ "$SYNO_HOSTNAME" != "127.0.0.1" ]; then
if [ "$SYNO_LOCAL_HOSTNAME" != "1" ]; then
_err "SYNO_USE_TEMP_ADMIN=1 only support local deployment, though if you are sure that the hostname $SYNO_HOSTNAME is targeting to your **current local machine**, execute 'export SYNO_LOCAL_HOSTNAME=1' then rerun."
return 1
fi
@ -320,7 +320,7 @@ synology_dsm_deploy() {
_cleardeployconf SYNO_DEVICE_ID
_cleardeployconf SYNO_DEVICE_NAME
_savedeployconf SYNO_USE_TEMP_ADMIN "$SYNO_USE_TEMP_ADMIN"
_savedeployconf SYNO_LOCAL_HOSTNAME "$SYNO_HOSTNAME"
_savedeployconf SYNO_LOCAL_HOSTNAME "$SYNO_LOCAL_HOSTNAME"
else
_savedeployconf SYNO_USERNAME "$SYNO_USERNAME"
_savedeployconf SYNO_PASSWORD "$SYNO_PASSWORD"
@ -411,7 +411,7 @@ _temp_admin_create() {
_username="$1"
_password="$2"
synouser --del "$_username" >/dev/null 2>/dev/null
synouser --add "$_username" "$_password" "" 0 "scruelt@hotmail.com" 0 >/dev/null
synouser --add "$_username" "$_password" "" 0 "" 0 >/dev/null
}
_temp_admin_cleanup() {

View File

@ -217,7 +217,7 @@ truenas_deploy() {
_app_id=$(echo "$_app_id_list" | sed -n "${i}p")
_app_config="$(_post "\"$_app_id\"" "$_api_url/app/config" "" "POST" "application/json")"
# Check if the app use the same certificate TrueNAS web UI
_app_active_cert_config=$(echo "$_app_config" | _json_decode | jq -r ".ix_certificates[\"$_active_cert_id\"]")
_app_active_cert_config=$(echo "$_app_config" | tr -d '\000-\037' | _json_decode | jq -r ".ix_certificates[\"$_active_cert_id\"]")
if [ "$_app_active_cert_config" != "null" ]; then
_info "Updating certificate from $_active_cert_id to $_cert_id for app: $_app_id"
#Replace the old certificate id with the new one in path

325
deploy/truenas_ws.sh Normal file
View File

@ -0,0 +1,325 @@
#!/usr/bin/env sh
# TrueNAS deploy script for SCALE/CORE using websocket
# It is recommend to use a wildcard certificate
#
# Websocket Documentation: https://www.truenas.com/docs/api/scale_websocket_api.html
#
# Tested with TrueNAS Scale - Electric Eel 24.10
# Changes certificate in the following services:
# - Web UI
# - FTP
# - iX Apps
#
# The following environment variables must be set:
# ------------------------------------------------
#
# # API KEY
# # Use the folowing URL to create a new API token: <TRUENAS_HOSTNAME OR IP>/ui/apikeys
# export DEPLOY_TRUENAS_APIKEY="<API_KEY_GENERATED_IN_THE_WEB_UI"
#
### Private functions
# Call websocket method
# Usage:
# _ws_response=$(_ws_call "math.dummycalc" "'{"x": 4, "y": 5}'")
# _info "$_ws_response"
#
# Output:
# {"z": 9}
#
# Arguments:
# $@ - midclt arguments for call
#
# Returns:
# JSON/JOBID
_ws_call() {
_debug "_ws_call arg1" "$1"
_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")
fi
if [ $# -eq 2 ]; then
_ws_response=$(midclt -K "$DEPLOY_TRUENAS_APIKEY" call "$1" "$2")
fi
if [ $# -eq 1 ]; then
_ws_response=$(midclt -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() 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:
#
# Output:
# n/a
#
# Arguments:
# $1 - Anything
#
# Returns:
# 0: true
# 1: false
_ws_check_jobid() {
case "$1" in
[0-9]*)
return 0
;;
esac
return 1
}
# Wait for job to finish and return result as JSON
# Usage:
# _ws_result=$(_ws_get_job_result "$_ws_jobid")
# _new_certid=$(printf "%s" "$_ws_result" | jq -r '."id"')
#
# Output:
# JSON result of the job
#
# Arguments:
# $1 - JobID
#
# Returns:
# n/a
_ws_get_job_result() {
while true; do
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"')"
_debug "_ws_result" "$_ws_result"
printf "%s" "$_ws_result"
_ws_error="$(printf "%s" "$_ws_response" | jq '.[]."error"')"
if [ "$_ws_error" != "null" ]; then
_err "Job $1 failed:"
_err "$_ws_error"
return 7
fi
break
fi
done
return 0
}
########################
### Public functions ###
########################
# truenas_ws_deploy
#
# Deploy new certificate to TrueNAS services
#
# Arguments
# 1: Domain
# 2: Key-File
# 3: Certificate-File
# 4: CA-File
# 5: FullChain-File
# Returns:
# 0: Success
# 1: Missing API Key
# 2: TrueNAS not ready
# 3: Not a JobID
# 4: FTP cert error
# 5: WebUI cert error
# 6: Job error
# 7: WS call error
#
truenas_ws_deploy() {
_domain="$1"
_file_key="$2"
_file_cert="$3"
_file_ca="$4"
_file_fullchain="$5"
_debug _domain "$_domain"
_debug _file_key "$_file_key"
_debug _file_cert "$_file_cert"
_debug _file_ca "$_file_ca"
_debug _file_fullchain "$_file_fullchain"
########## Environment check
_info "Checking environment variables..."
_getdeployconf DEPLOY_TRUENAS_APIKEY
# 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
_secure_debug2 DEPLOY_TRUENAS_APIKEY "$DEPLOY_TRUENAS_APIKEY"
_info "Environment variables: OK"
########## Health check
_info "Checking TrueNAS health..."
_ws_response=$(_ws_call "system.ready" | tr '[:lower:]' '[:upper:]')
_ws_ret=$?
if [ $_ws_ret -gt 0 ]; then
_err "Error calling system.ready:"
_err "$_ws_response"
return $_ws_ret
fi
if [ "$_ws_response" != "TRUE" ]; then
_err "TrueNAS is not ready."
_err "Please check environment variables DEPLOY_TRUENAS_APIKEY, DEPLOY_TRUENAS_HOSTNAME and DEPLOY_TRUENAS_PROTOCOL."
_err "Verify API key."
return 2
fi
_savedeployconf DEPLOY_TRUENAS_APIKEY "$DEPLOY_TRUENAS_APIKEY"
_info "TrueNAS health: OK"
########## System info
_info "Gather system info..."
_ws_response=$(_ws_call "system.info")
_truenas_version=$(printf "%s" "$_ws_response" | jq -r '."version"')
_info "TrueNAS version: $_truenas_version"
########## Gather current certificate
_info "Gather current WebUI certificate..."
_ws_response="$(_ws_call "system.general.config")"
_ui_certificate_id=$(printf "%s" "$_ws_response" | jq -r '."ui_certificate"."id"')
_ui_certificate_name=$(printf "%s" "$_ws_response" | jq -r '."ui_certificate"."name"')
_info "Current WebUI certificate ID: $_ui_certificate_id"
_info "Current WebUI certificate name: $_ui_certificate_name"
########## Upload new certificate
_info "Upload new certificate..."
_certname="acme_$(_utc_date | tr -d '\-\:' | tr ' ' '_')"
_info "New WebUI certificate name: $_certname"
_debug _certname "$_certname"
_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
_info "Replace FTP certificate..."
_ws_response=$(_ws_call "ftp.update" "{\"ssltls_certificate\": $_new_certid}")
_ftp_certid=$(printf "%s" "$_ws_response" | jq -r '."ssltls_certificate"')
if [ "$_ftp_certid" != "$_new_certid" ]; then
_err "Cannot set FTP certificate."
_debug "_ws_response" "$_ws_response"
return 4
fi
########## ix Apps (SCALE only)
_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..."
fi
done
########## WebUI
_info "Replace WebUI certificate..."
_ws_response=$(_ws_call "system.general.update" "{\"ui_certificate\": $_new_certid}")
_changed_certid=$(printf "%s" "$_ws_response" | jq -r '."ui_certificate"."id"')
if [ "$_changed_certid" != "$_new_certid" ]; then
_err "WebUI certificate change error.."
return 5
else
_info "WebUI certificate replaced."
fi
_info "Restarting WebUI..."
_ws_response=$(_ws_call "system.general.ui_restart")
_info "Waiting for UI restart..."
sleep 6
########## Certificates
_info "Deleting old certificate..."
_ws_jobid=$(_ws_call "certificate.delete" "$_ui_certificate_id")
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
_info "Have a nice day...bye!"
}

View File

@ -30,7 +30,9 @@
# Keystore password (built into Unifi Controller, not a user-set password):
#DEPLOY_UNIFI_KEYPASS="aircontrolenterprise"
# Command to restart Unifi Controller:
#DEPLOY_UNIFI_RELOAD="service unifi restart"
# DEPLOY_UNIFI_RELOAD="systemctl restart unifi"
# System Properties file location for controller
#DEPLOY_UNIFI_SYSTEM_PROPERTIES="/usr/lib/unifi/data/system.properties"
#
# Settings for Unifi Cloud Key Gen1 (nginx admin pages):
# Directory where cloudkey.crt and cloudkey.key live:
@ -43,7 +45,7 @@
# Directory where unifi-core.crt and unifi-core.key live:
#DEPLOY_UNIFI_CORE_CONFIG="/data/unifi-core/config/"
# Command to restart unifi-core:
#DEPLOY_UNIFI_RELOAD="systemctl restart unifi-core"
# DEPLOY_UNIFI_OS_RELOAD="systemctl restart unifi-core"
#
# At least one of DEPLOY_UNIFI_KEYSTORE, DEPLOY_UNIFI_CLOUDKEY_CERTDIR,
# or DEPLOY_UNIFI_CORE_CONFIG must exist to receive the deployed certs.
@ -69,12 +71,16 @@ unifi_deploy() {
_getdeployconf DEPLOY_UNIFI_CLOUDKEY_CERTDIR
_getdeployconf DEPLOY_UNIFI_CORE_CONFIG
_getdeployconf DEPLOY_UNIFI_RELOAD
_getdeployconf DEPLOY_UNIFI_SYSTEM_PROPERTIES
_getdeployconf DEPLOY_UNIFI_OS_RELOAD
_debug2 DEPLOY_UNIFI_KEYSTORE "$DEPLOY_UNIFI_KEYSTORE"
_debug2 DEPLOY_UNIFI_KEYPASS "$DEPLOY_UNIFI_KEYPASS"
_debug2 DEPLOY_UNIFI_CLOUDKEY_CERTDIR "$DEPLOY_UNIFI_CLOUDKEY_CERTDIR"
_debug2 DEPLOY_UNIFI_CORE_CONFIG "$DEPLOY_UNIFI_CORE_CONFIG"
_debug2 DEPLOY_UNIFI_RELOAD "$DEPLOY_UNIFI_RELOAD"
_debug2 DEPLOY_UNIFI_OS_RELOAD "$DEPLOY_UNIFI_OS_RELOAD"
_debug2 DEPLOY_UNIFI_SYSTEM_PROPERTIES "$DEPLOY_UNIFI_SYSTEM_PROPERTIES"
# Space-separated list of environments detected and installed:
_services_updated=""
@ -135,33 +141,53 @@ unifi_deploy() {
cp -f "$_import_pkcs12" "$_unifi_keystore"
fi
# correct file ownership according to the directory, the keystore is placed in
_unifi_keystore_dir=$(dirname "${_unifi_keystore}")
_unifi_keystore_dir_owner=$(find "${_unifi_keystore_dir}" -maxdepth 0 -printf '%u\n')
_unifi_keystore_owner=$(find "${_unifi_keystore}" -maxdepth 0 -printf '%u\n')
if ! [ "${_unifi_keystore_owner}" = "${_unifi_keystore_dir_owner}" ]; then
_debug "Changing keystore owner to ${_unifi_keystore_dir_owner}"
chown "$_unifi_keystore_dir_owner" "${_unifi_keystore}" >/dev/null 2>&1 # fail quietly if we're not running as root
fi
# Update unifi service for certificate cipher compatibility
_unifi_system_properties="${DEPLOY_UNIFI_SYSTEM_PROPERTIES:-/usr/lib/unifi/data/system.properties}"
if ${ACME_OPENSSL_BIN:-openssl} pkcs12 \
-in "$_import_pkcs12" \
-password pass:aircontrolenterprise \
-nokeys | ${ACME_OPENSSL_BIN:-openssl} x509 -text \
-noout | grep -i "signature" | grep -iq ecdsa >/dev/null 2>&1; then
cp -f /usr/lib/unifi/data/system.properties /usr/lib/unifi/data/system.properties_original
_info "Updating system configuration for cipher compatibility."
_info "Saved original system config to /usr/lib/unifi/data/system.properties_original"
sed -i '/unifi\.https\.ciphers/d' /usr/lib/unifi/data/system.properties
echo "unifi.https.ciphers=ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES128-GCM-SHA256" >>/usr/lib/unifi/data/system.properties
sed -i '/unifi\.https\.sslEnabledProtocols/d' /usr/lib/unifi/data/system.properties
echo "unifi.https.sslEnabledProtocols=TLSv1.3,TLSv1.2" >>/usr/lib/unifi/data/system.properties
_info "System configuration updated."
if [ -f "$(dirname "${DEPLOY_UNIFI_KEYSTORE}")/system.properties" ]; then
_unifi_system_properties="$(dirname "${DEPLOY_UNIFI_KEYSTORE}")/system.properties"
else
_unifi_system_properties="/usr/lib/unifi/data/system.properties"
fi
if [ -f "${_unifi_system_properties}" ]; then
cp -f "${_unifi_system_properties}" "${_unifi_system_properties}"_original
_info "Updating system configuration for cipher compatibility."
_info "Saved original system config to ${_unifi_system_properties}_original"
sed -i '/unifi\.https\.ciphers/d' "${_unifi_system_properties}"
echo "unifi.https.ciphers=ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES128-GCM-SHA256" >>"${_unifi_system_properties}"
sed -i '/unifi\.https\.sslEnabledProtocols/d' "${_unifi_system_properties}"
echo "unifi.https.sslEnabledProtocols=TLSv1.3,TLSv1.2" >>"${_unifi_system_properties}"
_info "System configuration updated."
fi
fi
rm "$_import_pkcs12"
# Restarting unifi-core will bring up unifi, doing it out of order results in
# a certificate error, and breaks wifiman.
# Restart if we aren't doing unifi-core, otherwise stop for later restart.
if systemctl -q is-active unifi; then
if [ ! -f "${DEPLOY_UNIFI_CORE_CONFIG:-/data/unifi-core/config}/unifi-core.key" ]; then
_reload_cmd="${_reload_cmd:+$_reload_cmd && }systemctl restart unifi"
else
_reload_cmd="${_reload_cmd:+$_reload_cmd && }systemctl stop unifi"
fi
# Restart if we aren't doing Unifi OS (e.g. unifi-core service), otherwise stop for later restart.
_unifi_reload="${DEPLOY_UNIFI_RELOAD:-systemctl restart unifi}"
if [ ! -f "${DEPLOY_UNIFI_CORE_CONFIG:-/data/unifi-core/config}/unifi-core.key" ]; then
_reload_cmd="${_reload_cmd:+$_reload_cmd && }$_unifi_reload"
else
_info "Stopping Unifi Controller for later restart."
_unifi_stop=$(echo "${_unifi_reload}" | sed -e 's/restart/stop/')
$_unifi_stop
_reload_cmd="${_reload_cmd:+$_reload_cmd && }$_unifi_reload"
_info "Unifi Controller stopped."
fi
_services_updated="${_services_updated} unifi"
_info "Install Unifi Controller certificate success!"
@ -181,13 +207,24 @@ unifi_deploy() {
return 1
fi
# Cloud Key expects to load the keystore from /etc/ssl/private/unifi.keystore.jks.
# Normally /usr/lib/unifi/data/keystore is a symlink there (so the keystore was
# updated above), but if not, we don't know how to handle this installation:
if ! cmp -s "$_unifi_keystore" "${_cloudkey_certdir}/unifi.keystore.jks"; then
_err "Unsupported Cloud Key configuration: keystore not found at '${_cloudkey_certdir}/unifi.keystore.jks'"
return 1
# It appears that unifi won't start if this is a symlink, so we'll copy it instead.
# if ! cmp -s "$_unifi_keystore" "${_cloudkey_certdir}/unifi.keystore.jks"; then
# _err "Unsupported Cloud Key configuration: keystore not found at '${_cloudkey_certdir}/unifi.keystore.jks'"
# return 1
# fi
_info "Updating ${_cloudkey_certdir}/unifi.keystore.jks"
if [ -e "${_cloudkey_certdir}/unifi.keystore.jks" ]; then
if [ -L "${_cloudkey_certdir}/unifi.keystore.jks" ]; then
rm -f "${_cloudkey_certdir}/unifi.keystore.jks"
else
mv "${_cloudkey_certdir}/unifi.keystore.jks" "${_cloudkey_certdir}/unifi.keystore.jks_original"
fi
fi
cp "${_unifi_keystore}" "${_cloudkey_certdir}/unifi.keystore.jks"
cat "$_cfullchain" >"${_cloudkey_certdir}/cloudkey.crt"
cat "$_ckey" >"${_cloudkey_certdir}/cloudkey.key"
(cd "$_cloudkey_certdir" && tar -cf cert.tar cloudkey.crt cloudkey.key unifi.keystore.jks)
@ -215,14 +252,14 @@ unifi_deploy() {
# Save the existing certs in case something goes wrong.
cp -f "${_unifi_core_config}"/unifi-core.crt "${_unifi_core_config}"/unifi-core_original.crt
cp -f "${_unifi_core_config}"/unifi-core.key "${_unifi_core_config}"/unifi-core_original.key
_info "Previous certificate and key saved to ${_unifi_core_config}/unifi-core_original.crt/key."
_info "Previous certificate and key saved to ${_unifi_core_config}/unifi-core_original.crt.key."
cat "$_cfullchain" >"${_unifi_core_config}/unifi-core.crt"
cat "$_ckey" >"${_unifi_core_config}/unifi-core.key"
if systemctl -q is-active unifi-core; then
_reload_cmd="${_reload_cmd:+$_reload_cmd && }systemctl restart unifi-core"
fi
_unifi_os_reload="${DEPLOY_UNIFI_OS_RELOAD:-systemctl restart unifi-core}"
_reload_cmd="${_reload_cmd:+$_reload_cmd && }$_unifi_os_reload"
_info "Install UnifiOS certificate success!"
_services_updated="${_services_updated} unifi-core"
elif [ "$DEPLOY_UNIFI_CORE_CONFIG" ]; then
@ -261,6 +298,8 @@ unifi_deploy() {
_savedeployconf DEPLOY_UNIFI_CLOUDKEY_CERTDIR "$DEPLOY_UNIFI_CLOUDKEY_CERTDIR"
_savedeployconf DEPLOY_UNIFI_CORE_CONFIG "$DEPLOY_UNIFI_CORE_CONFIG"
_savedeployconf DEPLOY_UNIFI_RELOAD "$DEPLOY_UNIFI_RELOAD"
_savedeployconf DEPLOY_UNIFI_OS_RELOAD "$DEPLOY_UNIFI_OS_RELOAD"
_savedeployconf DEPLOY_UNIFI_SYSTEM_PROPERTIES "$DEPLOY_UNIFI_SYSTEM_PROPERTIES"
return 0
}

View File

@ -80,10 +80,15 @@ vault_deploy() {
if [ -n "$VAULT_RENEW_TOKEN" ]; then
URL="$VAULT_ADDR/v1/auth/token/renew-self"
_info "Renew the Vault token to default TTL"
if ! _post "" "$URL" >/dev/null; then
_response=$(_post "" "$URL")
if [ "$?" != "0" ]; then
_err "Failed to renew the Vault token"
return 1
fi
if echo "$_response" | grep -q '"errors":\['; then
_err "Failed to renew the Vault token: $_response"
return 1
fi
fi
URL="$VAULT_ADDR/v1/$VAULT_PREFIX/$_cdomain"
@ -91,29 +96,85 @@ vault_deploy() {
if [ -n "$VAULT_FABIO_MODE" ]; then
_info "Writing certificate and key to $URL in Fabio mode"
if [ -n "$VAULT_KV_V2" ]; then
_post "{ \"data\": {\"cert\": \"$_cfullchain\", \"key\": \"$_ckey\"} }" "$URL" >/dev/null || return 1
_response=$(_post "{ \"data\": {\"cert\": \"$_cfullchain\", \"key\": \"$_ckey\"} }" "$URL")
if [ "$?" != "0" ]; then return 1; fi
if echo "$_response" | grep -q '"errors":\['; then
_err "Vault error: $_response"
return 1
fi
else
_post "{\"cert\": \"$_cfullchain\", \"key\": \"$_ckey\"}" "$URL" >/dev/null || return 1
_response=$(_post "{\"cert\": \"$_cfullchain\", \"key\": \"$_ckey\"}" "$URL")
if [ "$?" != "0" ]; then return 1; fi
if echo "$_response" | grep -q '"errors":\['; then
_err "Vault error: $_response"
return 1
fi
fi
else
if [ -n "$VAULT_KV_V2" ]; then
_info "Writing certificate to $URL/cert.pem"
_post "{\"data\": {\"value\": \"$_ccert\"}}" "$URL/cert.pem" >/dev/null || return 1
_response=$(_post "{\"data\": {\"value\": \"$_ccert\"}}" "$URL/cert.pem")
if [ "$?" != "0" ]; then return 1; fi
if echo "$_response" | grep -q '"errors":\['; then
_err "Vault error writing cert.pem: $_response"
return 1
fi
_info "Writing key to $URL/cert.key"
_post "{\"data\": {\"value\": \"$_ckey\"}}" "$URL/cert.key" >/dev/null || return 1
_response=$(_post "{\"data\": {\"value\": \"$_ckey\"}}" "$URL/cert.key")
if [ "$?" != "0" ]; then return 1; fi
if echo "$_response" | grep -q '"errors":\['; then
_err "Vault error writing cert.key: $_response"
return 1
fi
_info "Writing CA certificate to $URL/ca.pem"
_post "{\"data\": {\"value\": \"$_cca\"}}" "$URL/ca.pem" >/dev/null || return 1
_response=$(_post "{\"data\": {\"value\": \"$_cca\"}}" "$URL/ca.pem")
if [ "$?" != "0" ]; then return 1; fi
if echo "$_response" | grep -q '"errors":\['; then
_err "Vault error writing ca.pem: $_response"
return 1
fi
_info "Writing full-chain certificate to $URL/fullchain.pem"
_post "{\"data\": {\"value\": \"$_cfullchain\"}}" "$URL/fullchain.pem" >/dev/null || return 1
_response=$(_post "{\"data\": {\"value\": \"$_cfullchain\"}}" "$URL/fullchain.pem")
if [ "$?" != "0" ]; then return 1; fi
if echo "$_response" | grep -q '"errors":\['; then
_err "Vault error writing fullchain.pem: $_response"
return 1
fi
else
_info "Writing certificate to $URL/cert.pem"
_post "{\"value\": \"$_ccert\"}" "$URL/cert.pem" >/dev/null || return 1
_response=$(_post "{\"value\": \"$_ccert\"}" "$URL/cert.pem")
if [ "$?" != "0" ]; then return 1; fi
if echo "$_response" | grep -q '"errors":\['; then
_err "Vault error writing cert.pem: $_response"
return 1
fi
_info "Writing key to $URL/cert.key"
_post "{\"value\": \"$_ckey\"}" "$URL/cert.key" >/dev/null || return 1
_response=$(_post "{\"value\": \"$_ckey\"}" "$URL/cert.key")
if [ "$?" != "0" ]; then return 1; fi
if echo "$_response" | grep -q '"errors":\['; then
_err "Vault error writing cert.key: $_response"
return 1
fi
_info "Writing CA certificate to $URL/ca.pem"
_post "{\"value\": \"$_cca\"}" "$URL/ca.pem" >/dev/null || return 1
_response=$(_post "{\"value\": \"$_cca\"}" "$URL/ca.pem")
if [ "$?" != "0" ]; then return 1; fi
if echo "$_response" | grep -q '"errors":\['; then
_err "Vault error writing ca.pem: $_response"
return 1
fi
_info "Writing full-chain certificate to $URL/fullchain.pem"
_post "{\"value\": \"$_cfullchain\"}" "$URL/fullchain.pem" >/dev/null || return 1
_response=$(_post "{\"value\": \"$_cfullchain\"}" "$URL/fullchain.pem")
if [ "$?" != "0" ]; then return 1; fi
if echo "$_response" | grep -q '"errors":\['; then
_err "Vault error writing fullchain.pem: $_response"
return 1
fi
fi
# To make it compatible with the wrong ca path `chain.pem` which was used in former versions
@ -121,11 +182,20 @@ vault_deploy() {
_err "The CA certificate has moved from chain.pem to ca.pem, if you don't depend on chain.pem anymore, you can delete it to avoid this warning"
_info "Updating CA certificate to $URL/chain.pem for backward compatibility"
if [ -n "$VAULT_KV_V2" ]; then
_post "{\"data\": {\"value\": \"$_cca\"}}" "$URL/chain.pem" >/dev/null || return 1
_response=$(_post "{\"data\": {\"value\": \"$_cca\"}}" "$URL/chain.pem")
if [ "$?" != "0" ]; then return 1; fi
if echo "$_response" | grep -q '"errors":\['; then
_err "Vault error writing chain.pem: $_response"
return 1
fi
else
_post "{\"value\": \"$_cca\"}" "$URL/chain.pem" >/dev/null || return 1
_response=$(_post "{\"value\": \"$_cca\"}" "$URL/chain.pem")
if [ "$?" != "0" ]; then return 1; fi
if echo "$_response" | grep -q '"errors":\['; then
_err "Vault error writing chain.pem: $_response"
return 1
fi
fi
fi
fi
}

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

@ -9,7 +9,7 @@ Options:
AZUREDNS_APPID App ID. App ID of the service principal
AZUREDNS_CLIENTSECRET Client Secret. Secret from creating the service principal
AZUREDNS_MANAGEDIDENTITY Use Managed Identity. Use Managed Identity assigned to a resource instead of a service principal. "true"/"false"
AZUREDNS_BEARERTOKEN Optional Bearer Token. Used instead of service principal credentials or managed identity
AZUREDNS_BEARERTOKEN Bearer Token. Used instead of service principal credentials or managed identity. Optional.
'
wiki=https://github.com/acmesh-official/acme.sh/wiki/How-to-use-Azure-DNS
@ -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 \")

281
dnsapi/dns_beget.sh Executable file
View File

@ -0,0 +1,281 @@
#!/usr/bin/env sh
# shellcheck disable=SC2034
dns_beget_info='Beget.com
Site: Beget.com
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_beget
Options:
BEGET_User API user
BEGET_Password API password
Issues: github.com/acmesh-official/acme.sh/issues/6200
Author: ARNik arnik@arnik.ru
'
Beget_Api="https://api.beget.com/api"
#################### Public functions ####################
# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
# Used to add txt record
dns_beget_add() {
fulldomain=$1
txtvalue=$2
_debug "dns_beget_add() $fulldomain $txtvalue"
fulldomain=$(echo "$fulldomain" | _lower_case)
Beget_Username="${Beget_Username:-$(_readaccountconf_mutable Beget_Username)}"
Beget_Password="${Beget_Password:-$(_readaccountconf_mutable Beget_Password)}"
if [ -z "$Beget_Username" ] || [ -z "$Beget_Password" ]; then
Beget_Username=""
Beget_Password=""
_err "You must export variables: Beget_Username, and Beget_Password"
return 1
fi
#save the credentials to the account conf file.
_saveaccountconf_mutable Beget_Username "$Beget_Username"
_saveaccountconf_mutable Beget_Password "$Beget_Password"
_info "Prepare subdomain."
if ! _prepare_subdomain "$fulldomain"; then
_err "Can't prepare subdomain."
return 1
fi
_info "Get domain records"
data="{\"fqdn\":\"$fulldomain\"}"
res=$(_api_call "$Beget_Api/dns/getData" "$data")
if ! _is_api_reply_ok "$res"; then
_err "Can't get domain records."
return 1
fi
_info "Add new TXT record"
data="{\"fqdn\":\"$fulldomain\",\"records\":{"
data=${data}$(_parce_records "$res" "A")
data=${data}$(_parce_records "$res" "AAAA")
data=${data}$(_parce_records "$res" "CAA")
data=${data}$(_parce_records "$res" "MX")
data=${data}$(_parce_records "$res" "SRV")
data=${data}$(_parce_records "$res" "TXT")
data=$(echo "$data" | sed 's/,$//')
data=${data}'}}'
str=$(_txt_to_dns_json "$txtvalue")
data=$(_add_record "$data" "TXT" "$str")
res=$(_api_call "$Beget_Api/dns/changeRecords" "$data")
if ! _is_api_reply_ok "$res"; then
_err "Can't change domain records."
return 1
fi
return 0
}
# Usage: fulldomain txtvalue
# Used to remove the txt record after validation
dns_beget_rm() {
fulldomain=$1
txtvalue=$2
_debug "dns_beget_rm() $fulldomain $txtvalue"
fulldomain=$(echo "$fulldomain" | _lower_case)
Beget_Username="${Beget_Username:-$(_readaccountconf_mutable Beget_Username)}"
Beget_Password="${Beget_Password:-$(_readaccountconf_mutable Beget_Password)}"
_info "Get current domain records"
data="{\"fqdn\":\"$fulldomain\"}"
res=$(_api_call "$Beget_Api/dns/getData" "$data")
if ! _is_api_reply_ok "$res"; then
_err "Can't get domain records."
return 1
fi
_info "Remove TXT record"
data="{\"fqdn\":\"$fulldomain\",\"records\":{"
data=${data}$(_parce_records "$res" "A")
data=${data}$(_parce_records "$res" "AAAA")
data=${data}$(_parce_records "$res" "CAA")
data=${data}$(_parce_records "$res" "MX")
data=${data}$(_parce_records "$res" "SRV")
data=${data}$(_parce_records "$res" "TXT")
data=$(echo "$data" | sed 's/,$//')
data=${data}'}}'
str=$(_txt_to_dns_json "$txtvalue")
data=$(_rm_record "$data" "$str")
res=$(_api_call "$Beget_Api/dns/changeRecords" "$data")
if ! _is_api_reply_ok "$res"; then
_err "Can't change domain records."
return 1
fi
return 0
}
#################### Private functions below ####################
# Create subdomain if needed
# Usage: _prepare_subdomain [fulldomain]
_prepare_subdomain() {
fulldomain=$1
_info "Detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug _domain_id "$_domain_id"
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
if [ -z "$_sub_domain" ]; then
_debug "$fulldomain is a root domain."
return 0
fi
_info "Get subdomain list"
res=$(_api_call "$Beget_Api/domain/getSubdomainList")
if ! _is_api_reply_ok "$res"; then
_err "Can't get subdomain list."
return 1
fi
if _contains "$res" "\"fqdn\":\"$fulldomain\""; then
_debug "Subdomain $fulldomain already exist."
return 0
fi
_info "Subdomain $fulldomain does not exist. Let's create one."
data="{\"subdomain\":\"$_sub_domain\",\"domain_id\":$_domain_id}"
res=$(_api_call "$Beget_Api/domain/addSubdomainVirtual" "$data")
if ! _is_api_reply_ok "$res"; then
_err "Can't create subdomain."
return 1
fi
_debug "Cleanup subdomen records"
data="{\"fqdn\":\"$fulldomain\",\"records\":{}}"
res=$(_api_call "$Beget_Api/dns/changeRecords" "$data")
if ! _is_api_reply_ok "$res"; then
_debug "Can't cleanup $fulldomain records."
fi
data="{\"fqdn\":\"www.$fulldomain\",\"records\":{}}"
res=$(_api_call "$Beget_Api/dns/changeRecords" "$data")
if ! _is_api_reply_ok "$res"; then
_debug "Can't cleanup www.$fulldomain records."
fi
return 0
}
# Usage: _get_root _acme-challenge.www.domain.com
#returns
# _sub_domain=_acme-challenge.www
# _domain=domain.com
# _domain_id=32436365
_get_root() {
fulldomain=$1
i=1
p=1
_debug "Get domain list"
res=$(_api_call "$Beget_Api/domain/getList")
if ! _is_api_reply_ok "$res"; then
_err "Can't get domain list."
return 1
fi
while true; do
h=$(printf "%s" "$fulldomain" | cut -d . -f "$i"-100)
_debug h "$h"
if [ -z "$h" ]; then
return 1
fi
if _contains "$res" "$h"; then
_domain_id=$(echo "$res" | _egrep_o "\"id\":[0-9]*,\"fqdn\":\"$h\"" | cut -d , -f1 | cut -d : -f2)
if [ "$_domain_id" ]; then
if [ "$h" != "$fulldomain" ]; then
_sub_domain=$(echo "$fulldomain" | cut -d . -f 1-"$p")
else
_sub_domain=""
fi
_domain=$h
return 0
fi
return 1
fi
p="$i"
i=$(_math "$i" + 1)
done
return 1
}
# Parce DNS records from json string
# Usage: _parce_records [j_str] [record_name]
_parce_records() {
j_str=$1
record_name=$2
res="\"$record_name\":["
res=${res}$(echo "$j_str" | _egrep_o "\"$record_name\":\[.*" | cut -d '[' -f2 | cut -d ']' -f1)
res=${res}"],"
echo "$res"
}
# Usage: _add_record [data] [record_name] [record_data]
_add_record() {
data=$1
record_name=$2
record_data=$3
echo "$data" | sed "s/\"$record_name\":\[/\"$record_name\":\[$record_data,/" | sed "s/,\]/\]/"
}
# Usage: _rm_record [data] [record_data]
_rm_record() {
data=$1
record_data=$2
echo "$data" | sed "s/$record_data//g" | sed "s/,\+/,/g" |
sed "s/{,/{/g" | sed "s/,}/}/g" |
sed "s/\[,/\[/g" | sed "s/,\]/\]/g"
}
_txt_to_dns_json() {
echo "{\"ttl\":600,\"txtdata\":\"$1\"}"
}
# Usage: _api_call [api_url] [input_data]
_api_call() {
api_url="$1"
input_data="$2"
_debug "_api_call $api_url"
_debug "Request: $input_data"
# res=$(curl -s -L -D ./http.header \
# "$api_url" \
# --data-urlencode login=$Beget_Username \
# --data-urlencode passwd=$Beget_Password \
# --data-urlencode input_format=json \
# --data-urlencode output_format=json \
# --data-urlencode "input_data=$input_data")
url="$api_url?login=$Beget_Username&passwd=$Beget_Password&input_format=json&output_format=json"
if [ -n "$input_data" ]; then
url=${url}"&input_data="
url=${url}$(echo "$input_data" | _url_encode)
fi
res=$(_get "$url")
_debug "Reply: $res"
echo "$res"
}
# Usage: _is_api_reply_ok [api_reply]
_is_api_reply_ok() {
_contains "$1" '^{"status":"success","answer":{"status":"success","result":.*}}$'
}

View File

@ -215,10 +215,8 @@ _cyon_change_domain_env() {
if ! _cyon_check_if_2fa_missed "${domain_env_response}"; then return 1; fi
domain_env_success="$(printf "%s" "${domain_env_response}" | _egrep_o '"authenticated":\w*' | cut -d : -f 2)"
# Bail if domain environment change fails.
if [ "${domain_env_success}" != "true" ]; then
if [ "$(printf "%s" "${domain_env_response}" | _cyon_get_environment_change_status)" != "true" ]; then
_err " $(printf "%s" "${domain_env_response}" | _cyon_get_response_message)"
_err ""
return 1
@ -232,7 +230,7 @@ _cyon_add_txt() {
_info " - Adding DNS TXT entry..."
add_txt_url="https://my.cyon.ch/domain/dnseditor/add-record-async"
add_txt_data="zone=${fulldomain_idn}.&ttl=900&type=TXT&value=${txtvalue}"
add_txt_data="name=${fulldomain_idn}.&ttl=900&type=TXT&dnscontent=${txtvalue}"
add_txt_response="$(_post "$add_txt_data" "$add_txt_url")"
_debug add_txt_response "${add_txt_response}"
@ -241,9 +239,10 @@ _cyon_add_txt() {
add_txt_message="$(printf "%s" "${add_txt_response}" | _cyon_get_response_message)"
add_txt_status="$(printf "%s" "${add_txt_response}" | _cyon_get_response_status)"
add_txt_validation="$(printf "%s" "${add_txt_response}" | _cyon_get_validation_status)"
# Bail if adding TXT entry fails.
if [ "${add_txt_status}" != "true" ]; then
if [ "${add_txt_status}" != "true" ] || [ "${add_txt_validation}" != "true" ]; then
_err " ${add_txt_message}"
_err ""
return 1
@ -305,13 +304,21 @@ _cyon_get_response_message() {
}
_cyon_get_response_status() {
_egrep_o '"status":\w*' | cut -d : -f 2
_egrep_o '"status":[a-zA-z0-9]*' | cut -d : -f 2
}
_cyon_get_validation_status() {
_egrep_o '"valid":[a-zA-z0-9]*' | cut -d : -f 2
}
_cyon_get_response_success() {
_egrep_o '"onSuccess":"[^"]*"' | cut -d : -f 2 | tr -d '"'
}
_cyon_get_environment_change_status() {
_egrep_o '"authenticated":[a-zA-z0-9]*' | cut -d : -f 2
}
_cyon_check_if_2fa_missed() {
# Did we miss the 2FA?
if test "${1#*multi_factor_form}" != "${1}"; then

163
dnsapi/dns_edgecenter.sh Normal file
View File

@ -0,0 +1,163 @@
#!/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
Options:
EDGECENTER_API_KEY auth APIKey'
EDGECENTER_API="https://api.edgecenter.ru"
DOMAIN_TYPE=
DOMAIN_MASTER=
######## Public functions #####################
#Usage: dns_edgecenter_add _acme-challenge.www.domain.com "TXT_RECORD_VALUE"
dns_edgecenter_add() {
fulldomain="$1"
txtvalue="$2"
_info "Using EdgeCenter DNS API"
if ! _dns_edgecenter_init_check; then
return 1
fi
_debug "Detecting root zone for $fulldomain"
if ! _get_root "$fulldomain"; then
return 1
fi
subdomain="${fulldomain%."$_zone"}"
subdomain=${subdomain%.}
_debug "Zone: $_zone"
_debug "Subdomain: $subdomain"
_debug "TXT value: $txtvalue"
payload='{"resource_records": [ { "content": ["'"$txtvalue"'"] } ], "ttl": 60 }'
_dns_edgecenter_http_api_call "post" "dns/v2/zones/$_zone/$subdomain.$_zone/txt" "$payload"
if _contains "$response" '"error":"rrset is already exists"'; then
_debug "RRSet exists, merging values"
_dns_edgecenter_http_api_call "get" "dns/v2/zones/$_zone/$subdomain.$_zone/txt"
current="$response"
newlist=""
for v in $(echo "$current" | sed -n 's/.*"content":\["\([^"]*\)"\].*/\1/p'); do
newlist="$newlist {\"content\":[\"$v\"]},"
done
newlist="$newlist{\"content\":[\"$txtvalue\"]}"
putdata="{\"resource_records\":[${newlist}]}
"
_dns_edgecenter_http_api_call "put" "dns/v2/zones/$_zone/$subdomain.$_zone/txt" "$putdata"
_info "Updated existing RRSet with new TXT value."
return 0
fi
if _contains "$response" '"exception":'; then
_err "Record cannot be added."
return 1
fi
_info "TXT record added successfully."
return 0
}
#Usage: dns_edgecenter_rm _acme-challenge.www.domain.com "TXT_RECORD_VALUE"
dns_edgecenter_rm() {
fulldomain="$1"
txtvalue="$2"
_info "Removing TXT record for $fulldomain"
if ! _dns_edgecenter_init_check; then
return 1
fi
if ! _get_root "$fulldomain"; then
return 1
fi
subdomain="${fulldomain%."$_zone"}"
subdomain=${subdomain%.}
_dns_edgecenter_http_api_call "delete" "dns/v2/zones/$_zone/$subdomain.$_zone/txt"
if [ -z "$response" ]; then
_info "TXT record deleted successfully."
else
_info "TXT record may not have been deleted: $response"
fi
return 0
}
#################### Private functions below ##################################
_dns_edgecenter_init_check() {
EDGECENTER_API_KEY="${EDGECENTER_API_KEY:-$(_readaccountconf_mutable EDGECENTER_API_KEY)}"
if [ -z "$EDGECENTER_API_KEY" ]; then
_err "EDGECENTER_API_KEY was not exported."
return 1
fi
_saveaccountconf_mutable EDGECENTER_API_KEY "$EDGECENTER_API_KEY"
export _H1="Authorization: APIKey $EDGECENTER_API_KEY"
_dns_edgecenter_http_api_call "get" "dns/v2/clients/me/features"
if ! _contains "$response" '"id":'; then
_err "Invalid API key."
return 1
fi
return 0
}
_get_root() {
domain="$1"
i=1
while true; do
h=$(printf "%s" "$domain" | cut -d . -f "$i"-)
if [ -z "$h" ]; then
return 1
fi
_dns_edgecenter_http_api_call "get" "dns/v2/zones/$h"
if ! _contains "$response" 'zone is not found'; then
_zone="$h"
return 0
fi
i=$((i + 1))
done
return 1
}
_dns_edgecenter_http_api_call() {
mtd="$1"
endpoint="$2"
data="$3"
export _H1="Authorization: APIKey $EDGECENTER_API_KEY"
case "$mtd" in
get)
response="$(_get "$EDGECENTER_API/$endpoint")"
;;
post)
response="$(_post "$data" "$EDGECENTER_API/$endpoint")"
;;
delete)
response="$(_post "" "$EDGECENTER_API/$endpoint" "" "DELETE")"
;;
put)
response="$(_post "$data" "$EDGECENTER_API/$endpoint" "" "PUT")"
;;
*)
_err "Unknown HTTP method $mtd"
return 1
;;
esac
_debug "HTTP $mtd response: $response"
return 0
}

View File

@ -88,7 +88,7 @@ _get_root() {
i=1
while true; do
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
_debug h "$h"
if [ -z "$h" ]; then
#not valid

105
dnsapi/dns_freemyip.sh Normal file
View File

@ -0,0 +1,105 @@
#!/usr/bin/env sh
# shellcheck disable=SC2034
dns_freemyip_info='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}
Author: Recolic Keghart <root@recolic.net>, @Giova96
'
FREEMYIP_DNS_API="https://freemyip.com/update?"
################ Public functions ################
#Usage: dns_freemyip_add fulldomain txtvalue
dns_freemyip_add() {
fulldomain="$1"
txtvalue="$2"
_info "Add TXT record $txtvalue for $fulldomain using freemyip.com api"
FREEMYIP_Token="${FREEMYIP_Token:-$(_readaccountconf_mutable FREEMYIP_Token)}"
if [ -z "$FREEMYIP_Token" ]; then
FREEMYIP_Token=""
_err "You don't specify FREEMYIP_Token yet."
_err "Please specify your token and try again."
return 1
fi
#save the credentials to the account conf file.
_saveaccountconf_mutable FREEMYIP_Token "$FREEMYIP_Token"
if _is_root_domain_published "$fulldomain"; then
_err "freemyip API don't allow you to set multiple TXT record for the same subdomain!"
_err "You must apply certificate for only one domain at a time!"
_err "===="
_err "For example, aaa.yourdomain.freemyip.com and bbb.yourdomain.freemyip.com and yourdomain.freemyip.com ALWAYS share the same TXT record. They will overwrite each other if you apply multiple domain at the same time."
_debug "If you are testing this workflow in github pipeline or acmetest, please set TEST_DNS_NO_SUBDOMAIN=1 and TEST_DNS_NO_WILDCARD=1"
return 1
fi
# txtvalue must be url-encoded. But it's not necessary for acme txt value.
_freemyip_get_until_ok "${FREEMYIP_DNS_API}token=$FREEMYIP_Token&domain=$fulldomain&txt=$txtvalue" 2>&1
return $?
}
#Usage: dns_freemyip_rm fulldomain txtvalue
dns_freemyip_rm() {
fulldomain="$1"
txtvalue="$2"
_info "Delete TXT record $txtvalue for $fulldomain using freemyip.com api"
FREEMYIP_Token="${FREEMYIP_Token:-$(_readaccountconf_mutable FREEMYIP_Token)}"
if [ -z "$FREEMYIP_Token" ]; then
FREEMYIP_Token=""
_err "You don't specify FREEMYIP_Token yet."
_err "Please specify your token and try again."
return 1
fi
#save the credentials to the account conf file.
_saveaccountconf_mutable FREEMYIP_Token "$FREEMYIP_Token"
# Leave the TXT record as empty or "null" to delete the record.
_freemyip_get_until_ok "${FREEMYIP_DNS_API}token=$FREEMYIP_Token&domain=$fulldomain&txt=" 2>&1
return $?
}
################ Private functions below ################
_get_root() {
_fmi_d="$1"
echo "$_fmi_d" | rev | cut -d '.' -f 1-3 | rev
}
# There is random failure while calling freemyip API too fast. This function automatically retry until success.
_freemyip_get_until_ok() {
_fmi_url="$1"
for i in $(seq 1 8); do
_debug "HTTP GET freemyip.com API '$_fmi_url', retry $i/8..."
_get "$_fmi_url" | tee /dev/fd/2 | grep OK && return 0
_sleep 1 # DO NOT send the request too fast
done
_err "Failed to request freemyip API: $_fmi_url . Server does not say 'OK'"
return 1
}
# Verify in public dns if domain is already there.
_is_root_domain_published() {
_fmi_d="$1"
_webroot="$(_get_root "$_fmi_d")"
_info "Verifying '""$_fmi_d""' freemyip webroot (""$_webroot"") is not published yet"
for i in $(seq 1 3); do
_debug "'$_webroot' ns lookup, retry $i/3..."
if [ "$(_ns_lookup "$_fmi_d" TXT)" ]; then
_debug "'$_webroot' already has a TXT record published!"
return 0
fi
_sleep 10 # Give it some time to propagate the TXT record
done
return 1
}

44
dnsapi/dns_he_ddns.sh Normal file
View File

@ -0,0 +1,44 @@
#!/usr/bin/env sh
# shellcheck disable=SC2034
dns_he_ddns_info='Hurricane Electric HE.net DDNS
Site: dns.he.net
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_he_ddns
Options:
HE_DDNS_KEY The DDNS key
Author: Markku Leiniö
'
HE_DDNS_URL="https://dyn.dns.he.net/nic/update"
######## Public functions #####################
#Usage: dns_he_ddns_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_he_ddns_add() {
fulldomain=$1
txtvalue=$2
HE_DDNS_KEY="${HE_DDNS_KEY:-$(_readaccountconf_mutable HE_DDNS_KEY)}"
if [ -z "$HE_DDNS_KEY" ]; then
HE_DDNS_KEY=""
_err "You didn't specify a DDNS key for accessing the TXT record in HE API."
return 1
fi
#Save the DDNS key to the account conf file.
_saveaccountconf_mutable HE_DDNS_KEY "$HE_DDNS_KEY"
_info "Using Hurricane Electric DDNS API"
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
response="$(_post "hostname=$fulldomain&password=$HE_DDNS_KEY&txt=$txtvalue" "$HE_DDNS_URL")"
_info "Response: $response"
_contains "$response" "good" && return 0 || return 1
}
# dns_he_ddns_rm() is not doing anything because the API call always updates the
# contents of the existing record (that the API key gives access to).
dns_he_ddns_rm() {
fulldomain=$1
_debug "Delete TXT record called for '${fulldomain}', not doing anything."
return 0
}

2
dnsapi/dns_hetzner.sh Normal file → Executable file
View File

@ -212,7 +212,7 @@ _get_root() {
_response_has_error() {
unset _response_error
err_part="$(echo "$response" | _egrep_o '"error":{[^}]*}')"
err_part="$(echo "$response" | _egrep_o '"error":\{[^\}]*\}')"
if [ -n "$err_part" ]; then
err_code=$(echo "$err_part" | _egrep_o '"code":[0-9]+' | cut -d : -f 2)

View File

@ -1,13 +1,13 @@
#!/usr/bin/env sh
# Created by Laraveluser
#
# Pass credentials before "acme.sh --issue --dns dns_limacity ..."
# --
# export LIMACITY_APIKEY="<API-KEY>"
# --
#
# Pleas note: APIKEY must have following roles: dns.admin, domains.reader
# shellcheck disable=SC2034
dns_limacity_info='lima-city.de
Site: www.lima-city.de
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_limacity
Options:
LIMACITY_APIKEY API Key. Note: The API Key must have following roles: dns.admin, domains.reader
Issues: github.com/acmesh-official/acme.sh/issues/4758
Author: @Laraveluser
'
######## Public functions #####################

215
dnsapi/dns_mijnhost.sh Normal file
View File

@ -0,0 +1,215 @@
#!/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
Options:
MIJNHOST_API_KEY API Key
'
######## Public functions ###################### Constants for your mijn-host API
MIJNHOST_API="https://mijn.host/api/v2"
# Add TXT record for domain verification
dns_mijnhost_add() {
fulldomain=$1
txtvalue=$2
MIJNHOST_API_KEY="${MIJNHOST_API_KEY:-$(_readaccountconf_mutable MIJNHOST_API_KEY)}"
if [ -z "$MIJNHOST_API_KEY" ]; then
MIJNHOST_API_KEY=""
_err "You haven't specified your mijn-host API key yet."
_err "Please add MIJNHOST_API_KEY to the env."
return 1
fi
# Save the API key for future use
_saveaccountconf_mutable MIJNHOST_API_KEY "$MIJNHOST_API_KEY"
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "Invalid domain"
return 1
fi
_debug2 _sub_domain "$_sub_domain"
_debug2 _domain "$_domain"
_debug "Adding DNS record" "${fulldomain}."
# Construct the API URL
api_url="$MIJNHOST_API/domains/$_domain/dns"
# Getting previous records
_mijnhost_rest GET "$api_url" ""
if [ "$_code" != "200" ]; then
_err "Error getting current DNS enties ($_code)"
return 1
fi
records=$(echo "$response" | _egrep_o '"records":\[.*\]' | sed 's/"records"://')
_debug2 "Current records" "$records"
# Build the payload for the API
data="{\"type\":\"TXT\",\"name\":\"$fulldomain.\",\"value\":\"$txtvalue\",\"ttl\":300}"
_debug2 "Record to add" "$data"
# Updating the records
updated_records=$(echo "$records" | sed -E "s/\]( *$)/,$data\]/")
_debug2 "Updated records" "$updated_records"
# data
data="{\"records\": $updated_records}"
_mijnhost_rest PUT "$api_url" "$data"
if [ "$_code" = "200" ]; then
_info "DNS record succesfully added."
return 0
else
_err "Error adding DNS record ($_code)."
return 1
fi
}
# Remove TXT record after verification
dns_mijnhost_rm() {
fulldomain=$1
txtvalue=$2
MIJNHOST_API_KEY="${MIJNHOST_API_KEY:-$(_readaccountconf_mutable MIJNHOST_API_KEY)}"
if [ -z "$MIJNHOST_API_KEY" ]; then
MIJNHOST_API_KEY=""
_err "You haven't specified your mijn-host API key yet."
_err "Please add MIJNHOST_API_KEY to the env."
return 1
fi
_debug "Detecting root zone for" "${fulldomain}."
if ! _get_root "$fulldomain"; then
_err "Invalid domain"
return 1
fi
_debug "Removing DNS record for TXT value" "${txtvalue}."
# Construct the API URL
api_url="$MIJNHOST_API/domains/$_domain/dns"
# Get current records
_mijnhost_rest GET "$api_url" ""
if [ "$_code" != "200" ]; then
_err "Error getting current DNS enties ($_code)"
return 1
fi
_debug2 "Get current records response:" "$response"
records=$(echo "$response" | _egrep_o '"records":\[.*\]' | sed 's/"records"://')
_debug2 "Current records:" "$records"
updated_records=$(echo "$records" | sed -E "s/\{[^}]*\"value\":\"$txtvalue\"[^}]*\},?//g" | sed 's/,]/]/g')
_debug2 "Updated records:" "$updated_records"
# Build the new payload
data="{\"records\": $updated_records}"
# Use the _put method to update the records
_mijnhost_rest PUT "$api_url" "$data"
if [ "$_code" = "200" ]; then
_info "DNS record removed successfully."
return 0
else
_err "Error removing DNS record ($_code)."
return 1
fi
}
# Helper function to detect the root zone
_get_root() {
domain=$1
# Get current records
_debug "Getting current domains"
_mijnhost_rest GET "$MIJNHOST_API/domains" ""
if [ "$_code" != "200" ]; then
_err "error getting current domains ($_code)"
return 1
fi
# Extract root domains from response
rootDomains=$(echo "$response" | _egrep_o '"domain":"[^"]*"' | sed -E 's/"domain":"([^"]*)"/\1/')
_debug "Root domains:" "$rootDomains"
for rootDomain in $rootDomains; do
if _contains "$domain" "$rootDomain"; then
_domain="$rootDomain"
_sub_domain=$(echo "$domain" | sed "s/.$rootDomain//g")
_debug "Found root domain" "$_domain" "and subdomain" "$_sub_domain" "for" "$domain"
return 0
fi
done
return 1
}
# Helper function for rest calls
_mijnhost_rest() {
m=$1
ep="$2"
data="$3"
MAX_REQUEST_RETRY_TIMES=15
_request_retry_times=0
_retry_sleep=5 #Initial sleep time in seconds.
while [ "${_request_retry_times}" -lt "$MAX_REQUEST_RETRY_TIMES" ]; do
_debug2 _request_retry_times "$_request_retry_times"
export _H1="API-Key: $MIJNHOST_API_KEY"
export _H2="Content-Type: application/json"
# clear headers from previous request to avoid getting wrong http code on timeouts
: >"$HTTP_HEADER"
_debug "$ep"
if [ "$m" != "GET" ]; then
_debug2 "data $data"
response="$(_post "$data" "$ep" "" "$m")"
else
response="$(_get "$ep")"
fi
_ret="$?"
_debug2 "response $response"
_code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")"
_debug "http response code $_code"
if [ "$_code" = "401" ]; then
# we have an invalid API token, maybe it is expired?
_err "Access denied. Invalid API token."
return 1
fi
if [ "$_ret" != "0" ] || [ -z "$_code" ] || [ "$_code" = "400" ] || _contains "$response" "DNS records not managed by mijn.host"; then #Sometimes API errors out
_request_retry_times="$(_math "$_request_retry_times" + 1)"
_info "REST call error $_code retrying $ep in ${_retry_sleep}s"
_sleep "$_retry_sleep"
_retry_sleep="$(_math "$_retry_sleep" \* 2)"
continue
fi
break
done
if [ "$_request_retry_times" = "$MAX_REQUEST_RETRY_TIMES" ]; then
_err "Error mijn.host API call was retried $MAX_REQUEST_RETRY_TIMES times."
_err "Calling $ep failed."
return 1
fi
response="$(echo "$response" | _normalizeJson)"
return 0
}

View File

@ -1,12 +1,14 @@
#!/usr/bin/env sh
# shellcheck disable=SC2034
dns_myapi_info='Custom API Example
A sample custom DNS API script.
Domains: example.com
A sample custom DNS API script description.
Domains: example.com example.net
Site: github.com/acmesh-official/acme.sh/wiki/DNS-API-Dev-Guide
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_duckdns
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_myapi
Options:
MYAPI_Token API Token. Get API Token from https://example.com/api/. Optional.
MYAPI_Token API Token. Get API Token from https://example.com/api/
MYAPI_Variable2 Option 2. Default "default value".
MYAPI_Variable2 Option 3. Optional.
Issues: github.com/acmesh-official/acme.sh
Author: Neil Pang <neilgit@neilpang.com>
'

View File

@ -19,7 +19,7 @@ client=""
dns_netcup_add() {
_debug NC_Apikey "$NC_Apikey"
login
_login
if [ "$NC_Apikey" = "" ] || [ "$NC_Apipw" = "" ] || [ "$NC_CID" = "" ]; then
_err "No Credentials given"
return 1
@ -61,7 +61,7 @@ dns_netcup_add() {
}
dns_netcup_rm() {
login
_login
fulldomain=$1
txtvalue=$2
@ -125,7 +125,7 @@ dns_netcup_rm() {
logout
}
login() {
_login() {
tmp=$(_post "{\"action\": \"login\", \"param\": {\"apikey\": \"$NC_Apikey\", \"apipassword\": \"$NC_Apipw\", \"customernumber\": \"$NC_CID\"}}" "$end" "" "POST")
sid=$(echo "$tmp" | tr '{}' '\n' | grep apisessionid | cut -d '"' -f 4)
_debug "$tmp"

View File

@ -4,8 +4,8 @@ dns_omglol_info='omg.lol
Site: omg.lol
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_omglol
Options:
OMG_ApiKey API Key from omg.lol. This is accessible from the bottom of the account page at https://home.omg.lol/account
OMG_Address This is your omg.lol address, without the preceding @ - you can see your list on your dashboard at https://home.omg.lol/dashboard
OMG_ApiKey API Key. This is accessible from the bottom of the account page at https://home.omg.lol/account
OMG_Address Address. This is your omg.lol address, without the preceding @ - you can see your list on your dashboard at https://home.omg.lol/dashboard
Issues: github.com/acmesh-official/acme.sh/issues/5299
Author: @Kholin <kholin+acme.omglolapi@omg.lol>
'

View File

@ -2,6 +2,7 @@
# shellcheck disable=SC2034
dns_openprovider_info='OpenProvider.eu
Site: OpenProvider.eu
Domains: OpenProvider.com
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_openprovider
Options:
OPENPROVIDER_USER Username

View File

@ -7,7 +7,7 @@ Options:
PDNS_Url API URL. E.g. "http://ns.example.com:8081"
PDNS_ServerId Server ID. E.g. "localhost"
PDNS_Token API Token
PDNS_Ttl=60 Domain TTL. Default: "60".
PDNS_Ttl Domain TTL. Default: "60".
'
DEFAULT_PDNS_TTL=60

View File

@ -1,14 +1,31 @@
#!/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:
SL_Key API Key
'
SL_Api="https://api.selectel.ru/domains/v1"
# 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
#
SL_Api="https://api.selectel.ru/domains"
auth_uri="https://cloud.api.selcloud.ru/identity/v3/auth/tokens"
_sl_sep='#'
######## Public functions #####################
@ -17,17 +34,14 @@ dns_selectel_add() {
fulldomain=$1
txtvalue=$2
SL_Key="${SL_Key:-$(_readaccountconf_mutable SL_Key)}"
if [ -z "$SL_Key" ]; then
SL_Key=""
_err "You don't specify selectel.ru api key yet."
_err "Please create you key and try again."
if ! _sl_init_vars; then
return 1
fi
#save the api key to the account conf file.
_saveaccountconf_mutable SL_Key "$SL_Key"
_debug2 SL_Ver "$SL_Ver"
_debug2 SL_Expire "$SL_Expire"
_debug2 SL_Login_Name "$SL_Login_Name"
_debug2 SL_Login_ID "$SL_Login_ID"
_debug2 SL_Project_Name "$SL_Project_Name"
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
@ -39,11 +53,63 @@ dns_selectel_add() {
_debug _domain "$_domain"
_info "Adding record"
if _sl_rest POST "/$_domain_id/records/" "{\"type\": \"TXT\", \"ttl\": 60, \"name\": \"$fulldomain\", \"content\": \"$txtvalue\"}"; then
if _contains "$response" "$txtvalue" || _contains "$response" "record_already_exists"; then
if [ "$SL_Ver" = "v2" ]; then
_ext_srv1="/zones/"
_ext_srv2="/rrset/"
_text_tmp=$(echo "$txtvalue" | sed -En "s/[\"]*([^\"]*)/\1/p")
_text_tmp='\"'$_text_tmp'\"'
_data="{\"type\": \"TXT\", \"ttl\": 60, \"name\": \"${fulldomain}.\", \"records\": [{\"content\":\"$_text_tmp\"}]}"
elif [ "$SL_Ver" = "v1" ]; then
_ext_srv1="/"
_ext_srv2="/records/"
_data="{\"type\":\"TXT\",\"ttl\":60,\"name\":\"$fulldomain\",\"content\":\"$txtvalue\"}"
else
_err "Error. Unsupported version API $SL_Ver"
return 1
fi
_ext_uri="${_ext_srv1}$_domain_id${_ext_srv2}"
_debug _ext_uri "$_ext_uri"
_debug _data "$_data"
if _sl_rest POST "$_ext_uri" "$_data"; then
if _contains "$response" "$txtvalue"; then
_info "Added, OK"
return 0
fi
if _contains "$response" "already_exists"; then
# record TXT with $fulldomain already exists
if [ "$SL_Ver" = "v2" ]; then
# It is necessary to add one more content to the comments
# read all records rrset
_debug "Getting txt records"
_sl_rest GET "${_ext_uri}"
# There is already a $txtvalue value, no need to add it
if _contains "$response" "$txtvalue"; then
_info "Added, OK"
_info "Txt record ${fulldomain} with value ${txtvalue} already exists"
return 0
fi
# group \1 - full record rrset; group \2 - records attribute value, exactly {"content":"\"value1\""},{"content":"\"value2\""}",...
_record_seg="$(echo "$response" | sed -En "s/.*(\{\"id\"[^}]*${fulldomain}[^}]*records[^}]*\[(\{[^]]*\})\][^}]*}).*/\1/p")"
_record_array="$(echo "$response" | sed -En "s/.*(\{\"id\"[^}]*${fulldomain}[^}]*records[^}]*\[(\{[^]]*\})\][^}]*}).*/\2/p")"
# record id
_record_id="$(echo "$_record_seg" | tr "," "\n" | tr "}" "\n" | tr -d " " | grep "\"id\"" | cut -d : -f 2 | tr -d "\"")"
# preparing _data
_tmp_str="${_record_array},{\"content\":\"${_text_tmp}\"}"
_data="{\"ttl\": 60, \"records\": [${_tmp_str}]}"
_debug2 _record_seg "$_record_seg"
_debug2 _record_array "$_record_array"
_debug2 _record_array "$_record_id"
_debug "New data for record" "$_data"
if _sl_rest PATCH "${_ext_uri}${_record_id}" "$_data"; then
_info "Added, OK"
return 0
fi
elif [ "$SL_Ver" = "v1" ]; then
_info "Added, OK"
return 0
fi
fi
fi
_err "Add txt record error."
return 1
@ -54,15 +120,15 @@ dns_selectel_rm() {
fulldomain=$1
txtvalue=$2
SL_Key="${SL_Key:-$(_readaccountconf_mutable SL_Key)}"
if [ -z "$SL_Key" ]; then
SL_Key=""
_err "You don't specify slectel api key yet."
_err "Please create you key and try again."
if ! _sl_init_vars "nosave"; then
return 1
fi
_debug2 SL_Ver "$SL_Ver"
_debug2 SL_Expire "$SL_Expire"
_debug2 SL_Login_Name "$SL_Login_Name"
_debug2 SL_Login_ID "$SL_Login_ID"
_debug2 SL_Project_Name "$SL_Project_Name"
#
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
@ -71,91 +137,195 @@ dns_selectel_rm() {
_debug _domain_id "$_domain_id"
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
#
if [ "$SL_Ver" = "v2" ]; then
_ext_srv1="/zones/"
_ext_srv2="/rrset/"
elif [ "$SL_Ver" = "v1" ]; then
_ext_srv1="/"
_ext_srv2="/records/"
else
_err "Error. Unsupported version API $SL_Ver"
return 1
fi
#
_debug "Getting txt records"
_sl_rest GET "/${_domain_id}/records/"
_ext_uri="${_ext_srv1}$_domain_id${_ext_srv2}"
_debug _ext_uri "$_ext_uri"
_sl_rest GET "${_ext_uri}"
#
if ! _contains "$response" "$txtvalue"; then
_err "Txt record not found"
return 1
fi
_record_seg="$(echo "$response" | _egrep_o "[^{]*\"content\" *: *\"$txtvalue\"[^}]*}")"
#
if [ "$SL_Ver" = "v2" ]; then
_record_seg="$(echo "$response" | sed -En "s/.*(\{\"id\"[^}]*records[^[]*(\[(\{[^]]*${txtvalue}[^]]*)\])[^}]*}).*/\1/gp")"
_record_arr="$(echo "$response" | sed -En "s/.*(\{\"id\"[^}]*records[^[]*(\[(\{[^]]*${txtvalue}[^]]*)\])[^}]*}).*/\3/p")"
elif [ "$SL_Ver" = "v1" ]; then
_record_seg="$(echo "$response" | _egrep_o "[^{]*\"content\" *: *\"$txtvalue\"[^}]*}")"
else
_err "Error. Unsupported version API $SL_Ver"
return 1
fi
_debug2 "_record_seg" "$_record_seg"
if [ -z "$_record_seg" ]; then
_err "can not find _record_seg"
return 1
fi
_record_id="$(echo "$_record_seg" | tr "," "\n" | tr "}" "\n" | tr -d " " | grep "\"id\"" | cut -d : -f 2)"
_debug2 "_record_id" "$_record_id"
# record id
# the following lines change the algorithm for deleting records with the value $txtvalue
# if you use the 1st line, then all such records are deleted at once
# if you use the 2nd line, then only the first entry from them is deleted
#_record_id="$(echo "$_record_seg" | tr "," "\n" | tr "}" "\n" | tr -d " " | grep "\"id\"" | cut -d : -f 2 | tr -d "\"")"
_record_id="$(echo "$_record_seg" | tr "," "\n" | tr "}" "\n" | tr -d " " | grep "\"id\"" | cut -d : -f 2 | tr -d "\"" | sed '1!d')"
if [ -z "$_record_id" ]; then
_err "can not find _record_id"
return 1
fi
if ! _sl_rest DELETE "/$_domain_id/records/$_record_id"; then
_err "Delete record error."
return 1
_debug2 "_record_id" "$_record_id"
# delete all record type TXT with text $txtvalue
if [ "$SL_Ver" = "v2" ]; then
# actual
_new_arr="$(echo "$_record_seg" | sed -En "s/.*(\{\"id\"[^}]*records[^[]*(\[(\{[^]]*${txtvalue}[^]]*)\])[^}]*}).*/\3/gp" | sed -En "s/(\},\{)/}\n{/gp" | sed "/${txtvalue}/d" | sed ":a;N;s/\n/,/;ta")"
# uri record for DEL or PATCH
_del_uri="${_ext_uri}${_record_id}"
_debug _del_uri "$_del_uri"
if [ -z "$_new_arr" ]; then
# remove record
if ! _sl_rest DELETE "${_del_uri}"; then
_err "Delete record error: ${_del_uri}."
else
info "Delete record success: ${_del_uri}."
fi
else
# update a record by removing one element in content
_data="{\"ttl\": 60, \"records\": [${_new_arr}]}"
_debug2 _data "$_data"
# REST API PATCH call
if _sl_rest PATCH "${_del_uri}" "$_data"; then
_info "Patched, OK: ${_del_uri}"
else
_err "Patched record error: ${_del_uri}."
fi
fi
else
# legacy
for _one_id in $_record_id; do
_del_uri="${_ext_uri}${_one_id}"
_debug _del_uri "$_del_uri"
if ! _sl_rest DELETE "${_del_uri}"; then
_err "Delete record error: ${_del_uri}."
else
info "Delete record success: ${_del_uri}."
fi
done
fi
return 0
}
#################### Private functions below ##################################
#_acme-challenge.www.domain.com
#returns
# _sub_domain=_acme-challenge.www
# _domain=domain.com
# _domain_id=sdjkglgdfewsdfg
_get_root() {
domain=$1
if ! _sl_rest GET "/"; then
return 1
fi
i=2
p=1
while true; do
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
_debug h "$h"
if [ -z "$h" ]; then
#not valid
if [ "$SL_Ver" = 'v1' ]; then
# version API 1
if ! _sl_rest GET "/"; then
return 1
fi
if _contains "$response" "\"name\" *: *\"$h\","; then
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain=$h
_debug "Getting domain id for $h"
if ! _sl_rest GET "/$h"; then
i=2
p=1
while true; do
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
_debug h "$h"
if [ -z "$h" ]; then
return 1
fi
_domain_id="$(echo "$response" | tr "," "\n" | tr "}" "\n" | tr -d " " | grep "\"id\":" | cut -d : -f 2)"
return 0
if _contains "$response" "\"name\" *: *\"$h\","; then
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain=$h
_debug "Getting domain id for $h"
if ! _sl_rest GET "/$h"; then
_err "Error read records of all domains $SL_Ver"
return 1
fi
_domain_id="$(echo "$response" | tr "," "\n" | tr "}" "\n" | tr -d " " | grep "\"id\":" | cut -d : -f 2)"
return 0
fi
p=$i
i=$(_math "$i" + 1)
done
_err "Error read records of all domains $SL_Ver"
return 1
elif [ "$SL_Ver" = "v2" ]; then
# version API 2
_ext_uri='/zones/'
domain="${domain}."
_debug "domain:: " "$domain"
# read records of all domains
if ! _sl_rest GET "$_ext_uri"; then
_err "Error read records of all domains $SL_Ver"
return 1
fi
p=$i
i=$(_math "$i" + 1)
done
return 1
i=1
p=1
while true; do
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
_debug h "$h"
if [ -z "$h" ]; then
_err "The domain was not found among the registered ones"
return 1
fi
_domain_record=$(echo "$response" | sed -En "s/.*(\{[^}]*id[^}]*\"name\" *: *\"$h\"[^}]*}).*/\1/p")
_debug "_domain_record:: " "$_domain_record"
if [ -n "$_domain_record" ]; then
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain=$h
_debug "Getting domain id for $h"
_domain_id=$(echo "$_domain_record" | sed -En "s/\{[^}]*\"id\" *: *\"([^\"]*)\"[^}]*\}/\1/p")
return 0
fi
p=$i
i=$(_math "$i" + 1)
done
_err "Error read records of all domains $SL_Ver"
return 1
else
_err "Error. Unsupported version API $SL_Ver"
return 1
fi
}
#################################################################
# use: method add_url body
_sl_rest() {
m=$1
ep="$2"
data="$3"
_debug "$ep"
export _H1="X-Token: $SL_Key"
_token=$(_get_auth_token)
if [ -z "$_token" ]; then
_err "BAD key or token $ep"
return 1
fi
if [ "$SL_Ver" = v2 ]; then
_h1_name="X-Auth-Token"
else
_h1_name='X-Token'
fi
export _H1="${_h1_name}: ${_token}"
export _H2="Content-Type: application/json"
_debug2 "Full URI: " "$SL_Api/${SL_Ver}${ep}"
_debug2 "_H1:" "$_H1"
_debug2 "_H2:" "$_H2"
if [ "$m" != "GET" ]; then
_debug data "$data"
response="$(_post "$data" "$SL_Api/$ep" "" "$m")"
response="$(_post "$data" "$SL_Api/${SL_Ver}${ep}" "" "$m")"
else
response="$(_get "$SL_Api/$ep")"
response="$(_get "$SL_Api/${SL_Ver}${ep}")"
fi
# shellcheck disable=SC2181
if [ "$?" != "0" ]; then
_err "error $ep"
return 1
@ -163,3 +333,152 @@ _sl_rest() {
_debug2 response "$response"
return 0
}
_get_auth_token() {
if [ "$SL_Ver" = 'v1' ]; then
# token for v1
_debug "Token v1"
_token_keystone=$SL_Key
elif [ "$SL_Ver" = 'v2' ]; then
# token for v2. Get a token for calling the API
_debug "Keystone Token v2"
token_v2=$(_readaccountconf_mutable SL_Token_V2)
if [ -n "$token_v2" ]; then
# The structure with the token was considered. Let's check its validity
# field 1 - SL_Login_Name
# field 2 - token keystone
# field 3 - SL_Login_ID
# field 4 - SL_Project_Name
# field 5 - Receipt time
# separator - '$_sl_sep'
_login_name=$(_getfield "$token_v2" 1 "$_sl_sep")
_token_keystone=$(_getfield "$token_v2" 2 "$_sl_sep")
_project_name=$(_getfield "$token_v2" 4 "$_sl_sep")
_receipt_time=$(_getfield "$token_v2" 5 "$_sl_sep")
_login_id=$(_getfield "$token_v2" 3 "$_sl_sep")
_debug2 _login_name "$_login_name"
_debug2 _login_id "$_login_id"
_debug2 _project_name "$_project_name"
# check the validity of the token for the user and the project and its lifetime
_dt_diff_minute=$((($(date +%s) - _receipt_time) / 60))
_debug2 _dt_diff_minute "$_dt_diff_minute"
[ "$_dt_diff_minute" -gt "$SL_Expire" ] && unset _token_keystone
if [ "$_project_name" != "$SL_Project_Name" ] || [ "$_login_name" != "$SL_Login_Name" ] || [ "$_login_id" != "$SL_Login_ID" ]; then
unset _token_keystone
fi
_debug "Get exists token"
fi
if [ -z "$_token_keystone" ]; then
# the previous token is incorrect or was not received, get a new one
_debug "Update (get new) token"
_data_auth="{\"auth\":{\"identity\":{\"methods\":[\"password\"],\"password\":{\"user\":{\"name\":\"${SL_Login_Name}\",\"domain\":{\"name\":\"${SL_Login_ID}\"},\"password\":\"${SL_Pswd}\"}}},\"scope\":{\"project\":{\"name\":\"${SL_Project_Name}\",\"domain\":{\"name\":\"${SL_Login_ID}\"}}}}}"
export _H1="Content-Type: application/json"
_result=$(_post "$_data_auth" "$auth_uri")
_token_keystone=$(grep 'x-subject-token' "$HTTP_HEADER" | sed -nE "s/[[:space:]]*x-subject-token:[[:space:]]*([[:print:]]*)(\r*)/\1/p")
_dt_curr=$(date +%s)
SL_Token_V2="${SL_Login_Name}${_sl_sep}${_token_keystone}${_sl_sep}${SL_Login_ID}${_sl_sep}${SL_Project_Name}${_sl_sep}${_dt_curr}"
_saveaccountconf_mutable SL_Token_V2 "$SL_Token_V2"
fi
else
# token set empty for unsupported version API
_token_keystone=""
fi
printf -- "%s" "$_token_keystone"
}
#################################################################
# use: [non_save]
_sl_init_vars() {
_non_save="${1}"
_debug2 _non_save "$_non_save"
_debug "First init variables"
# version API
SL_Ver="${SL_Ver:-$(_readaccountconf_mutable SL_Ver)}"
if [ -z "$SL_Ver" ]; then
SL_Ver="v1"
fi
if ! [ "$SL_Ver" = "v1" ] && ! [ "$SL_Ver" = "v2" ]; then
_err "You don't specify selectel.ru API version."
_err "Please define specify API version."
fi
_debug2 SL_Ver "$SL_Ver"
if [ "$SL_Ver" = "v1" ]; then
# token
SL_Key="${SL_Key:-$(_readaccountconf_mutable SL_Key)}"
if [ -z "$SL_Key" ]; then
SL_Key=""
_err "You don't specify selectel.ru api key yet."
_err "Please create you key and try again."
return 1
fi
#save the api key to the account conf file.
if [ -z "$_non_save" ]; then
_saveaccountconf_mutable SL_Key "$SL_Key"
fi
elif [ "$SL_Ver" = "v2" ]; then
# time expire token
SL_Expire="${SL_Expire:-$(_readaccountconf_mutable SL_Expire)}"
if [ -z "$SL_Expire" ]; then
SL_Expire=1400 # 23h 20 min
fi
if [ -z "$_non_save" ]; then
_saveaccountconf_mutable SL_Expire "$SL_Expire"
fi
# login service user
SL_Login_Name="${SL_Login_Name:-$(_readaccountconf_mutable SL_Login_Name)}"
if [ -z "$SL_Login_Name" ]; then
SL_Login_Name=''
_err "You did not specify the selectel.ru API service user name."
_err "Please provide a service user name and try again."
return 1
fi
if [ -z "$_non_save" ]; then
_saveaccountconf_mutable SL_Login_Name "$SL_Login_Name"
fi
# user ID
SL_Login_ID="${SL_Login_ID:-$(_readaccountconf_mutable SL_Login_ID)}"
if [ -z "$SL_Login_ID" ]; then
SL_Login_ID=''
_err "You did not specify the selectel.ru API user ID."
_err "Please provide a user ID and try again."
return 1
fi
if [ -z "$_non_save" ]; then
_saveaccountconf_mutable SL_Login_ID "$SL_Login_ID"
fi
# project name
SL_Project_Name="${SL_Project_Name:-$(_readaccountconf_mutable SL_Project_Name)}"
if [ -z "$SL_Project_Name" ]; then
SL_Project_Name=''
_err "You did not specify the project name."
_err "Please provide a project name and try again."
return 1
fi
if [ -z "$_non_save" ]; then
_saveaccountconf_mutable SL_Project_Name "$SL_Project_Name"
fi
# service user password
SL_Pswd="${SL_Pswd:-$(_readaccountconf_mutable SL_Pswd)}"
if [ -z "$SL_Pswd" ]; then
SL_Pswd=''
_err "You did not specify the service user password."
_err "Please provide a service user password and try again."
return 1
fi
if [ -z "$_non_save" ]; then
_saveaccountconf_mutable SL_Pswd "$SL_Pswd" "12345678"
fi
else
SL_Ver=""
_err "You also specified the wrong version of the selectel.ru API."
_err "Please provide the correct API version and try again."
return 1
fi
if [ -z "$_non_save" ]; then
_saveaccountconf_mutable SL_Ver "$SL_Ver"
fi
return 0
}

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 Spaceship API Key
SPACESHIP_API_SECRET Spaceship API Secret
SPACESHIP_ROOT_DOMAIN (Optional) Manually specify the root domain if auto-detection fails
Issues: github.com/acmesh-official/acme.sh/issues/6304
Author: Meow <https://github.com/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
}

55
dnsapi/dns_technitium.sh Executable file
View File

@ -0,0 +1,55 @@
#!/usr/bin/env sh
# shellcheck disable=SC2034
dns_technitium_info='Technitium DNS Server
Site: Technitium.com/dns/
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_technitium
Options:
Technitium_Server Server Address
Technitium_Token API Token
Issues: github.com/acmesh-official/acme.sh/issues/6116
Author: Henning Reich <acmesh@qupfer.de>
'
dns_technitium_add() {
_info "add txt Record using Technitium"
_Technitium_account
fulldomain=$1
txtvalue=$2
response="$(_get "$Technitium_Server/api/zones/records/add?token=$Technitium_Token&domain=$fulldomain&type=TXT&text=${txtvalue}")"
if _contains "$response" '"status":"ok"'; then
return 0
fi
_err "Could not add txt record."
return 1
}
dns_technitium_rm() {
_info "remove txt record using Technitium"
_Technitium_account
fulldomain=$1
txtvalue=$2
response="$(_get "$Technitium_Server/api/zones/records/delete?token=$Technitium_Token&domain=$fulldomain&type=TXT&text=${txtvalue}")"
if _contains "$response" '"status":"ok"'; then
return 0
fi
_err "Could not remove txt record"
return 1
}
#################### Private functions below ##################################
_Technitium_account() {
Technitium_Server="${Technitium_Server:-$(_readaccountconf_mutable Technitium_Server)}"
Technitium_Token="${Technitium_Token:-$(_readaccountconf_mutable Technitium_Token)}"
if [ -z "$Technitium_Server" ] || [ -z "$Technitium_Token" ]; then
Technitium_Server=""
Technitium_Token=""
_err "You don't specify Technitium Server and Token yet."
_err "Please create your Token and add server address and try again."
return 1
fi
#save the credentials to the account conf file.
_saveaccountconf_mutable Technitium_Server "$Technitium_Server"
_saveaccountconf_mutable Technitium_Token "$Technitium_Token"
}

View File

@ -1,9 +1,13 @@
#!/usr/bin/env sh
# West.cn Domain api
#WEST_Username="username"
#WEST_Key="sADDsdasdgdsf"
#Set key at https://www.west.cn/manager/API/APIconfig.asp
# shellcheck disable=SC2034
dns_west_cn_info='West.cn
Site: West.cn
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_west_cn
Options:
WEST_Username API username
WEST_Key API Key. Set at https://www.west.cn/manager/API/APIconfig.asp
Issues: github.com/acmesh-official/acme.sh/issues/4894
'
REST_API="https://api.west.cn/API/v2"

View File

@ -115,7 +115,7 @@ dns_world4you_rm() {
_resethttp
export ACME_HTTP_NO_REDIRECTS=1
body="DeleteDnsRecordForm[recordId]=$recordid&DeleteDnsRecordForm[uniqueFormIdDP]=$formiddp&DeleteDnsRecordForm[_token]=$form_token"
body="DeleteDnsRecordForm[id]=$recordid&DeleteDnsRecordForm[uniqueFormIdDP]=$formiddp&DeleteDnsRecordForm[_token]=$form_token"
_info "Removing record..."
ret=$(_post "$body" "$WORLD4YOU_API/$paketnr/dns/record/delete" '' POST 'application/x-www-form-urlencoded')
_resethttp
@ -202,7 +202,8 @@ _get_paketnr() {
fqdn="$1"
form="$2"
domains=$(echo "$form" | grep '<ul class="nav header-paket-list">' | sed 's/<li/\n<li/g' | sed 's/<[^>]*>/ /g' | sed 's/^.*>\([^>]*\)$/\1/')
domains=$(echo "$form" | grep 'paketListData' | grep -o '"fqdn":"[^"]*"' | sed 's/.*:"\(.*\)"/\1/')
_debug domains "$domains"
domain=''
for domain in $domains; do
if _contains "$fqdn" "$domain\$"; then
@ -217,7 +218,7 @@ _get_paketnr() {
TLD="$domain"
_debug domain "$domain"
RECORD=$(echo "$fqdn" | cut -c"1-$((${#fqdn} - ${#TLD} - 1))")
PAKETNR=$(echo "$domains" | grep "$domain" | sed 's/^[^,]*, *\([0-9]*\).*$/\1/')
PAKETNR=$(echo "$form" | grep -o "\"id\":[^{}]*\"fqdn\":\"$domain\"" | sed 's/"id":\([0-9]*\).*$/\1/')
return 0
}

View File

@ -1,7 +1,7 @@
#!/usr/bin/env sh
# shellcheck disable=SC2034
dns_yandex360_info='Yandex 360 for Business DNS API.
Yandex 360 for Business is a digital environment for effective collaboration.
Yandex 360 for Business is a digital environment for effective collaboration.
Site: https://360.yandex.com/
Docs: https://github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_yandex360
Options:

149
dnsapi/dns_zoneedit.sh Normal file
View File

@ -0,0 +1,149 @@
#!/usr/bin/env sh
# shellcheck disable=SC2034
dns_zoneedit_info='ZoneEdit.com
Site: ZoneEdit.com
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_zoneedit
Options:
ZONEEDIT_ID ID
ZONEEDIT_Token API Token
Issues: github.com/acmesh-official/acme.sh/issues/6135
'
# https://github.com/blueslow/sslcertzoneedit
######## Public functions #####################
# Usage: dns_zoneedit_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_zoneedit_add() {
fulldomain=$1
txtvalue=$2
_info "Using ZoneEdit"
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
# Load the credentials from the account conf file
ZONEEDIT_ID="${ZONEEDIT_ID:-$(_readaccountconf_mutable ZONEEDIT_ID)}"
ZONEEDIT_Token="${ZONEEDIT_Token:-$(_readaccountconf_mutable ZONEEDIT_Token)}"
if [ -z "$ZONEEDIT_ID" ] || [ -z "$ZONEEDIT_Token" ]; then
ZONEEDIT_ID=""
ZONEEDIT_Token=""
_err "Please specify ZONEEDIT_ID and _Token."
_err "Please export as ZONEEDIT_ID and ZONEEDIT_Token then try again."
return 1
fi
# Save the credentials to the account conf file
_saveaccountconf_mutable ZONEEDIT_ID "$ZONEEDIT_ID"
_saveaccountconf_mutable ZONEEDIT_Token "$ZONEEDIT_Token"
if _zoneedit_api "CREATE" "$fulldomain" "$txtvalue"; then
_info "Added, OK"
return 0
else
_err "Add txt record error."
return 1
fi
}
# Usage: dns_zoneedit_rm fulldomain txtvalue
dns_zoneedit_rm() {
fulldomain=$1
txtvalue=$2
_info "Using ZoneEdit"
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
# Load the credentials from the account conf file
ZONEEDIT_ID="${ZONEEDIT_ID:-$(_readaccountconf_mutable ZONEEDIT_ID)}"
ZONEEDIT_Token="${ZONEEDIT_Token:-$(_readaccountconf_mutable ZONEEDIT_Token)}"
if [ -z "$ZONEEDIT_ID" ] || [ -z "$ZONEEDIT_Token" ]; then
ZONEEDIT_ID=""
ZONEEDIT_Token=""
_err "Please specify ZONEEDIT_ID and _Token."
_err "Please export as ZONEEDIT_ID and ZONEEDIT_Token then try again."
return 1
fi
if _zoneedit_api "DELETE" "$fulldomain" "$txtvalue"; then
_info "Deleted, OK"
return 0
else
_err "Delete txt record error."
return 1
fi
}
#################### Private functions below ##################################
#Usage: _zoneedit_api <CREATE|DELETE> fulldomain txtvalue
_zoneedit_api() {
cmd=$1
fulldomain=$2
txtvalue=$3
# Construct basic authorization header
credentials=$(printf "%s:%s" "$ZONEEDIT_ID" "$ZONEEDIT_Token" | _base64)
export _H1="Authorization: Basic ${credentials}"
# Generate request URL
case "$cmd" in
"CREATE")
# https://dynamic.zoneedit.com/txt-create.php?host=_acme-challenge.example.com&rdata=depE1VF_xshMm1IVY1Y56Kk9Zb_7jA2VFkP65WuNgu8W
geturl="https://dynamic.zoneedit.com/txt-create.php?host=${fulldomain}&rdata=${txtvalue}"
;;
"DELETE")
# https://dynamic.zoneedit.com/txt-delete.php?host=_acme-challenge.example.com&rdata=depE1VF_xshMm1IVY1Y56Kk9Zb_7jA2VFkP65WuNgu8W
geturl="https://dynamic.zoneedit.com/txt-delete.php?host=${fulldomain}&rdata=${txtvalue}"
ze_sleep=2
;;
*)
_err "Unknown parameter : $cmd"
return 1
;;
esac
# Execute request
i=3 # Tries
while [ "$i" -gt 0 ]; do
i=$(_math "$i" - 1)
if ! response=$(_get "$geturl"); then
_err "_get() failed ($response)"
return 1
fi
_debug2 response "$response"
if _contains "$response" "SUCCESS.*200"; then
# Sleep (when needed) to work around a Zonedit API bug
# https://forum.zoneedit.com/threads/automating-changes-of-txt-records-in-dns.7394/page-2#post-23855
if [ "$ze_sleep" ]; then _sleep "$ze_sleep"; fi
return 0
elif _contains "$response" "ERROR.*Minimum.*seconds"; then
_info "ZoneEdit responded with a rate limit of..."
ze_ratelimit=$(echo "$response" | sed -n 's/.*Minimum \([0-9]\+\) seconds.*/\1/p')
if [ "$ze_ratelimit" ] && [ ! "$(echo "$ze_ratelimit" | tr -d '0-9')" ]; then
_info "$ze_ratelimit seconds."
else
_err "$response"
_err "not a number, or blank ($ze_ratelimit), API change?"
unset ze_ratelimit
fi
else
_err "$response"
_err "Unknown response, API change?"
fi
# Retry
if [ "$i" -lt 1 ]; then
_err "Tries exceeded, giving up."
return 1
fi
if [ "$ze_ratelimit" ]; then
_info "Waiting $ze_ratelimit seconds..."
_sleep "$ze_ratelimit"
else
_err "Going to retry after 10 seconds..."
_sleep 10
fi
done
return 1
}

View File

@ -89,7 +89,7 @@ _use_metadata() {
_normalizeJson |
tr '{,}' '\n' |
while read -r _line; do
_key="$(echo "${_line%%:*}" | tr -d '"')"
_key="$(echo "${_line%%:*}" | tr -d \")"
_value="${_line#*:}"
_debug3 "_key" "$_key"
_secure_debug3 "_value" "$_value"

View File

@ -52,7 +52,7 @@ cqhttp_send() {
_finalUrl="$CQHTTP_APIROOT$CQHTTP_APIPATH?access_token=$_access_token&user_id=$_user_id&message=$_message"
response="$(_get "$_finalUrl")"
if [ "$?" = "0" ] && _contains "$response" "\"retcode\":0,\"status\":\"ok\""; then
if [ "$?" = "0" ] && _contains "$response" "\"retcode\":0" && _contains "$response" "\"status\":\"ok\""; then
_info "QQ send success."
return 0
fi

View File

@ -4,6 +4,7 @@
#NTFY_URL="https://ntfy.sh"
#NTFY_TOPIC="xxxxxxxxxxxxx"
#NTFY_TOKEN="xxxxxxxxxxxxx"
ntfy_send() {
_subject="$1"
@ -23,6 +24,12 @@ ntfy_send() {
_saveaccountconf_mutable NTFY_TOPIC "$NTFY_TOPIC"
fi
NTFY_TOKEN="${NTFY_TOKEN:-$(_readaccountconf_mutable NTFY_TOKEN)}"
if [ "$NTFY_TOKEN" ]; then
_saveaccountconf_mutable NTFY_TOKEN "$NTFY_TOKEN"
export _H1="Authorization: Bearer $NTFY_TOKEN"
fi
_data="${_subject}. $_content"
response="$(_post "$_data" "$NTFY_URL/$NTFY_TOPIC" "" "POST" "")"

View File

@ -4,6 +4,7 @@
#TELEGRAM_BOT_APITOKEN=""
#TELEGRAM_BOT_CHATID=""
#TELEGRAM_BOT_URLBASE=""
telegram_send() {
_subject="$1"
@ -27,6 +28,12 @@ telegram_send() {
fi
_saveaccountconf_mutable TELEGRAM_BOT_CHATID "$TELEGRAM_BOT_CHATID"
TELEGRAM_BOT_URLBASE="${TELEGRAM_BOT_URLBASE:-$(_readaccountconf_mutable TELEGRAM_BOT_URLBASE)}"
if [ -z "$TELEGRAM_BOT_URLBASE" ]; then
TELEGRAM_BOT_URLBASE="https://api.telegram.org"
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')"
_content="$(printf "*%s*\n%s" "$_subject" "$_content" | _json_encode)"
@ -38,7 +45,7 @@ telegram_send() {
_debug "$_data"
export _H1="Content-Type: application/json"
_telegram_bot_url="https://api.telegram.org/bot${TELEGRAM_BOT_APITOKEN}/sendMessage"
_telegram_bot_url="${TELEGRAM_BOT_URLBASE}/bot${TELEGRAM_BOT_APITOKEN}/sendMessage"
if _post "$_data" "$_telegram_bot_url" >/dev/null; then
# shellcheck disable=SC2154
_message=$(printf "%s\n" "$response" | sed -n 's/.*"ok":\([^,]*\).*/\1/p')