mirror of
https://github.com/acmesh-official/acme.sh.git
synced 2025-12-29 23:52:09 +08:00
Compare commits
1146 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d795cb4850 | ||
|
|
47f24126f5 | ||
|
|
76fdac59bc | ||
|
|
49a3d586a3 | ||
|
|
361e7c5ad4 | ||
|
|
fc7168e11d | ||
|
|
260df0048b | ||
|
|
11cae37405 | ||
|
|
61b59831c4 | ||
|
|
96f38655b4 | ||
|
|
ab7835ec58 | ||
|
|
b8b1f1e9b4 | ||
|
|
6b66e734a9 | ||
|
|
4f0a4850a6 | ||
|
|
8a78865174 | ||
|
|
69dd2cf78b | ||
|
|
a961e03a59 | ||
|
|
1eee4dee9c | ||
|
|
1d8788767f | ||
|
|
986a6138eb | ||
|
|
f850e8d0e4 | ||
|
|
d375012c5d | ||
|
|
37c25aa107 | ||
|
|
7b16526e7f | ||
|
|
093f36b4d6 | ||
|
|
e5b47f6402 | ||
|
|
a55d40be97 | ||
|
|
c1c49d5a01 | ||
|
|
88e4d64c1a | ||
|
|
17e0bbcbb6 | ||
|
|
95c7546051 | ||
|
|
c1e17c366f | ||
|
|
88cde7be6d | ||
|
|
c16e059535 | ||
|
|
88d4637ee3 | ||
|
|
74ed0354a3 | ||
|
|
2cc5e66517 | ||
|
|
ba7c368ee5 | ||
|
|
768de270bf | ||
|
|
db1dc4de0d | ||
|
|
fb0926dc81 | ||
|
|
34eb2a655a | ||
|
|
23e1a53ec8 | ||
|
|
67d58a12e7 | ||
|
|
0ed5e21232 | ||
|
|
b2eb1d2bbc | ||
|
|
3c184486c3 | ||
|
|
5a730bf00d | ||
|
|
cbd5dae3b4 | ||
|
|
b9c877adb9 | ||
|
|
d24c7e977e | ||
|
|
6a1ff1c0a6 | ||
|
|
383557df61 | ||
|
|
dba4be8065 | ||
|
|
a670c07caf | ||
|
|
09cc2bdfa5 | ||
|
|
cc897cab4c | ||
|
|
7ba9597928 | ||
|
|
d2d862420e | ||
|
|
fd6a14de8a | ||
|
|
0eb40c6ce6 | ||
|
|
e321b3c75c | ||
|
|
03d8d3bc1b | ||
|
|
f85de2b0d3 | ||
|
|
5fb42b7339 | ||
|
|
1b2630dc0d | ||
|
|
3fb4c313ec | ||
|
|
65892453be | ||
|
|
cba0ff8321 | ||
|
|
b6523c2301 | ||
|
|
e92d0a7492 | ||
|
|
0e5aab346f | ||
|
|
a5ad15be02 | ||
|
|
27ebf09c5c | ||
|
|
1c65c04b54 | ||
|
|
987882ea37 | ||
|
|
00aaed1b14 | ||
|
|
e3b1bccb6a | ||
|
|
85ff92170b | ||
|
|
7ac8c6c75b | ||
|
|
6004e7f5cd | ||
|
|
4a7e5d0720 | ||
|
|
b4042d5ccb | ||
|
|
1413aa332b | ||
|
|
37cf431e80 | ||
|
|
0c9d2dafe3 | ||
|
|
e8708a7489 | ||
|
|
79592c700f | ||
|
|
ad3783170e | ||
|
|
329dab9a67 | ||
|
|
f142f37064 | ||
|
|
0d2955b48d | ||
|
|
95da407de8 | ||
|
|
503ca1e9c2 | ||
|
|
d8722c46d9 | ||
|
|
546c2d47d5 | ||
|
|
2ba615555c | ||
|
|
e94c6be4a1 | ||
|
|
11eaad1fa7 | ||
|
|
daf7f7c268 | ||
|
|
4965c704d7 | ||
|
|
b7fe7a40ba | ||
|
|
3b2c2b16b2 | ||
|
|
45cb36f6d9 | ||
|
|
70e965fd55 | ||
|
|
bee01c938a | ||
|
|
5cbae50ec1 | ||
|
|
51b4fa0080 | ||
|
|
64a6ea68fa | ||
|
|
d97b4477b2 | ||
|
|
b8e394e76a | ||
|
|
671d542898 | ||
|
|
9980ad0fef | ||
|
|
004deaeea1 | ||
|
|
36b8ca2bc0 | ||
|
|
890ab4a7bb | ||
|
|
490b9e2d09 | ||
|
|
ca35e8c118 | ||
|
|
eeb91de6a3 | ||
|
|
657b7195d6 | ||
|
|
054a73f297 | ||
|
|
5c6d8aacbe | ||
|
|
ac0df6bc88 | ||
|
|
f007b46c1b | ||
|
|
c5566eafeb | ||
|
|
40290ad42a | ||
|
|
705fbcd570 | ||
|
|
75ee17aeeb | ||
|
|
9b30bd5a03 | ||
|
|
94a163397b | ||
|
|
46a2608783 | ||
|
|
d2539c3f1a | ||
|
|
8faebdc99c | ||
|
|
3d3053f427 | ||
|
|
90e6c9b87b | ||
|
|
a9f96bf709 | ||
|
|
c950b67e4b | ||
|
|
6715320e78 | ||
|
|
77805086d0 | ||
|
|
ff8c12ac75 | ||
|
|
0d1f9edf3f | ||
|
|
4bfc5aafd3 | ||
|
|
a6ff1d6924 | ||
|
|
7c7c99656a | ||
|
|
66bad853ae | ||
|
|
52ac922014 | ||
|
|
4a7f35dea7 | ||
|
|
ad2cb507a4 | ||
|
|
dca23a98f1 | ||
|
|
8955732c05 | ||
|
|
762b140808 | ||
|
|
57f8221bab | ||
|
|
b7a575b837 | ||
|
|
0a4500e85b | ||
|
|
9a994e7f36 | ||
|
|
839d611f64 | ||
|
|
421946a923 | ||
|
|
1f598a0a79 | ||
|
|
d5c669bcd2 | ||
|
|
32c664d450 | ||
|
|
adb46748de | ||
|
|
59a286b0b7 | ||
|
|
c5f41479a9 | ||
|
|
b694019a40 | ||
|
|
72259059ef | ||
|
|
0fa53d62cb | ||
|
|
47d17c1298 | ||
|
|
d187b982eb | ||
|
|
b7c8601540 | ||
|
|
693b1f7a74 | ||
|
|
5f81460504 | ||
|
|
b65f432ee0 | ||
|
|
3d21ac4525 | ||
|
|
7c5b9a5b92 | ||
|
|
82ff65c3d4 | ||
|
|
48c48cb344 | ||
|
|
a1f4f9ddb7 | ||
|
|
0c7943868d | ||
|
|
3cdce86339 | ||
|
|
8cb9713493 | ||
|
|
ef76831d37 | ||
|
|
08246f7005 | ||
|
|
c377ec69d3 | ||
|
|
a2c2b7ffee | ||
|
|
65bd3d67b4 | ||
|
|
c4671272c0 | ||
|
|
25c564bae1 | ||
|
|
3c3ec2c97c | ||
|
|
15197455f2 | ||
|
|
f7cc72be35 | ||
|
|
17da49bb78 | ||
|
|
a5754e9ec4 | ||
|
|
2584f09083 | ||
|
|
d439933b52 | ||
|
|
094d03bf7a | ||
|
|
0f5093c0b7 | ||
|
|
80748b9fe0 | ||
|
|
b244c76dd5 | ||
|
|
a91ab54449 | ||
|
|
f2dbf56db1 | ||
|
|
e5214ea2e5 | ||
|
|
11995b958a | ||
|
|
493ec4be52 | ||
|
|
604e6873ba | ||
|
|
5954f0dde5 | ||
|
|
b8e91036dd | ||
|
|
f22b490a10 | ||
|
|
49ed112ef5 | ||
|
|
471e0c05f9 | ||
|
|
c3ec827fdd | ||
|
|
ca7bdd9101 | ||
|
|
070cd0f4df | ||
|
|
86f2584162 | ||
|
|
1b00ced7ad | ||
|
|
44c7473ef9 | ||
|
|
20c80f553f | ||
|
|
b4a5149ba2 | ||
|
|
df350e6660 | ||
|
|
d76f4b27b0 | ||
|
|
a1ea2a5aa6 | ||
|
|
8608e9cd3a | ||
|
|
5f8f7ee576 | ||
|
|
d8a92a2e65 | ||
|
|
67ed930100 | ||
|
|
020c52e583 | ||
|
|
03dd318265 | ||
|
|
fc3bfda3bd | ||
|
|
7eb0ce6e90 | ||
|
|
5aa964cde9 | ||
|
|
31d7264583 | ||
|
|
93dc22a71f | ||
|
|
c4d228ad8d | ||
|
|
30faf500eb | ||
|
|
26d4bac85f | ||
|
|
e0e3cdc316 | ||
|
|
1deb52f86d | ||
|
|
39cb87dc4b | ||
|
|
04e2549239 | ||
|
|
e06cdbf0ac | ||
|
|
d366b7e4fc | ||
|
|
5aae3333bc | ||
|
|
6d40ac8644 | ||
|
|
d7c428fc8d | ||
|
|
28f8f56fa3 | ||
|
|
b1f6b5314c | ||
|
|
fdb1e8c2e4 | ||
|
|
5b02e86334 | ||
|
|
10627e4974 | ||
|
|
1b89e7c2ec | ||
|
|
bcf0afb25e | ||
|
|
ab22c8ca1c | ||
|
|
49513cb2ee | ||
|
|
1b5e66f9c2 | ||
|
|
1f486fc9a5 | ||
|
|
4cdbdf2125 | ||
|
|
ec5ecabdec | ||
|
|
e391301348 | ||
|
|
49866b6cf4 | ||
|
|
09d326de3a | ||
|
|
eda8614754 | ||
|
|
06c1911a28 | ||
|
|
8cac773e34 | ||
|
|
365ec1fd5e | ||
|
|
40e58ed12d | ||
|
|
0c98dc54fe | ||
|
|
3b0f624302 | ||
|
|
56242423b2 | ||
|
|
8113711b7a | ||
|
|
85ec6343ff | ||
|
|
daf183e2cc | ||
|
|
c6819cbd6b | ||
|
|
01ed3c3326 | ||
|
|
76b68f7ccb | ||
|
|
2bea808251 | ||
|
|
52e051bb02 | ||
|
|
74fdf649d3 | ||
|
|
2402f463e1 | ||
|
|
424d33faa0 | ||
|
|
b64c57fa36 | ||
|
|
6966b3810d | ||
|
|
02cb4764c2 | ||
|
|
89071f7226 | ||
|
|
d6eca62e56 | ||
|
|
b025e7f0f2 | ||
|
|
0f7ecad4e8 | ||
|
|
4a16aaacb6 | ||
|
|
ca08ce4262 | ||
|
|
21c758c3d3 | ||
|
|
c338f1a3fa | ||
|
|
242085d676 | ||
|
|
289d895423 | ||
|
|
eb22a84db4 | ||
|
|
d66dd99621 | ||
|
|
f2b248243c | ||
|
|
fcd358eb71 | ||
|
|
06d3739a8d | ||
|
|
bff1064dbd | ||
|
|
19678db933 | ||
|
|
3d8b682380 | ||
|
|
7b76502ee6 | ||
|
|
28687ad7c7 | ||
|
|
d420ff0e56 | ||
|
|
5a085f2514 | ||
|
|
e79ee7fb74 | ||
|
|
ed1adfd878 | ||
|
|
58a7b9ffa7 | ||
|
|
af5d046698 | ||
|
|
9e75b707ce | ||
|
|
8241b078ce | ||
|
|
c8f1e41197 | ||
|
|
cddf098f47 | ||
|
|
500cfbc19c | ||
|
|
133ae8555a | ||
|
|
f132010acb | ||
|
|
426305dc33 | ||
|
|
55282851c4 | ||
|
|
9e7d1b9ce7 | ||
|
|
e0da5f1703 | ||
|
|
4f5a70b804 | ||
|
|
42bbd1b44a | ||
|
|
fdeaf861e3 | ||
|
|
8e032a27c4 | ||
|
|
5bfd43b718 | ||
|
|
ce7f4cb9a8 | ||
|
|
99a4cf9e07 | ||
|
|
b82f6801cd | ||
|
|
184cb0b9a8 | ||
|
|
bf2e99efa6 | ||
|
|
7543d5220c | ||
|
|
ca73e1f024 | ||
|
|
718ff3a5f5 | ||
|
|
c2ccc1f980 | ||
|
|
f34d4bd6da | ||
|
|
8a4b436314 | ||
|
|
42aaf7c2a0 | ||
|
|
eb00852a71 | ||
|
|
b0535d8b40 | ||
|
|
8b4d93cc14 | ||
|
|
e2d0923122 | ||
|
|
2928d84339 | ||
|
|
b5e3883891 | ||
|
|
a2e52dadb9 | ||
|
|
ca4cb018d0 | ||
|
|
c2762d3b6f | ||
|
|
947e872850 | ||
|
|
3baa5e145f | ||
|
|
67fd35127c | ||
|
|
c421e2ddfc | ||
|
|
42febe97b5 | ||
|
|
74ca0fb763 | ||
|
|
13631ea2de | ||
|
|
a1eee5923a | ||
|
|
af92bbca2a | ||
|
|
c9287071e3 | ||
|
|
292026288a | ||
|
|
1f056998f3 | ||
|
|
4d7cb7de5f | ||
|
|
67855f21d4 | ||
|
|
8484565e95 | ||
|
|
f7d8abe8ea | ||
|
|
8ca90297e7 | ||
|
|
e08f9080c2 | ||
|
|
d01aefd1eb | ||
|
|
e1d447847f | ||
|
|
419738fbd5 | ||
|
|
7f1423dd6f | ||
|
|
9f09dcd18c | ||
|
|
91081ade3c | ||
|
|
4d933c23a8 | ||
|
|
f29bfd995d | ||
|
|
24a1b93842 | ||
|
|
bed1c311e3 | ||
|
|
1ce8d3ae9b | ||
|
|
676d76696b | ||
|
|
1d18432f8d | ||
|
|
277f13ff9d | ||
|
|
30d5d1aea9 | ||
|
|
7a0450a7f4 | ||
|
|
5bb09f469f | ||
|
|
90e9d8ff52 | ||
|
|
59a43ce5d1 | ||
|
|
5bc01aa251 | ||
|
|
9eeb979c7b | ||
|
|
eabd7592fe | ||
|
|
e089a3d8a1 | ||
|
|
7560375502 | ||
|
|
1f77b89266 | ||
|
|
7c610124d9 | ||
|
|
a0c5ef4e6f | ||
|
|
218934e767 | ||
|
|
c6a9825c0a | ||
|
|
2d365efe56 | ||
|
|
e81635f23d | ||
|
|
827315e059 | ||
|
|
e55a54f3d4 | ||
|
|
5e8b40faf6 | ||
|
|
45b9982172 | ||
|
|
9b267bb572 | ||
|
|
20c7bfaff2 | ||
|
|
dd29f970a2 | ||
|
|
b6a9a1a7ee | ||
|
|
1d87d9b370 | ||
|
|
40b6db6a27 | ||
|
|
9ba662a53e | ||
|
|
dc228c3dbb | ||
|
|
dd5dd422d1 | ||
|
|
73fdf4b957 | ||
|
|
a1de13657e | ||
|
|
93c19356d0 | ||
|
|
8a27c902ff | ||
|
|
be35b55444 | ||
|
|
a16ccf40c2 | ||
|
|
5c7b1cd5f2 | ||
|
|
fc5e593e2d | ||
|
|
f1dc79f0b5 | ||
|
|
34ea97dcea | ||
|
|
f2931163aa | ||
|
|
7e320d2748 | ||
|
|
b38d15adb3 | ||
|
|
29342e036f | ||
|
|
45660902ed | ||
|
|
72318cb687 | ||
|
|
f277223a65 | ||
|
|
262fa74f5e | ||
|
|
ebdd221c72 | ||
|
|
2b8d162c9e | ||
|
|
dc8cf0e02e | ||
|
|
a054f6a97b | ||
|
|
63d11c3eea | ||
|
|
dd2199c472 | ||
|
|
661c3bbdb0 | ||
|
|
5236e81068 | ||
|
|
c459b5eb9d | ||
|
|
384f0fb89a | ||
|
|
1d628bcf9e | ||
|
|
b8644f49ec | ||
|
|
091c1f7ef1 | ||
|
|
c838c3fa73 | ||
|
|
8bb9106b42 | ||
|
|
a7e3b0d184 | ||
|
|
449c542d5a | ||
|
|
7f19f8cf0e | ||
|
|
b2eb704394 | ||
|
|
899da0c6ea | ||
|
|
def41a7878 | ||
|
|
6dbe49089e | ||
|
|
5883e5159f | ||
|
|
26a5d48f63 | ||
|
|
aa6feb4b62 | ||
|
|
2fef0ebea8 | ||
|
|
66dd51a09a | ||
|
|
077868604d | ||
|
|
6c555cb666 | ||
|
|
0ae80272fb | ||
|
|
7bef7cf9e2 | ||
|
|
6fa24cad18 | ||
|
|
6313020ba9 | ||
|
|
b2eead386d | ||
|
|
b826123d98 | ||
|
|
cf537070d8 | ||
|
|
7a00a1dced | ||
|
|
80c37ebab2 | ||
|
|
5fb4136bcd | ||
|
|
6a249ca1dd | ||
|
|
5610d4782f | ||
|
|
fef4574c3c | ||
|
|
65239c7c25 | ||
|
|
1b123054b3 | ||
|
|
b29d17f522 | ||
|
|
04aefbf28d | ||
|
|
f8fcf02448 | ||
|
|
0e4fd5269b | ||
|
|
6328496bfb | ||
|
|
524b40b75f | ||
|
|
0241552c0d | ||
|
|
fd3f6687ce | ||
|
|
93d63749e6 | ||
|
|
41bf5450d9 | ||
|
|
d37553e7b8 | ||
|
|
101d7138c2 | ||
|
|
11de3aed51 | ||
|
|
4bb41cf886 | ||
|
|
1ae7dd9b11 | ||
|
|
a3250fac6d | ||
|
|
fabe6317ef | ||
|
|
23a4af9a52 | ||
|
|
9526dbadad | ||
|
|
6ad469c637 | ||
|
|
b30cdf0873 | ||
|
|
234bc93ddb | ||
|
|
42862852b8 | ||
|
|
7a6101c417 | ||
|
|
588123ed11 | ||
|
|
ac9852f9df | ||
|
|
d093476da5 | ||
|
|
9ad794f2cc | ||
|
|
b0f566a80d | ||
|
|
d3f3a70350 | ||
|
|
3f262fe872 | ||
|
|
150c708726 | ||
|
|
7512dbffbb | ||
|
|
3cfa882fe1 | ||
|
|
35f3b7088d | ||
|
|
07220a324d | ||
|
|
c7cecd5b4f | ||
|
|
ab1a2045d9 | ||
|
|
5e9a067e87 | ||
|
|
4cc460be64 | ||
|
|
112bfccfd5 | ||
|
|
b29cb08956 | ||
|
|
e42ad9be24 | ||
|
|
02da1700e0 | ||
|
|
a9f97e1fe2 | ||
|
|
d75077c6f9 | ||
|
|
b81939f02d | ||
|
|
0e1d90dd0c | ||
|
|
03e9c612b9 | ||
|
|
ee661e5d71 | ||
|
|
5ddffc9172 | ||
|
|
f981c782bb | ||
|
|
40dd085ef8 | ||
|
|
3fbdb7a007 | ||
|
|
d5b5bcef56 | ||
|
|
2997a15ba8 | ||
|
|
b4c02eca16 | ||
|
|
223dc87cef | ||
|
|
3ccdae0634 | ||
|
|
11af6f49c6 | ||
|
|
22dfedf6ae | ||
|
|
157d3f9dc9 | ||
|
|
f3b2ab5e60 | ||
|
|
fca53a0c2c | ||
|
|
6d302889fa | ||
|
|
a94ac63e74 | ||
|
|
19e73f8889 | ||
|
|
aacc26179e | ||
|
|
36f060dcf6 | ||
|
|
cb12c8dfd5 | ||
|
|
9cd1d1a9dc | ||
|
|
ebaf4c9c01 | ||
|
|
44240339d9 | ||
|
|
9110375173 | ||
|
|
c3557bbe3f | ||
|
|
cd924099e4 | ||
|
|
f2a311bb81 | ||
|
|
d7855e8fe5 | ||
|
|
1e52efa867 | ||
|
|
5d6f1bd2d7 | ||
|
|
48ccaa8b91 | ||
|
|
54ac0048c4 | ||
|
|
276e089419 | ||
|
|
3b8608761b | ||
|
|
413a91646c | ||
|
|
4299c6a802 | ||
|
|
4232923641 | ||
|
|
75b4bb306b | ||
|
|
4e0686f73c | ||
|
|
709f1e76d3 | ||
|
|
0c2d7b9c06 | ||
|
|
412e14a41c | ||
|
|
532b425dd9 | ||
|
|
2bb5fbdee5 | ||
|
|
38c41b72d6 | ||
|
|
30e89c3cdc | ||
|
|
b665014707 | ||
|
|
e98e7a232f | ||
|
|
5f7ad72d45 | ||
|
|
9c25365b12 | ||
|
|
0cc74b7cfe | ||
|
|
8a972ea213 | ||
|
|
2229bcc98b | ||
|
|
1bfd4672e1 | ||
|
|
15e9e8575a | ||
|
|
d7bafa6dfa | ||
|
|
35632f2109 | ||
|
|
08807b498e | ||
|
|
e8a453c567 | ||
|
|
717802611a | ||
|
|
b6a77e0231 | ||
|
|
21b966c8e6 | ||
|
|
64a1e88982 | ||
|
|
34c8b882c6 | ||
|
|
fa420d70cc | ||
|
|
2c67934191 | ||
|
|
821adcf178 | ||
|
|
8bf9482bc0 | ||
|
|
fe8ad3548b | ||
|
|
4f17bc0d86 | ||
|
|
1a43c81840 | ||
|
|
d3cf3f7a5c | ||
|
|
4f96a2a667 | ||
|
|
7b63ebfcaa | ||
|
|
724f3aa301 | ||
|
|
1ff326c89c | ||
|
|
2663f500cf | ||
|
|
43ed998ed6 | ||
|
|
a3032ab945 | ||
|
|
af9425ed8f | ||
|
|
d058ac6174 | ||
|
|
a4e7806d21 | ||
|
|
03906cc055 | ||
|
|
10833dcf39 | ||
|
|
a00323412b | ||
|
|
838a20ea95 | ||
|
|
e8ed8cbfcd | ||
|
|
5c78a5e4fe | ||
|
|
30ed4af38d | ||
|
|
6a9304dd1c | ||
|
|
1782eeb785 | ||
|
|
1aabb7d6de | ||
|
|
d4b8f9700b | ||
|
|
2ebecf1aa0 | ||
|
|
7031df4948 | ||
|
|
e0381dd757 | ||
|
|
9b2eae24d2 | ||
|
|
7362e8de4d | ||
|
|
87beb0a5f2 | ||
|
|
fc69cea4f7 | ||
|
|
ad4780a1ac | ||
|
|
72f487d2e3 | ||
|
|
0c10bd7dd7 | ||
|
|
b0418cb394 | ||
|
|
a30c81dadc | ||
|
|
40df6e87db | ||
|
|
0dff1dce8f | ||
|
|
ffd3b3e6b5 | ||
|
|
1305b0d8dd | ||
|
|
5fc41a3ea2 | ||
|
|
fc87fc7cab | ||
|
|
8887a0a6d3 | ||
|
|
ba7764aeaf | ||
|
|
28afe6f29f | ||
|
|
138ab6dbbd | ||
|
|
26c2fc21c8 | ||
|
|
fef74c3bca | ||
|
|
c390f1bfee | ||
|
|
f6698d4a84 | ||
|
|
4193196c8b | ||
|
|
da06dc3728 | ||
|
|
a0b8be5941 | ||
|
|
fe971680ea | ||
|
|
254eb8f304 | ||
|
|
c9d15901d1 | ||
|
|
59b3f5fb19 | ||
|
|
dbe9dd47ce | ||
|
|
8e35f8c3aa | ||
|
|
80970a0ac6 | ||
|
|
432f6ac4d7 | ||
|
|
eaf11009d1 | ||
|
|
df93fb773f | ||
|
|
f6887a4dac | ||
|
|
1c58c4c409 | ||
|
|
a972901438 | ||
|
|
25703296a6 | ||
|
|
7ebe97b931 | ||
|
|
76719d1bf5 | ||
|
|
cb113437f6 | ||
|
|
e6b3e42d61 | ||
|
|
c20b0169a9 | ||
|
|
89342bcb75 | ||
|
|
e036eea362 | ||
|
|
2044d633e9 | ||
|
|
8cb684e6bd | ||
|
|
997bd3392f | ||
|
|
ad44c87746 | ||
|
|
45ea2f82ba | ||
|
|
df6aa99ec2 | ||
|
|
1029dd3504 | ||
|
|
114eb6288d | ||
|
|
2ea37e6a0d | ||
|
|
ea2330b49f | ||
|
|
610bb2b85c | ||
|
|
fca6e9b932 | ||
|
|
fc7f86104e | ||
|
|
d057a9bb6d | ||
|
|
167aba6f26 | ||
|
|
9ecd84080b | ||
|
|
22d260f4e6 | ||
|
|
ea94477cd4 | ||
|
|
522c953860 | ||
|
|
f86ee84457 | ||
|
|
2d282597ca | ||
|
|
8635d89cc8 | ||
|
|
dc341ef9c1 | ||
|
|
beb31ab2fa | ||
|
|
e0214a2c2a | ||
|
|
5275daa66c | ||
|
|
fb27261568 | ||
|
|
5a20ce81e3 | ||
|
|
1454b85671 | ||
|
|
fc4dba4468 | ||
|
|
adff28dade | ||
|
|
c345ecae63 | ||
|
|
e6461380c6 | ||
|
|
deb2a3e415 | ||
|
|
c71dcd7611 | ||
|
|
c1e05664ed | ||
|
|
0f1e5f4fa4 | ||
|
|
8860915fb9 | ||
|
|
f0d486d1ff | ||
|
|
0fa20da990 | ||
|
|
b27767e8f4 | ||
|
|
0b2edd28df | ||
|
|
dd634382d7 | ||
|
|
c7fb155733 | ||
|
|
1eaa2cc619 | ||
|
|
92a47aaac5 | ||
|
|
fcffe8beb9 | ||
|
|
02fb40c507 | ||
|
|
dc6ea97877 | ||
|
|
031d53b04f | ||
|
|
3006c90fb8 | ||
|
|
9cec2688ed | ||
|
|
65c3dc21f4 | ||
|
|
cefa7d940a | ||
|
|
fab292d2de | ||
|
|
00bbe68f78 | ||
|
|
fa3591f4f2 | ||
|
|
10cfc6838d | ||
|
|
42e78f9a3e | ||
|
|
0122eabd44 | ||
|
|
435bb3f1d3 | ||
|
|
1d59d43286 | ||
|
|
fe30bf7d09 | ||
|
|
5f68ad4e19 | ||
|
|
3fc39aad33 | ||
|
|
a94653ba77 | ||
|
|
d8637b2c0f | ||
|
|
f1b6016157 | ||
|
|
de99d6d9fc | ||
|
|
bb8386ab85 | ||
|
|
ca6226359b | ||
|
|
adfafe5c54 | ||
|
|
a8d8fefceb | ||
|
|
3bb5943b20 | ||
|
|
57da04b5ec | ||
|
|
e711d168df | ||
|
|
833632eee3 | ||
|
|
abc76299c0 | ||
|
|
c96fcf319a | ||
|
|
5214a7c3ec | ||
|
|
1700f064b3 | ||
|
|
de902166a8 | ||
|
|
2f08bd1965 | ||
|
|
13c68cd799 | ||
|
|
c7d78f4594 | ||
|
|
2f1ca949f0 | ||
|
|
a6488ff9ac | ||
|
|
cb5eae888d | ||
|
|
b6f7710621 | ||
|
|
3e36f05a8c | ||
|
|
bcb7e5f2c8 | ||
|
|
3cefcd8204 | ||
|
|
4cd1871816 | ||
|
|
7aaa9583fa | ||
|
|
dd582c0306 | ||
|
|
5cded5b53e | ||
|
|
0a64567822 | ||
|
|
7ca861805d | ||
|
|
6b6faa8129 | ||
|
|
f84577bcda | ||
|
|
37f9fd3498 | ||
|
|
11aef82993 | ||
|
|
9861e2d724 | ||
|
|
7ae0d0caa3 | ||
|
|
2f5ea120cb | ||
|
|
93d2c9a3f0 | ||
|
|
60569fdd83 | ||
|
|
0f61e9c15e | ||
|
|
d81fc155cb | ||
|
|
60f7750d77 | ||
|
|
9bdfd8f4fe | ||
|
|
945b7de76f | ||
|
|
3c35eadbc4 | ||
|
|
ab86e056a2 | ||
|
|
a2bc79ddd5 | ||
|
|
51151293d7 | ||
|
|
5789e80d74 | ||
|
|
2beb2f5659 | ||
|
|
65868ab8a7 | ||
|
|
89586530a5 | ||
|
|
0d93145834 | ||
|
|
617f4acfd6 | ||
|
|
fe4113d623 | ||
|
|
9a0e15cced | ||
|
|
e0c63d58b2 | ||
|
|
f7f8ea9b97 | ||
|
|
58cad98cd8 | ||
|
|
2f8fb360aa | ||
|
|
05ec3922f1 | ||
|
|
1c9423ef31 | ||
|
|
74ffbb2172 | ||
|
|
cc9c85cc1a | ||
|
|
3f40380c69 | ||
|
|
1116b73a08 | ||
|
|
a8f252e45d | ||
|
|
b821836dc4 | ||
|
|
a42fef0905 | ||
|
|
f7e09af5c9 | ||
|
|
304cb56337 | ||
|
|
6b7b5caf54 | ||
|
|
fd461fe015 | ||
|
|
2797d2c535 | ||
|
|
f440656572 | ||
|
|
b1d019146a | ||
|
|
1dbc58d4e0 | ||
|
|
47ccb28482 | ||
|
|
744dea00ca | ||
|
|
373c2b379c | ||
|
|
95b51cf9e6 | ||
|
|
a375e924b4 | ||
|
|
957bbab440 | ||
|
|
bd48c99383 | ||
|
|
d989617825 | ||
|
|
177d9b7cb0 | ||
|
|
7a1305c1bb | ||
|
|
52d1d421a3 | ||
|
|
c3cc13595d | ||
|
|
adc8031e34 | ||
|
|
0974c74a89 | ||
|
|
c64aae6f39 | ||
|
|
b7b1714637 | ||
|
|
d8525493a1 | ||
|
|
30d0945855 | ||
|
|
ffde1f8343 | ||
|
|
96c35b41ed | ||
|
|
dc2979926f | ||
|
|
ff357dd3fb | ||
|
|
f35e15204d | ||
|
|
3ae4ba3300 | ||
|
|
8bb29f53d1 | ||
|
|
2e3c1ef4ac | ||
|
|
ada7e12b5a | ||
|
|
b8949ba3dd | ||
|
|
73fe47ba79 | ||
|
|
577920de86 | ||
|
|
0d8a314bcf | ||
|
|
e7cfde1904 | ||
|
|
517baa3235 | ||
|
|
aa41df4e7d | ||
|
|
d73953af3d | ||
|
|
54eec82311 | ||
|
|
dab244ad25 | ||
|
|
dbe7cb8dbb | ||
|
|
e814cccc44 | ||
|
|
1078fdc157 | ||
|
|
4bf4259dda | ||
|
|
03b53cbb60 | ||
|
|
7ef1340e2a | ||
|
|
c0b87adee5 | ||
|
|
4a8c2251e0 | ||
|
|
d1df5f3021 | ||
|
|
e1acea52f8 | ||
|
|
2e9f159225 | ||
|
|
2fcda9a73a | ||
|
|
78ba205f4d | ||
|
|
f44dec2c8d | ||
|
|
8c07af6fc7 | ||
|
|
cd01104de9 | ||
|
|
28f438a6bd | ||
|
|
9ff89b570f | ||
|
|
bc90376489 | ||
|
|
43b5ea801f | ||
|
|
9863e7ea6e | ||
|
|
bfba44fbad | ||
|
|
c466f063c8 | ||
|
|
ebaa39b03f | ||
|
|
fa3d7ad14b | ||
|
|
c51104f956 | ||
|
|
84795ff4d9 | ||
|
|
cc5c722e29 | ||
|
|
4fcddd1893 | ||
|
|
295af01687 | ||
|
|
c8604255e4 | ||
|
|
492826a7f2 | ||
|
|
e7284df1df | ||
|
|
3b46060caa | ||
|
|
696182cfa4 | ||
|
|
d1a1d1da8f | ||
|
|
c508984f56 | ||
|
|
54eba51b35 | ||
|
|
5e64781d65 | ||
|
|
2d4b900e33 | ||
|
|
d2481f5790 | ||
|
|
49f6104f03 | ||
|
|
2728d2aa6e | ||
|
|
0588fc6b7c | ||
|
|
e3cd52cab4 | ||
|
|
b2c6b9a320 | ||
|
|
39fa40ab12 | ||
|
|
0bf87bf4af | ||
|
|
92d37f6eaf | ||
|
|
d3b022fe17 | ||
|
|
79640f6b7d | ||
|
|
2cbdf274b1 | ||
|
|
6af5293315 | ||
|
|
bd3a2b1bb5 | ||
|
|
ff090d2f74 | ||
|
|
68e3a12a91 | ||
|
|
50eda6b678 | ||
|
|
192ec598a3 | ||
|
|
5b449999a5 | ||
|
|
afed62f6de | ||
|
|
59d1e16f9c | ||
|
|
dbe0d477d6 | ||
|
|
7248560169 | ||
|
|
f840f7d75b | ||
|
|
6e14a073ff | ||
|
|
cf3839ecec | ||
|
|
b0ca4435fd | ||
|
|
aa8cf76fb1 | ||
|
|
10b4bb598a | ||
|
|
de14d59bb3 | ||
|
|
d76272f0ea | ||
|
|
e04093efe2 | ||
|
|
bd6bbba948 | ||
|
|
37e4f35c93 | ||
|
|
0084cb7403 | ||
|
|
99e5c159a7 | ||
|
|
802121d54a | ||
|
|
160b2e95c9 | ||
|
|
7ec692cdef | ||
|
|
3dca67112d | ||
|
|
f8dac5905c | ||
|
|
48e4e41e05 | ||
|
|
22374b81de | ||
|
|
b9157e29cb | ||
|
|
bd247c35f2 | ||
|
|
7da9a45c61 | ||
|
|
122dfa12ac | ||
|
|
1905830b20 | ||
|
|
bfb41ce123 | ||
|
|
85e3ecfe0b | ||
|
|
9e073c954d | ||
|
|
b79c3f5cc4 | ||
|
|
ad5acb80fe | ||
|
|
7b7c834b08 | ||
|
|
42827be7c3 | ||
|
|
7022d27b8e | ||
|
|
ab911f1ce9 | ||
|
|
a6a1de50c8 | ||
|
|
97723fbbc9 | ||
|
|
2e58cf1168 | ||
|
|
3ca97d7258 | ||
|
|
9786dccdee | ||
|
|
a3612f53dd | ||
|
|
112257c49e | ||
|
|
d8e2b96bce | ||
|
|
6992659ba9 | ||
|
|
05696d443a | ||
|
|
f59a925897 | ||
|
|
afacdfcb95 | ||
|
|
8cb1b6b5d5 | ||
|
|
f7d9d53ad2 | ||
|
|
f4315e2c6f | ||
|
|
f0ac566c93 | ||
|
|
50f6a459cf | ||
|
|
179c80ae6d | ||
|
|
6e72f161a6 | ||
|
|
f71d8d7348 | ||
|
|
a12a3640a7 | ||
|
|
3b7bc5a56a | ||
|
|
e09d45c844 | ||
|
|
36fc321096 | ||
|
|
98a7a01dbb | ||
|
|
0f7be90500 | ||
|
|
7aaf4432d4 | ||
|
|
884a8995b4 | ||
|
|
bb42595275 | ||
|
|
a4bd89c938 | ||
|
|
f364d4fbef | ||
|
|
f899d0d8ed | ||
|
|
074cf00a7c | ||
|
|
15d10eeebc | ||
|
|
bea71f3411 | ||
|
|
eb99803b53 | ||
|
|
a60d0c4108 | ||
|
|
1cc3a13c49 | ||
|
|
feffbba6de | ||
|
|
6ea09444ec | ||
|
|
f1f486dacf | ||
|
|
fec4af3194 | ||
|
|
5342c7c82b | ||
|
|
8454ffa331 | ||
|
|
199977be6a | ||
|
|
00dbc3881f | ||
|
|
d93a5b2d20 | ||
|
|
8ca5ca6594 | ||
|
|
fe890c62f4 | ||
|
|
ed72b090af | ||
|
|
e15513bfdd | ||
|
|
dbe569c0d9 | ||
|
|
f2e1b589b5 | ||
|
|
377a37e4c9 | ||
|
|
6e163208b4 | ||
|
|
87a7bde618 | ||
|
|
37b0498699 | ||
|
|
59f976dc48 | ||
|
|
8565a853a8 | ||
|
|
dfd49e46ad | ||
|
|
73bbaced62 | ||
|
|
0c8870cb7f | ||
|
|
1a90f66f73 | ||
|
|
558e706bde | ||
|
|
1a08be0a3f | ||
|
|
29b2960805 | ||
|
|
f7f3a0bf0d | ||
|
|
ae4c186f55 | ||
|
|
af534a73fc | ||
|
|
772bbdc862 | ||
|
|
86521ec443 | ||
|
|
e3c4c9265d | ||
|
|
b3f8612e61 | ||
|
|
27b1dd04c4 | ||
|
|
46a876445f | ||
|
|
9bb58e47a7 | ||
|
|
b8447fcab8 | ||
|
|
3abcfd8fa9 | ||
|
|
f4ff2d5d2e | ||
|
|
09b41aa667 | ||
|
|
87dc4fe388 | ||
|
|
eed8a7f078 | ||
|
|
c18364c755 | ||
|
|
04946e992e | ||
|
|
5533782152 | ||
|
|
3f42487f0a | ||
|
|
8bdcd22486 | ||
|
|
b32d22731b | ||
|
|
b788cc24d1 | ||
|
|
94948f6d34 | ||
|
|
e5b76ed4c4 | ||
|
|
29a2920a2c | ||
|
|
089d35708b | ||
|
|
9b0b5bce9f | ||
|
|
ef20a0128f | ||
|
|
3039e4eb6d | ||
|
|
9143cd1485 | ||
|
|
13d31ecb7f | ||
|
|
a936b2f1f6 | ||
|
|
8d00f489cd | ||
|
|
b793dbf977 | ||
|
|
d52b38777a | ||
|
|
56cf93dff2 | ||
|
|
67d84cadad | ||
|
|
b384a24c0e | ||
|
|
66a68edbe6 | ||
|
|
dcf3d7234e | ||
|
|
0da839cce3 | ||
|
|
b6f62ac446 | ||
|
|
15ee036db1 | ||
|
|
6db8ae451a | ||
|
|
a7f3d413ef | ||
|
|
3b7be478aa | ||
|
|
7f39cdc856 | ||
|
|
80006f4730 | ||
|
|
a51025fe8f | ||
|
|
c42ed9c693 | ||
|
|
c48c8d07de | ||
|
|
4d4b6edbc2 | ||
|
|
2014ca9feb | ||
|
|
a9f631f404 | ||
|
|
ba468bb5e4 | ||
|
|
cf86d57a9f | ||
|
|
9e958f4e32 | ||
|
|
c7f6f20c9d | ||
|
|
1984f44ffe | ||
|
|
02de281e40 | ||
|
|
ae035deb92 | ||
|
|
edd1b60c3d | ||
|
|
55a3709bd1 | ||
|
|
d1fc01a407 | ||
|
|
65293f81d9 | ||
|
|
0afb0f7958 | ||
|
|
dd958872a8 | ||
|
|
62a2ce1d35 | ||
|
|
b556908cab | ||
|
|
e69a19db5c | ||
|
|
d86414febb | ||
|
|
832318fab1 | ||
|
|
80ad62ff56 | ||
|
|
ee50f254df | ||
|
|
cc0be6cd90 | ||
|
|
a7455d7edd | ||
|
|
b7c370fff7 | ||
|
|
8fd3a64e35 | ||
|
|
3761fb4377 | ||
|
|
0472f5da6a | ||
|
|
09041fb81d | ||
|
|
299a157409 | ||
|
|
53ede7b0d8 | ||
|
|
cd13aee3e7 | ||
|
|
8b3acb719e | ||
|
|
2961a90e7f | ||
|
|
db8a2d0c65 | ||
|
|
a6b5f0c9d4 | ||
|
|
8d136c6a25 | ||
|
|
4d94270cde | ||
|
|
e0d96bcb39 | ||
|
|
0d7b831661 | ||
|
|
0c9e4f67a8 | ||
|
|
da2c386b60 | ||
|
|
4770364d42 | ||
|
|
db3f131dfc | ||
|
|
d7f58c64f8 | ||
|
|
41b6aebe7c | ||
|
|
7d50332246 | ||
|
|
0d0478245f | ||
|
|
f680ede980 | ||
|
|
d108072bfb | ||
|
|
8cc7c5349a | ||
|
|
fb33ea2a0b | ||
|
|
63fca33b04 | ||
|
|
0548ad2fc6 | ||
|
|
623d615cd7 | ||
|
|
126df9647b | ||
|
|
2e2e7cd054 | ||
|
|
0ebc9f7a44 | ||
|
|
a8fba65cbd | ||
|
|
0b0476e196 | ||
|
|
f99d6dac08 | ||
|
|
cd2b7cd943 | ||
|
|
af759f2330 | ||
|
|
bb5f3cc326 | ||
|
|
7623025b90 | ||
|
|
56c98e9295 | ||
|
|
df753e2619 | ||
|
|
cbb7082afd | ||
|
|
6ee72e119c | ||
|
|
778ee02803 | ||
|
|
b3529dc748 | ||
|
|
13c7182948 | ||
|
|
edc76795d4 | ||
|
|
ed56d52af3 | ||
|
|
fb457968ec | ||
|
|
0bea2e2b94 | ||
|
|
72d02f442e | ||
|
|
bcf63b5d27 | ||
|
|
92dbe6cdf8 | ||
|
|
0781e8cf12 | ||
|
|
b71a088da7 | ||
|
|
d76fb566a2 | ||
|
|
24a40af103 | ||
|
|
4a60292f82 | ||
|
|
7eea866869 | ||
|
|
4ab5456a98 | ||
|
|
8881a9f40e |
155
.github/workflows/DNS.yml
vendored
155
.github/workflows/DNS.yml
vendored
@@ -1,5 +1,6 @@
|
|||||||
name: DNS
|
name: DNS
|
||||||
on:
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
push:
|
push:
|
||||||
paths:
|
paths:
|
||||||
- 'dnsapi/*.sh'
|
- 'dnsapi/*.sh'
|
||||||
@@ -65,7 +66,7 @@ jobs:
|
|||||||
TokenName4: ${{ secrets.TokenName4}}
|
TokenName4: ${{ secrets.TokenName4}}
|
||||||
TokenName5: ${{ secrets.TokenName5}}
|
TokenName5: ${{ secrets.TokenName5}}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Clone acmetest
|
- name: Clone acmetest
|
||||||
run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
|
run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
|
||||||
- name: Set env file
|
- name: Set env file
|
||||||
@@ -113,7 +114,7 @@ jobs:
|
|||||||
TokenName4: ${{ secrets.TokenName4}}
|
TokenName4: ${{ secrets.TokenName4}}
|
||||||
TokenName5: ${{ secrets.TokenName5}}
|
TokenName5: ${{ secrets.TokenName5}}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Install tools
|
- name: Install tools
|
||||||
run: brew install socat
|
run: brew install socat
|
||||||
- name: Clone acmetest
|
- name: Clone acmetest
|
||||||
@@ -164,7 +165,7 @@ jobs:
|
|||||||
- name: Set git to use LF
|
- name: Set git to use LF
|
||||||
run: |
|
run: |
|
||||||
git config --global core.autocrlf false
|
git config --global core.autocrlf false
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Install cygwin base packages with chocolatey
|
- name: Install cygwin base packages with chocolatey
|
||||||
run: |
|
run: |
|
||||||
choco config get cacheLocation
|
choco config get cacheLocation
|
||||||
@@ -204,7 +205,7 @@ jobs:
|
|||||||
|
|
||||||
|
|
||||||
FreeBSD:
|
FreeBSD:
|
||||||
runs-on: macos-12
|
runs-on: ubuntu-latest
|
||||||
needs: Windows
|
needs: Windows
|
||||||
env:
|
env:
|
||||||
TEST_DNS : ${{ secrets.TEST_DNS }}
|
TEST_DNS : ${{ secrets.TEST_DNS }}
|
||||||
@@ -223,15 +224,15 @@ jobs:
|
|||||||
TokenName4: ${{ secrets.TokenName4}}
|
TokenName4: ${{ secrets.TokenName4}}
|
||||||
TokenName5: ${{ secrets.TokenName5}}
|
TokenName5: ${{ secrets.TokenName5}}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Clone acmetest
|
- name: Clone acmetest
|
||||||
run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
|
run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
|
||||||
- uses: vmactions/freebsd-vm@v0
|
- uses: vmactions/freebsd-vm@v1
|
||||||
with:
|
with:
|
||||||
envs: 'TEST_DNS TestingDomain TEST_DNS_NO_WILDCARD TEST_DNS_NO_SUBDOMAIN TEST_DNS_SLEEP CASE TEST_LOCAL DEBUG http_proxy https_proxy TokenName1 TokenName2 TokenName3 TokenName4 TokenName5 ${{ secrets.TokenName1}} ${{ secrets.TokenName2}} ${{ secrets.TokenName3}} ${{ secrets.TokenName4}} ${{ secrets.TokenName5}}'
|
envs: 'TEST_DNS TestingDomain TEST_DNS_NO_WILDCARD TEST_DNS_NO_SUBDOMAIN TEST_DNS_SLEEP CASE TEST_LOCAL DEBUG http_proxy https_proxy TokenName1 TokenName2 TokenName3 TokenName4 TokenName5 ${{ secrets.TokenName1}} ${{ secrets.TokenName2}} ${{ secrets.TokenName3}} ${{ secrets.TokenName4}} ${{ secrets.TokenName5}}'
|
||||||
prepare: pkg install -y socat curl
|
prepare: pkg install -y socat curl
|
||||||
usesh: true
|
usesh: true
|
||||||
copyback: false
|
sync: nfs
|
||||||
run: |
|
run: |
|
||||||
if [ "${{ secrets.TokenName1}}" ] ; then
|
if [ "${{ secrets.TokenName1}}" ] ; then
|
||||||
export ${{ secrets.TokenName1}}="${{ secrets.TokenValue1}}"
|
export ${{ secrets.TokenName1}}="${{ secrets.TokenValue1}}"
|
||||||
@@ -255,7 +256,7 @@ jobs:
|
|||||||
|
|
||||||
|
|
||||||
OpenBSD:
|
OpenBSD:
|
||||||
runs-on: macos-12
|
runs-on: ubuntu-latest
|
||||||
needs: FreeBSD
|
needs: FreeBSD
|
||||||
env:
|
env:
|
||||||
TEST_DNS : ${{ secrets.TEST_DNS }}
|
TEST_DNS : ${{ secrets.TEST_DNS }}
|
||||||
@@ -274,15 +275,15 @@ jobs:
|
|||||||
TokenName4: ${{ secrets.TokenName4}}
|
TokenName4: ${{ secrets.TokenName4}}
|
||||||
TokenName5: ${{ secrets.TokenName5}}
|
TokenName5: ${{ secrets.TokenName5}}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Clone acmetest
|
- name: Clone acmetest
|
||||||
run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
|
run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
|
||||||
- uses: vmactions/openbsd-vm@v0
|
- uses: vmactions/openbsd-vm@v1
|
||||||
with:
|
with:
|
||||||
envs: 'TEST_DNS TestingDomain TEST_DNS_NO_WILDCARD TEST_DNS_NO_SUBDOMAIN TEST_DNS_SLEEP CASE TEST_LOCAL DEBUG http_proxy https_proxy TokenName1 TokenName2 TokenName3 TokenName4 TokenName5 ${{ secrets.TokenName1}} ${{ secrets.TokenName2}} ${{ secrets.TokenName3}} ${{ secrets.TokenName4}} ${{ secrets.TokenName5}}'
|
envs: 'TEST_DNS TestingDomain TEST_DNS_NO_WILDCARD TEST_DNS_NO_SUBDOMAIN TEST_DNS_SLEEP CASE TEST_LOCAL DEBUG http_proxy https_proxy TokenName1 TokenName2 TokenName3 TokenName4 TokenName5 ${{ secrets.TokenName1}} ${{ secrets.TokenName2}} ${{ secrets.TokenName3}} ${{ secrets.TokenName4}} ${{ secrets.TokenName5}}'
|
||||||
prepare: pkg_add socat curl
|
prepare: pkg_add socat curl libiconv
|
||||||
usesh: true
|
usesh: true
|
||||||
copyback: false
|
sync: nfs
|
||||||
run: |
|
run: |
|
||||||
if [ "${{ secrets.TokenName1}}" ] ; then
|
if [ "${{ secrets.TokenName1}}" ] ; then
|
||||||
export ${{ secrets.TokenName1}}="${{ secrets.TokenValue1}}"
|
export ${{ secrets.TokenName1}}="${{ secrets.TokenValue1}}"
|
||||||
@@ -306,7 +307,7 @@ jobs:
|
|||||||
|
|
||||||
|
|
||||||
NetBSD:
|
NetBSD:
|
||||||
runs-on: macos-12
|
runs-on: ubuntu-latest
|
||||||
needs: OpenBSD
|
needs: OpenBSD
|
||||||
env:
|
env:
|
||||||
TEST_DNS : ${{ secrets.TEST_DNS }}
|
TEST_DNS : ${{ secrets.TEST_DNS }}
|
||||||
@@ -325,16 +326,16 @@ jobs:
|
|||||||
TokenName4: ${{ secrets.TokenName4}}
|
TokenName4: ${{ secrets.TokenName4}}
|
||||||
TokenName5: ${{ secrets.TokenName5}}
|
TokenName5: ${{ secrets.TokenName5}}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Clone acmetest
|
- name: Clone acmetest
|
||||||
run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
|
run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
|
||||||
- uses: vmactions/netbsd-vm@v0
|
- uses: vmactions/netbsd-vm@v1
|
||||||
with:
|
with:
|
||||||
envs: 'TEST_DNS TestingDomain TEST_DNS_NO_WILDCARD TEST_DNS_NO_SUBDOMAIN TEST_DNS_SLEEP CASE TEST_LOCAL DEBUG http_proxy https_proxy TokenName1 TokenName2 TokenName3 TokenName4 TokenName5 ${{ secrets.TokenName1}} ${{ secrets.TokenName2}} ${{ secrets.TokenName3}} ${{ secrets.TokenName4}} ${{ secrets.TokenName5}}'
|
envs: 'TEST_DNS TestingDomain TEST_DNS_NO_WILDCARD TEST_DNS_NO_SUBDOMAIN TEST_DNS_SLEEP CASE TEST_LOCAL DEBUG http_proxy https_proxy TokenName1 TokenName2 TokenName3 TokenName4 TokenName5 ${{ secrets.TokenName1}} ${{ secrets.TokenName2}} ${{ secrets.TokenName3}} ${{ secrets.TokenName4}} ${{ secrets.TokenName5}}'
|
||||||
prepare: |
|
prepare: |
|
||||||
pkg_add curl socat
|
/usr/sbin/pkg_add curl socat
|
||||||
usesh: true
|
usesh: true
|
||||||
copyback: false
|
sync: nfs
|
||||||
run: |
|
run: |
|
||||||
if [ "${{ secrets.TokenName1}}" ] ; then
|
if [ "${{ secrets.TokenName1}}" ] ; then
|
||||||
export ${{ secrets.TokenName1}}="${{ secrets.TokenValue1}}"
|
export ${{ secrets.TokenName1}}="${{ secrets.TokenValue1}}"
|
||||||
@@ -358,7 +359,7 @@ jobs:
|
|||||||
|
|
||||||
|
|
||||||
DragonFlyBSD:
|
DragonFlyBSD:
|
||||||
runs-on: macos-12
|
runs-on: ubuntu-latest
|
||||||
needs: NetBSD
|
needs: NetBSD
|
||||||
env:
|
env:
|
||||||
TEST_DNS : ${{ secrets.TEST_DNS }}
|
TEST_DNS : ${{ secrets.TEST_DNS }}
|
||||||
@@ -377,16 +378,16 @@ jobs:
|
|||||||
TokenName4: ${{ secrets.TokenName4}}
|
TokenName4: ${{ secrets.TokenName4}}
|
||||||
TokenName5: ${{ secrets.TokenName5}}
|
TokenName5: ${{ secrets.TokenName5}}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Clone acmetest
|
- name: Clone acmetest
|
||||||
run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
|
run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
|
||||||
- uses: vmactions/dragonflybsd-vm@v0
|
- uses: vmactions/dragonflybsd-vm@v1
|
||||||
with:
|
with:
|
||||||
envs: 'TEST_DNS TestingDomain TEST_DNS_NO_WILDCARD TEST_DNS_NO_SUBDOMAIN TEST_DNS_SLEEP CASE TEST_LOCAL DEBUG http_proxy https_proxy TokenName1 TokenName2 TokenName3 TokenName4 TokenName5 ${{ secrets.TokenName1}} ${{ secrets.TokenName2}} ${{ secrets.TokenName3}} ${{ secrets.TokenName4}} ${{ secrets.TokenName5}}'
|
envs: 'TEST_DNS TestingDomain TEST_DNS_NO_WILDCARD TEST_DNS_NO_SUBDOMAIN TEST_DNS_SLEEP CASE TEST_LOCAL DEBUG http_proxy https_proxy TokenName1 TokenName2 TokenName3 TokenName4 TokenName5 ${{ secrets.TokenName1}} ${{ secrets.TokenName2}} ${{ secrets.TokenName3}} ${{ secrets.TokenName4}} ${{ secrets.TokenName5}}'
|
||||||
prepare: |
|
prepare: |
|
||||||
pkg install -y curl socat
|
pkg install -y curl socat libnghttp2
|
||||||
usesh: true
|
usesh: true
|
||||||
copyback: false
|
sync: nfs
|
||||||
run: |
|
run: |
|
||||||
if [ "${{ secrets.TokenName1}}" ] ; then
|
if [ "${{ secrets.TokenName1}}" ] ; then
|
||||||
export ${{ secrets.TokenName1}}="${{ secrets.TokenValue1}}"
|
export ${{ secrets.TokenName1}}="${{ secrets.TokenValue1}}"
|
||||||
@@ -413,7 +414,7 @@ jobs:
|
|||||||
|
|
||||||
|
|
||||||
Solaris:
|
Solaris:
|
||||||
runs-on: macos-12
|
runs-on: ubuntu-latest
|
||||||
needs: DragonFlyBSD
|
needs: DragonFlyBSD
|
||||||
env:
|
env:
|
||||||
TEST_DNS : ${{ secrets.TEST_DNS }}
|
TEST_DNS : ${{ secrets.TEST_DNS }}
|
||||||
@@ -433,14 +434,16 @@ jobs:
|
|||||||
TokenName4: ${{ secrets.TokenName4}}
|
TokenName4: ${{ secrets.TokenName4}}
|
||||||
TokenName5: ${{ secrets.TokenName5}}
|
TokenName5: ${{ secrets.TokenName5}}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Clone acmetest
|
- name: Clone acmetest
|
||||||
run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
|
run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
|
||||||
- uses: vmactions/solaris-vm@v0
|
- uses: vmactions/solaris-vm@v1
|
||||||
with:
|
with:
|
||||||
envs: 'TEST_DNS TestingDomain TEST_DNS_NO_WILDCARD TEST_DNS_NO_SUBDOMAIN TEST_DNS_SLEEP CASE TEST_LOCAL DEBUG http_proxy https_proxy HTTPS_INSECURE TokenName1 TokenName2 TokenName3 TokenName4 TokenName5 ${{ secrets.TokenName1}} ${{ secrets.TokenName2}} ${{ secrets.TokenName3}} ${{ secrets.TokenName4}} ${{ secrets.TokenName5}}'
|
envs: 'TEST_DNS TestingDomain TEST_DNS_NO_WILDCARD TEST_DNS_NO_SUBDOMAIN TEST_DNS_SLEEP CASE TEST_LOCAL DEBUG http_proxy https_proxy HTTPS_INSECURE TokenName1 TokenName2 TokenName3 TokenName4 TokenName5 ${{ secrets.TokenName1}} ${{ secrets.TokenName2}} ${{ secrets.TokenName3}} ${{ secrets.TokenName4}} ${{ secrets.TokenName5}}'
|
||||||
copyback: false
|
sync: nfs
|
||||||
prepare: pkgutil -y -i socat
|
prepare: |
|
||||||
|
pkgutil -U
|
||||||
|
pkgutil -y -i socat
|
||||||
run: |
|
run: |
|
||||||
pkg set-mediator -v -I default@1.1 openssl
|
pkg set-mediator -v -I default@1.1 openssl
|
||||||
export PATH=/usr/gnu/bin:$PATH
|
export PATH=/usr/gnu/bin:$PATH
|
||||||
@@ -463,3 +466,101 @@ jobs:
|
|||||||
./letest.sh
|
./letest.sh
|
||||||
|
|
||||||
|
|
||||||
|
Omnios:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: Solaris
|
||||||
|
env:
|
||||||
|
TEST_DNS : ${{ secrets.TEST_DNS }}
|
||||||
|
TestingDomain: ${{ secrets.TestingDomain }}
|
||||||
|
TEST_DNS_NO_WILDCARD: ${{ secrets.TEST_DNS_NO_WILDCARD }}
|
||||||
|
TEST_DNS_NO_SUBDOMAIN: ${{ secrets.TEST_DNS_NO_SUBDOMAIN }}
|
||||||
|
TEST_DNS_SLEEP: ${{ secrets.TEST_DNS_SLEEP }}
|
||||||
|
CASE: le_test_dnsapi
|
||||||
|
TEST_LOCAL: 1
|
||||||
|
DEBUG: ${{ secrets.DEBUG }}
|
||||||
|
http_proxy: ${{ secrets.http_proxy }}
|
||||||
|
https_proxy: ${{ secrets.https_proxy }}
|
||||||
|
HTTPS_INSECURE: 1 # always set to 1 to ignore https error, since Omnios doesn't accept the expired ISRG X1 root
|
||||||
|
TokenName1: ${{ secrets.TokenName1}}
|
||||||
|
TokenName2: ${{ secrets.TokenName2}}
|
||||||
|
TokenName3: ${{ secrets.TokenName3}}
|
||||||
|
TokenName4: ${{ secrets.TokenName4}}
|
||||||
|
TokenName5: ${{ secrets.TokenName5}}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Clone acmetest
|
||||||
|
run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
|
||||||
|
- uses: vmactions/omnios-vm@v1
|
||||||
|
with:
|
||||||
|
envs: 'TEST_DNS TestingDomain TEST_DNS_NO_WILDCARD TEST_DNS_NO_SUBDOMAIN TEST_DNS_SLEEP CASE TEST_LOCAL DEBUG http_proxy https_proxy HTTPS_INSECURE TokenName1 TokenName2 TokenName3 TokenName4 TokenName5 ${{ secrets.TokenName1}} ${{ secrets.TokenName2}} ${{ secrets.TokenName3}} ${{ secrets.TokenName4}} ${{ secrets.TokenName5}}'
|
||||||
|
sync: nfs
|
||||||
|
prepare: pkg install socat
|
||||||
|
run: |
|
||||||
|
if [ "${{ secrets.TokenName1}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName1}}="${{ secrets.TokenValue1}}"
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName2}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName2}}="${{ secrets.TokenValue2}}"
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName3}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName3}}="${{ secrets.TokenValue3}}"
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName4}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName4}}="${{ secrets.TokenValue4}}"
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName5}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName5}}="${{ secrets.TokenValue5}}"
|
||||||
|
fi
|
||||||
|
cd ../acmetest
|
||||||
|
./letest.sh
|
||||||
|
|
||||||
|
|
||||||
|
OpenIndiana:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: Omnios
|
||||||
|
env:
|
||||||
|
TEST_DNS : ${{ secrets.TEST_DNS }}
|
||||||
|
TestingDomain: ${{ secrets.TestingDomain }}
|
||||||
|
TEST_DNS_NO_WILDCARD: ${{ secrets.TEST_DNS_NO_WILDCARD }}
|
||||||
|
TEST_DNS_NO_SUBDOMAIN: ${{ secrets.TEST_DNS_NO_SUBDOMAIN }}
|
||||||
|
TEST_DNS_SLEEP: ${{ secrets.TEST_DNS_SLEEP }}
|
||||||
|
CASE: le_test_dnsapi
|
||||||
|
TEST_LOCAL: 1
|
||||||
|
DEBUG: ${{ secrets.DEBUG }}
|
||||||
|
http_proxy: ${{ secrets.http_proxy }}
|
||||||
|
https_proxy: ${{ secrets.https_proxy }}
|
||||||
|
HTTPS_INSECURE: 1 # always set to 1 to ignore https error, since OpenIndiana doesn't accept the expired ISRG X1 root
|
||||||
|
TokenName1: ${{ secrets.TokenName1}}
|
||||||
|
TokenName2: ${{ secrets.TokenName2}}
|
||||||
|
TokenName3: ${{ secrets.TokenName3}}
|
||||||
|
TokenName4: ${{ secrets.TokenName4}}
|
||||||
|
TokenName5: ${{ secrets.TokenName5}}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Clone acmetest
|
||||||
|
run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
|
||||||
|
- uses: vmactions/openindiana-vm@v0
|
||||||
|
with:
|
||||||
|
envs: 'TEST_DNS TestingDomain TEST_DNS_NO_WILDCARD TEST_DNS_NO_SUBDOMAIN TEST_DNS_SLEEP CASE TEST_LOCAL DEBUG http_proxy https_proxy HTTPS_INSECURE TokenName1 TokenName2 TokenName3 TokenName4 TokenName5 ${{ secrets.TokenName1}} ${{ secrets.TokenName2}} ${{ secrets.TokenName3}} ${{ secrets.TokenName4}} ${{ secrets.TokenName5}}'
|
||||||
|
sync: nfs
|
||||||
|
prepare: pkg install socat
|
||||||
|
run: |
|
||||||
|
if [ "${{ secrets.TokenName1}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName1}}="${{ secrets.TokenValue1}}"
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName2}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName2}}="${{ secrets.TokenValue2}}"
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName3}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName3}}="${{ secrets.TokenValue3}}"
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName4}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName4}}="${{ secrets.TokenValue4}}"
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName5}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName5}}="${{ secrets.TokenValue5}}"
|
||||||
|
fi
|
||||||
|
cd ../acmetest
|
||||||
|
./letest.sh
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
142
.github/workflows/DragonFlyBSD.yml
vendored
142
.github/workflows/DragonFlyBSD.yml
vendored
@@ -1,71 +1,71 @@
|
|||||||
name: DragonFlyBSD
|
name: DragonFlyBSD
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- '*'
|
- '*'
|
||||||
paths:
|
paths:
|
||||||
- '*.sh'
|
- '*.sh'
|
||||||
- '.github/workflows/DragonFlyBSD.yml'
|
- '.github/workflows/DragonFlyBSD.yml'
|
||||||
|
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- dev
|
- dev
|
||||||
paths:
|
paths:
|
||||||
- '*.sh'
|
- '*.sh'
|
||||||
- '.github/workflows/DragonFlyBSD.yml'
|
- '.github/workflows/DragonFlyBSD.yml'
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.workflow }}-${{ github.ref }}
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
jobs:
|
||||||
jobs:
|
DragonFlyBSD:
|
||||||
DragonFlyBSD:
|
strategy:
|
||||||
strategy:
|
matrix:
|
||||||
matrix:
|
include:
|
||||||
include:
|
- TEST_ACME_Server: "LetsEncrypt.org_test"
|
||||||
- TEST_ACME_Server: "LetsEncrypt.org_test"
|
CA_ECDSA: ""
|
||||||
CA_ECDSA: ""
|
CA: ""
|
||||||
CA: ""
|
CA_EMAIL: ""
|
||||||
CA_EMAIL: ""
|
TEST_PREFERRED_CHAIN: (STAGING)
|
||||||
TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
|
#- TEST_ACME_Server: "ZeroSSL.com"
|
||||||
#- TEST_ACME_Server: "ZeroSSL.com"
|
# CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
|
||||||
# CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
|
# CA: "ZeroSSL RSA Domain Secure Site CA"
|
||||||
# CA: "ZeroSSL RSA Domain Secure Site CA"
|
# CA_EMAIL: "githubtest@acme.sh"
|
||||||
# CA_EMAIL: "githubtest@acme.sh"
|
# TEST_PREFERRED_CHAIN: ""
|
||||||
# TEST_PREFERRED_CHAIN: ""
|
runs-on: ubuntu-latest
|
||||||
runs-on: macos-12
|
env:
|
||||||
env:
|
TEST_LOCAL: 1
|
||||||
TEST_LOCAL: 1
|
TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
|
||||||
TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
|
CA_ECDSA: ${{ matrix.CA_ECDSA }}
|
||||||
CA_ECDSA: ${{ matrix.CA_ECDSA }}
|
CA: ${{ matrix.CA }}
|
||||||
CA: ${{ matrix.CA }}
|
CA_EMAIL: ${{ matrix.CA_EMAIL }}
|
||||||
CA_EMAIL: ${{ matrix.CA_EMAIL }}
|
TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
|
||||||
TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
|
ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: vmactions/cf-tunnel@v0
|
- uses: vmactions/cf-tunnel@v0
|
||||||
id: tunnel
|
id: tunnel
|
||||||
with:
|
with:
|
||||||
protocol: http
|
protocol: http
|
||||||
port: 8080
|
port: 8080
|
||||||
- name: Set envs
|
- name: Set envs
|
||||||
run: echo "TestingDomain=${{steps.tunnel.outputs.server}}" >> $GITHUB_ENV
|
run: echo "TestingDomain=${{steps.tunnel.outputs.server}}" >> $GITHUB_ENV
|
||||||
- name: Clone acmetest
|
- name: Clone acmetest
|
||||||
run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
|
run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
|
||||||
- uses: vmactions/dragonflybsd-vm@v0
|
- uses: vmactions/dragonflybsd-vm@v1
|
||||||
with:
|
with:
|
||||||
envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN'
|
envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN ACME_USE_WGET'
|
||||||
copyback: "false"
|
nat: |
|
||||||
nat: |
|
"8080": "80"
|
||||||
"8080": "80"
|
prepare: |
|
||||||
prepare: |
|
pkg install -y curl socat libnghttp2
|
||||||
pkg install -y curl socat
|
usesh: true
|
||||||
usesh: true
|
sync: nfs
|
||||||
run: |
|
run: |
|
||||||
cd ../acmetest \
|
cd ../acmetest \
|
||||||
&& ./letest.sh
|
&& ./letest.sh
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
12
.github/workflows/FreeBSD.yml
vendored
12
.github/workflows/FreeBSD.yml
vendored
@@ -29,19 +29,19 @@ jobs:
|
|||||||
CA_ECDSA: ""
|
CA_ECDSA: ""
|
||||||
CA: ""
|
CA: ""
|
||||||
CA_EMAIL: ""
|
CA_EMAIL: ""
|
||||||
TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
|
TEST_PREFERRED_CHAIN: (STAGING)
|
||||||
- TEST_ACME_Server: "LetsEncrypt.org_test"
|
- TEST_ACME_Server: "LetsEncrypt.org_test"
|
||||||
CA_ECDSA: ""
|
CA_ECDSA: ""
|
||||||
CA: ""
|
CA: ""
|
||||||
CA_EMAIL: ""
|
CA_EMAIL: ""
|
||||||
TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
|
TEST_PREFERRED_CHAIN: (STAGING)
|
||||||
ACME_USE_WGET: 1
|
ACME_USE_WGET: 1
|
||||||
#- TEST_ACME_Server: "ZeroSSL.com"
|
#- TEST_ACME_Server: "ZeroSSL.com"
|
||||||
# CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
|
# CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
|
||||||
# CA: "ZeroSSL RSA Domain Secure Site CA"
|
# CA: "ZeroSSL RSA Domain Secure Site CA"
|
||||||
# CA_EMAIL: "githubtest@acme.sh"
|
# CA_EMAIL: "githubtest@acme.sh"
|
||||||
# TEST_PREFERRED_CHAIN: ""
|
# TEST_PREFERRED_CHAIN: ""
|
||||||
runs-on: macos-12
|
runs-on: ubuntu-latest
|
||||||
env:
|
env:
|
||||||
TEST_LOCAL: 1
|
TEST_LOCAL: 1
|
||||||
TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
|
TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
|
||||||
@@ -51,7 +51,7 @@ jobs:
|
|||||||
TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
|
TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
|
||||||
ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
|
ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: vmactions/cf-tunnel@v0
|
- uses: vmactions/cf-tunnel@v0
|
||||||
id: tunnel
|
id: tunnel
|
||||||
with:
|
with:
|
||||||
@@ -61,14 +61,14 @@ jobs:
|
|||||||
run: echo "TestingDomain=${{steps.tunnel.outputs.server}}" >> $GITHUB_ENV
|
run: echo "TestingDomain=${{steps.tunnel.outputs.server}}" >> $GITHUB_ENV
|
||||||
- name: Clone acmetest
|
- name: Clone acmetest
|
||||||
run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
|
run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
|
||||||
- uses: vmactions/freebsd-vm@v0
|
- uses: vmactions/freebsd-vm@v1
|
||||||
with:
|
with:
|
||||||
envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN ACME_USE_WGET'
|
envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN ACME_USE_WGET'
|
||||||
nat: |
|
nat: |
|
||||||
"8080": "80"
|
"8080": "80"
|
||||||
prepare: pkg install -y socat curl wget
|
prepare: pkg install -y socat curl wget
|
||||||
usesh: true
|
usesh: true
|
||||||
copyback: false
|
sync: nfs
|
||||||
run: |
|
run: |
|
||||||
cd ../acmetest \
|
cd ../acmetest \
|
||||||
&& ./letest.sh
|
&& ./letest.sh
|
||||||
|
|||||||
6
.github/workflows/Linux.yml
vendored
6
.github/workflows/Linux.yml
vendored
@@ -26,14 +26,14 @@ jobs:
|
|||||||
Linux:
|
Linux:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: ["ubuntu:latest", "debian:latest", "almalinux:latest", "fedora:latest", "centos:7", "opensuse/leap:latest", "alpine:latest", "oraclelinux:8", "kalilinux/kali", "archlinux:latest", "mageia", "gentoo/stage3"]
|
os: ["ubuntu:latest", "debian:latest", "almalinux:latest", "fedora:latest", "opensuse/leap:latest", "alpine:latest", "oraclelinux:8", "kalilinux/kali", "archlinux:latest", "gentoo/stage3"]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
env:
|
env:
|
||||||
TEST_LOCAL: 1
|
TEST_LOCAL: 1
|
||||||
TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
|
TEST_PREFERRED_CHAIN: (STAGING)
|
||||||
TEST_ACME_Server: "LetsEncrypt.org_test"
|
TEST_ACME_Server: "LetsEncrypt.org_test"
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Clone acmetest
|
- name: Clone acmetest
|
||||||
run: |
|
run: |
|
||||||
cd .. \
|
cd .. \
|
||||||
|
|||||||
4
.github/workflows/MacOS.yml
vendored
4
.github/workflows/MacOS.yml
vendored
@@ -29,7 +29,7 @@ jobs:
|
|||||||
CA_ECDSA: ""
|
CA_ECDSA: ""
|
||||||
CA: ""
|
CA: ""
|
||||||
CA_EMAIL: ""
|
CA_EMAIL: ""
|
||||||
TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
|
TEST_PREFERRED_CHAIN: (STAGING)
|
||||||
#- TEST_ACME_Server: "ZeroSSL.com"
|
#- TEST_ACME_Server: "ZeroSSL.com"
|
||||||
# CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
|
# CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
|
||||||
# CA: "ZeroSSL RSA Domain Secure Site CA"
|
# CA: "ZeroSSL RSA Domain Secure Site CA"
|
||||||
@@ -44,7 +44,7 @@ jobs:
|
|||||||
CA_EMAIL: ${{ matrix.CA_EMAIL }}
|
CA_EMAIL: ${{ matrix.CA_EMAIL }}
|
||||||
TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
|
TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Install tools
|
- name: Install tools
|
||||||
run: brew install socat
|
run: brew install socat
|
||||||
- name: Clone acmetest
|
- name: Clone acmetest
|
||||||
|
|||||||
142
.github/workflows/NetBSD.yml
vendored
142
.github/workflows/NetBSD.yml
vendored
@@ -1,71 +1,71 @@
|
|||||||
name: NetBSD
|
name: NetBSD
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- '*'
|
- '*'
|
||||||
paths:
|
paths:
|
||||||
- '*.sh'
|
- '*.sh'
|
||||||
- '.github/workflows/NetBSD.yml'
|
- '.github/workflows/NetBSD.yml'
|
||||||
|
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- dev
|
- dev
|
||||||
paths:
|
paths:
|
||||||
- '*.sh'
|
- '*.sh'
|
||||||
- '.github/workflows/NetBSD.yml'
|
- '.github/workflows/NetBSD.yml'
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.workflow }}-${{ github.ref }}
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
jobs:
|
||||||
jobs:
|
NetBSD:
|
||||||
NetBSD:
|
strategy:
|
||||||
strategy:
|
matrix:
|
||||||
matrix:
|
include:
|
||||||
include:
|
- TEST_ACME_Server: "LetsEncrypt.org_test"
|
||||||
- TEST_ACME_Server: "LetsEncrypt.org_test"
|
CA_ECDSA: ""
|
||||||
CA_ECDSA: ""
|
CA: ""
|
||||||
CA: ""
|
CA_EMAIL: ""
|
||||||
CA_EMAIL: ""
|
TEST_PREFERRED_CHAIN: (STAGING)
|
||||||
TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
|
#- TEST_ACME_Server: "ZeroSSL.com"
|
||||||
#- TEST_ACME_Server: "ZeroSSL.com"
|
# CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
|
||||||
# CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
|
# CA: "ZeroSSL RSA Domain Secure Site CA"
|
||||||
# CA: "ZeroSSL RSA Domain Secure Site CA"
|
# CA_EMAIL: "githubtest@acme.sh"
|
||||||
# CA_EMAIL: "githubtest@acme.sh"
|
# TEST_PREFERRED_CHAIN: ""
|
||||||
# TEST_PREFERRED_CHAIN: ""
|
runs-on: ubuntu-latest
|
||||||
runs-on: macos-12
|
env:
|
||||||
env:
|
TEST_LOCAL: 1
|
||||||
TEST_LOCAL: 1
|
TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
|
||||||
TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
|
CA_ECDSA: ${{ matrix.CA_ECDSA }}
|
||||||
CA_ECDSA: ${{ matrix.CA_ECDSA }}
|
CA: ${{ matrix.CA }}
|
||||||
CA: ${{ matrix.CA }}
|
CA_EMAIL: ${{ matrix.CA_EMAIL }}
|
||||||
CA_EMAIL: ${{ matrix.CA_EMAIL }}
|
TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
|
||||||
TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
|
ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: vmactions/cf-tunnel@v0
|
- uses: vmactions/cf-tunnel@v0
|
||||||
id: tunnel
|
id: tunnel
|
||||||
with:
|
with:
|
||||||
protocol: http
|
protocol: http
|
||||||
port: 8080
|
port: 8080
|
||||||
- name: Set envs
|
- name: Set envs
|
||||||
run: echo "TestingDomain=${{steps.tunnel.outputs.server}}" >> $GITHUB_ENV
|
run: echo "TestingDomain=${{steps.tunnel.outputs.server}}" >> $GITHUB_ENV
|
||||||
- name: Clone acmetest
|
- name: Clone acmetest
|
||||||
run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
|
run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
|
||||||
- uses: vmactions/netbsd-vm@v0
|
- uses: vmactions/netbsd-vm@v1
|
||||||
with:
|
with:
|
||||||
envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN'
|
envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN ACME_USE_WGET'
|
||||||
nat: |
|
nat: |
|
||||||
"8080": "80"
|
"8080": "80"
|
||||||
prepare: |
|
prepare: |
|
||||||
pkg_add curl socat
|
/usr/sbin/pkg_add curl socat
|
||||||
usesh: true
|
usesh: true
|
||||||
copyback: false
|
sync: nfs
|
||||||
run: |
|
run: |
|
||||||
cd ../acmetest \
|
cd ../acmetest \
|
||||||
&& ./letest.sh
|
&& ./letest.sh
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
75
.github/workflows/Omnios.yml
vendored
Normal file
75
.github/workflows/Omnios.yml
vendored
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
name: Omnios
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- '*'
|
||||||
|
paths:
|
||||||
|
- '*.sh'
|
||||||
|
- '.github/workflows/Omnios.yml'
|
||||||
|
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- dev
|
||||||
|
paths:
|
||||||
|
- '*.sh'
|
||||||
|
- '.github/workflows/Omnios.yml'
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
Omnios:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- TEST_ACME_Server: "LetsEncrypt.org_test"
|
||||||
|
CA_ECDSA: ""
|
||||||
|
CA: ""
|
||||||
|
CA_EMAIL: ""
|
||||||
|
TEST_PREFERRED_CHAIN: (STAGING)
|
||||||
|
- TEST_ACME_Server: "LetsEncrypt.org_test"
|
||||||
|
CA_ECDSA: ""
|
||||||
|
CA: ""
|
||||||
|
CA_EMAIL: ""
|
||||||
|
TEST_PREFERRED_CHAIN: (STAGING)
|
||||||
|
ACME_USE_WGET: 1
|
||||||
|
#- TEST_ACME_Server: "ZeroSSL.com"
|
||||||
|
# CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
|
||||||
|
# CA: "ZeroSSL RSA Domain Secure Site CA"
|
||||||
|
# CA_EMAIL: "githubtest@acme.sh"
|
||||||
|
# TEST_PREFERRED_CHAIN: ""
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
TEST_LOCAL: 1
|
||||||
|
TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
|
||||||
|
CA_ECDSA: ${{ matrix.CA_ECDSA }}
|
||||||
|
CA: ${{ matrix.CA }}
|
||||||
|
CA_EMAIL: ${{ matrix.CA_EMAIL }}
|
||||||
|
TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
|
||||||
|
ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: vmactions/cf-tunnel@v0
|
||||||
|
id: tunnel
|
||||||
|
with:
|
||||||
|
protocol: http
|
||||||
|
port: 8080
|
||||||
|
- name: Set envs
|
||||||
|
run: echo "TestingDomain=${{steps.tunnel.outputs.server}}" >> $GITHUB_ENV
|
||||||
|
- name: Clone acmetest
|
||||||
|
run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
|
||||||
|
- uses: vmactions/omnios-vm@v1
|
||||||
|
with:
|
||||||
|
envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN ACME_USE_WGET'
|
||||||
|
nat: |
|
||||||
|
"8080": "80"
|
||||||
|
prepare: pkg install socat wget
|
||||||
|
sync: nfs
|
||||||
|
run: |
|
||||||
|
cd ../acmetest \
|
||||||
|
&& ./letest.sh
|
||||||
|
|
||||||
|
|
||||||
14
.github/workflows/OpenBSD.yml
vendored
14
.github/workflows/OpenBSD.yml
vendored
@@ -29,19 +29,19 @@ jobs:
|
|||||||
CA_ECDSA: ""
|
CA_ECDSA: ""
|
||||||
CA: ""
|
CA: ""
|
||||||
CA_EMAIL: ""
|
CA_EMAIL: ""
|
||||||
TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
|
TEST_PREFERRED_CHAIN: (STAGING)
|
||||||
- TEST_ACME_Server: "LetsEncrypt.org_test"
|
- TEST_ACME_Server: "LetsEncrypt.org_test"
|
||||||
CA_ECDSA: ""
|
CA_ECDSA: ""
|
||||||
CA: ""
|
CA: ""
|
||||||
CA_EMAIL: ""
|
CA_EMAIL: ""
|
||||||
TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
|
TEST_PREFERRED_CHAIN: (STAGING)
|
||||||
ACME_USE_WGET: 1
|
ACME_USE_WGET: 1
|
||||||
#- TEST_ACME_Server: "ZeroSSL.com"
|
#- TEST_ACME_Server: "ZeroSSL.com"
|
||||||
# CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
|
# CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
|
||||||
# CA: "ZeroSSL RSA Domain Secure Site CA"
|
# CA: "ZeroSSL RSA Domain Secure Site CA"
|
||||||
# CA_EMAIL: "githubtest@acme.sh"
|
# CA_EMAIL: "githubtest@acme.sh"
|
||||||
# TEST_PREFERRED_CHAIN: ""
|
# TEST_PREFERRED_CHAIN: ""
|
||||||
runs-on: macos-12
|
runs-on: ubuntu-latest
|
||||||
env:
|
env:
|
||||||
TEST_LOCAL: 1
|
TEST_LOCAL: 1
|
||||||
TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
|
TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
|
||||||
@@ -51,7 +51,7 @@ jobs:
|
|||||||
TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
|
TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
|
||||||
ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
|
ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: vmactions/cf-tunnel@v0
|
- uses: vmactions/cf-tunnel@v0
|
||||||
id: tunnel
|
id: tunnel
|
||||||
with:
|
with:
|
||||||
@@ -61,14 +61,14 @@ jobs:
|
|||||||
run: echo "TestingDomain=${{steps.tunnel.outputs.server}}" >> $GITHUB_ENV
|
run: echo "TestingDomain=${{steps.tunnel.outputs.server}}" >> $GITHUB_ENV
|
||||||
- name: Clone acmetest
|
- name: Clone acmetest
|
||||||
run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
|
run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
|
||||||
- uses: vmactions/openbsd-vm@v0
|
- uses: vmactions/openbsd-vm@v1
|
||||||
with:
|
with:
|
||||||
envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN ACME_USE_WGET'
|
envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN ACME_USE_WGET'
|
||||||
nat: |
|
nat: |
|
||||||
"8080": "80"
|
"8080": "80"
|
||||||
prepare: pkg_add socat curl wget
|
prepare: pkg_add socat curl wget libnghttp2
|
||||||
usesh: true
|
usesh: true
|
||||||
copyback: false
|
sync: nfs
|
||||||
run: |
|
run: |
|
||||||
cd ../acmetest \
|
cd ../acmetest \
|
||||||
&& ./letest.sh
|
&& ./letest.sh
|
||||||
|
|||||||
75
.github/workflows/OpenIndiana.yml
vendored
Normal file
75
.github/workflows/OpenIndiana.yml
vendored
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
name: OpenIndiana
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- '*'
|
||||||
|
paths:
|
||||||
|
- '*.sh'
|
||||||
|
- '.github/workflows/OpenIndiana.yml'
|
||||||
|
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- dev
|
||||||
|
paths:
|
||||||
|
- '*.sh'
|
||||||
|
- '.github/workflows/OpenIndiana.yml'
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
OpenIndiana:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- TEST_ACME_Server: "LetsEncrypt.org_test"
|
||||||
|
CA_ECDSA: ""
|
||||||
|
CA: ""
|
||||||
|
CA_EMAIL: ""
|
||||||
|
TEST_PREFERRED_CHAIN: (STAGING)
|
||||||
|
- TEST_ACME_Server: "LetsEncrypt.org_test"
|
||||||
|
CA_ECDSA: ""
|
||||||
|
CA: ""
|
||||||
|
CA_EMAIL: ""
|
||||||
|
TEST_PREFERRED_CHAIN: (STAGING)
|
||||||
|
ACME_USE_WGET: 1
|
||||||
|
#- TEST_ACME_Server: "ZeroSSL.com"
|
||||||
|
# CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
|
||||||
|
# CA: "ZeroSSL RSA Domain Secure Site CA"
|
||||||
|
# CA_EMAIL: "githubtest@acme.sh"
|
||||||
|
# TEST_PREFERRED_CHAIN: ""
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
TEST_LOCAL: 1
|
||||||
|
TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
|
||||||
|
CA_ECDSA: ${{ matrix.CA_ECDSA }}
|
||||||
|
CA: ${{ matrix.CA }}
|
||||||
|
CA_EMAIL: ${{ matrix.CA_EMAIL }}
|
||||||
|
TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
|
||||||
|
ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: vmactions/cf-tunnel@v0
|
||||||
|
id: tunnel
|
||||||
|
with:
|
||||||
|
protocol: http
|
||||||
|
port: 8080
|
||||||
|
- name: Set envs
|
||||||
|
run: echo "TestingDomain=${{steps.tunnel.outputs.server}}" >> $GITHUB_ENV
|
||||||
|
- name: Clone acmetest
|
||||||
|
run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
|
||||||
|
- uses: vmactions/openindiana-vm@v0
|
||||||
|
with:
|
||||||
|
envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN ACME_USE_WGET'
|
||||||
|
nat: |
|
||||||
|
"8080": "80"
|
||||||
|
prepare: pkg install socat curl
|
||||||
|
sync: nfs
|
||||||
|
run: |
|
||||||
|
cd ../acmetest \
|
||||||
|
&& ./letest.sh
|
||||||
|
|
||||||
|
|
||||||
8
.github/workflows/PebbleStrict.yml
vendored
8
.github/workflows/PebbleStrict.yml
vendored
@@ -33,11 +33,11 @@ jobs:
|
|||||||
TEST_CA: "Pebble Intermediate CA"
|
TEST_CA: "Pebble Intermediate CA"
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Install tools
|
- name: Install tools
|
||||||
run: sudo apt-get install -y socat
|
run: sudo apt-get install -y socat
|
||||||
- name: Run Pebble
|
- name: Run Pebble
|
||||||
run: cd .. && curl https://raw.githubusercontent.com/letsencrypt/pebble/master/docker-compose.yml >docker-compose.yml && docker-compose up -d
|
run: cd .. && curl https://raw.githubusercontent.com/letsencrypt/pebble/master/docker-compose.yml >docker-compose.yml && docker compose up -d
|
||||||
- name: Set up Pebble
|
- name: Set up Pebble
|
||||||
run: curl --request POST --data '{"ip":"10.30.50.1"}' http://localhost:8055/set-default-ipv4
|
run: curl --request POST --data '{"ip":"10.30.50.1"}' http://localhost:8055/set-default-ipv4
|
||||||
- name: Clone acmetest
|
- name: Clone acmetest
|
||||||
@@ -58,14 +58,14 @@ jobs:
|
|||||||
TEST_IPCERT: 1
|
TEST_IPCERT: 1
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Install tools
|
- name: Install tools
|
||||||
run: sudo apt-get install -y socat
|
run: sudo apt-get install -y socat
|
||||||
- name: Run Pebble
|
- name: Run Pebble
|
||||||
run: |
|
run: |
|
||||||
docker run --rm -itd --name=pebble \
|
docker run --rm -itd --name=pebble \
|
||||||
-e PEBBLE_VA_ALWAYS_VALID=1 \
|
-e PEBBLE_VA_ALWAYS_VALID=1 \
|
||||||
-p 14000:14000 -p 15000:15000 letsencrypt/pebble:latest pebble -config /test/config/pebble-config.json -strict
|
-p 14000:14000 -p 15000:15000 ghcr.io/letsencrypt/pebble:latest -config /test/config/pebble-config.json -strict
|
||||||
- name: Clone acmetest
|
- name: Clone acmetest
|
||||||
run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
|
run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
|
||||||
- name: Run acmetest
|
- name: Run acmetest
|
||||||
|
|||||||
151
.github/workflows/Solaris.yml
vendored
151
.github/workflows/Solaris.yml
vendored
@@ -1,74 +1,77 @@
|
|||||||
name: Solaris
|
name: Solaris
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- '*'
|
- '*'
|
||||||
paths:
|
paths:
|
||||||
- '*.sh'
|
- '*.sh'
|
||||||
- '.github/workflows/Solaris.yml'
|
- '.github/workflows/Solaris.yml'
|
||||||
|
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- dev
|
- dev
|
||||||
paths:
|
paths:
|
||||||
- '*.sh'
|
- '*.sh'
|
||||||
- '.github/workflows/Solaris.yml'
|
- '.github/workflows/Solaris.yml'
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
concurrency:
|
cancel-in-progress: true
|
||||||
group: ${{ github.workflow }}-${{ github.ref }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
Solaris:
|
Solaris:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- TEST_ACME_Server: "LetsEncrypt.org_test"
|
- TEST_ACME_Server: "LetsEncrypt.org_test"
|
||||||
CA_ECDSA: ""
|
CA_ECDSA: ""
|
||||||
CA: ""
|
CA: ""
|
||||||
CA_EMAIL: ""
|
CA_EMAIL: ""
|
||||||
TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
|
TEST_PREFERRED_CHAIN: (STAGING)
|
||||||
- TEST_ACME_Server: "LetsEncrypt.org_test"
|
- TEST_ACME_Server: "LetsEncrypt.org_test"
|
||||||
CA_ECDSA: ""
|
CA_ECDSA: ""
|
||||||
CA: ""
|
CA: ""
|
||||||
CA_EMAIL: ""
|
CA_EMAIL: ""
|
||||||
TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
|
TEST_PREFERRED_CHAIN: (STAGING)
|
||||||
ACME_USE_WGET: 1
|
ACME_USE_WGET: 1
|
||||||
#- TEST_ACME_Server: "ZeroSSL.com"
|
#- TEST_ACME_Server: "ZeroSSL.com"
|
||||||
# CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
|
# CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
|
||||||
# CA: "ZeroSSL RSA Domain Secure Site CA"
|
# CA: "ZeroSSL RSA Domain Secure Site CA"
|
||||||
# CA_EMAIL: "githubtest@acme.sh"
|
# CA_EMAIL: "githubtest@acme.sh"
|
||||||
# TEST_PREFERRED_CHAIN: ""
|
# TEST_PREFERRED_CHAIN: ""
|
||||||
runs-on: macos-12
|
runs-on: ubuntu-latest
|
||||||
env:
|
env:
|
||||||
TEST_LOCAL: 1
|
TEST_LOCAL: 1
|
||||||
TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
|
TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
|
||||||
CA_ECDSA: ${{ matrix.CA_ECDSA }}
|
CA_ECDSA: ${{ matrix.CA_ECDSA }}
|
||||||
CA: ${{ matrix.CA }}
|
CA: ${{ matrix.CA }}
|
||||||
CA_EMAIL: ${{ matrix.CA_EMAIL }}
|
CA_EMAIL: ${{ matrix.CA_EMAIL }}
|
||||||
TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
|
TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
|
||||||
ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
|
ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: vmactions/cf-tunnel@v0
|
- uses: vmactions/cf-tunnel@v0
|
||||||
id: tunnel
|
id: tunnel
|
||||||
with:
|
with:
|
||||||
protocol: http
|
protocol: http
|
||||||
port: 8080
|
port: 8080
|
||||||
- name: Set envs
|
- name: Set envs
|
||||||
run: echo "TestingDomain=${{steps.tunnel.outputs.server}}" >> $GITHUB_ENV
|
run: echo "TestingDomain=${{steps.tunnel.outputs.server}}" >> $GITHUB_ENV
|
||||||
- name: Clone acmetest
|
- name: Clone acmetest
|
||||||
run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
|
run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
|
||||||
- uses: vmactions/solaris-vm@v0
|
- uses: vmactions/solaris-vm@v1
|
||||||
with:
|
with:
|
||||||
envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN ACME_USE_WGET'
|
envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN ACME_USE_WGET'
|
||||||
copyback: "false"
|
nat: |
|
||||||
nat: |
|
"8080": "80"
|
||||||
"8080": "80"
|
prepare: |
|
||||||
prepare: pkgutil -y -i socat curl wget
|
pkgutil -U
|
||||||
run: |
|
pkgutil -y -i socat curl wget
|
||||||
cd ../acmetest \
|
sync: nfs
|
||||||
&& ./letest.sh
|
run: |
|
||||||
|
cd ../acmetest \
|
||||||
|
&& ./letest.sh
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
6
.github/workflows/Ubuntu.yml
vendored
6
.github/workflows/Ubuntu.yml
vendored
@@ -29,12 +29,12 @@ jobs:
|
|||||||
CA_ECDSA: ""
|
CA_ECDSA: ""
|
||||||
CA: ""
|
CA: ""
|
||||||
CA_EMAIL: ""
|
CA_EMAIL: ""
|
||||||
TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
|
TEST_PREFERRED_CHAIN: (STAGING)
|
||||||
- TEST_ACME_Server: "LetsEncrypt.org_test"
|
- TEST_ACME_Server: "LetsEncrypt.org_test"
|
||||||
CA_ECDSA: ""
|
CA_ECDSA: ""
|
||||||
CA: ""
|
CA: ""
|
||||||
CA_EMAIL: ""
|
CA_EMAIL: ""
|
||||||
TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
|
TEST_PREFERRED_CHAIN: (STAGING)
|
||||||
ACME_USE_WGET: 1
|
ACME_USE_WGET: 1
|
||||||
- TEST_ACME_Server: "ZeroSSL.com"
|
- TEST_ACME_Server: "ZeroSSL.com"
|
||||||
CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
|
CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
|
||||||
@@ -70,7 +70,7 @@ jobs:
|
|||||||
TestingDomain: ${{ matrix.TestingDomain }}
|
TestingDomain: ${{ matrix.TestingDomain }}
|
||||||
ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
|
ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Install tools
|
- name: Install tools
|
||||||
run: sudo apt-get install -y socat wget
|
run: sudo apt-get install -y socat wget
|
||||||
- name: Start StepCA
|
- name: Start StepCA
|
||||||
|
|||||||
4
.github/workflows/Windows.yml
vendored
4
.github/workflows/Windows.yml
vendored
@@ -29,7 +29,7 @@ jobs:
|
|||||||
CA_ECDSA: ""
|
CA_ECDSA: ""
|
||||||
CA: ""
|
CA: ""
|
||||||
CA_EMAIL: ""
|
CA_EMAIL: ""
|
||||||
TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
|
TEST_PREFERRED_CHAIN: (STAGING)
|
||||||
#- TEST_ACME_Server: "ZeroSSL.com"
|
#- TEST_ACME_Server: "ZeroSSL.com"
|
||||||
# CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
|
# CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
|
||||||
# CA: "ZeroSSL RSA Domain Secure Site CA"
|
# CA: "ZeroSSL RSA Domain Secure Site CA"
|
||||||
@@ -49,7 +49,7 @@ jobs:
|
|||||||
- name: Set git to use LF
|
- name: Set git to use LF
|
||||||
run: |
|
run: |
|
||||||
git config --global core.autocrlf false
|
git config --global core.autocrlf false
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Install cygwin base packages with chocolatey
|
- name: Install cygwin base packages with chocolatey
|
||||||
run: |
|
run: |
|
||||||
choco config get cacheLocation
|
choco config get cacheLocation
|
||||||
|
|||||||
19
.github/workflows/dockerhub.yml
vendored
19
.github/workflows/dockerhub.yml
vendored
@@ -15,6 +15,8 @@ concurrency:
|
|||||||
group: ${{ github.workflow }}-${{ github.ref }}
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
env:
|
||||||
|
DOCKER_IMAGE: neilpang/acme.sh
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
CheckToken:
|
CheckToken:
|
||||||
@@ -41,9 +43,16 @@ jobs:
|
|||||||
if: "contains(needs.CheckToken.outputs.hasToken, 'true')"
|
if: "contains(needs.CheckToken.outputs.hasToken, 'true')"
|
||||||
steps:
|
steps:
|
||||||
- name: checkout code
|
- name: checkout code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v2
|
uses: docker/setup-qemu-action@v2
|
||||||
|
- name: Extract Docker metadata
|
||||||
|
id: meta
|
||||||
|
uses: docker/metadata-action@v5.5.1
|
||||||
|
with:
|
||||||
|
images: ${DOCKER_IMAGE}
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v2
|
uses: docker/setup-buildx-action@v2
|
||||||
- name: login to docker hub
|
- name: login to docker hub
|
||||||
@@ -51,8 +60,6 @@ jobs:
|
|||||||
echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin
|
echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin
|
||||||
- name: build and push the image
|
- name: build and push the image
|
||||||
run: |
|
run: |
|
||||||
DOCKER_IMAGE=neilpang/acme.sh
|
|
||||||
|
|
||||||
if [[ $GITHUB_REF == refs/tags/* ]]; then
|
if [[ $GITHUB_REF == refs/tags/* ]]; then
|
||||||
DOCKER_IMAGE_TAG=${GITHUB_REF#refs/tags/}
|
DOCKER_IMAGE_TAG=${GITHUB_REF#refs/tags/}
|
||||||
fi
|
fi
|
||||||
@@ -66,8 +73,14 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
DOCKER_LABELS=()
|
||||||
|
while read -r label; do
|
||||||
|
DOCKER_LABELS+=(--label "${label}")
|
||||||
|
done <<<"${DOCKER_METADATA_OUTPUT_LABELS}"
|
||||||
|
|
||||||
docker buildx build \
|
docker buildx build \
|
||||||
--tag ${DOCKER_IMAGE}:${DOCKER_IMAGE_TAG} \
|
--tag ${DOCKER_IMAGE}:${DOCKER_IMAGE_TAG} \
|
||||||
|
"${DOCKER_LABELS[@]}" \
|
||||||
--output "type=image,push=true" \
|
--output "type=image,push=true" \
|
||||||
--build-arg AUTO_UPGRADE=${AUTO_UPGRADE} \
|
--build-arg AUTO_UPGRADE=${AUTO_UPGRADE} \
|
||||||
--platform linux/arm64/v8,linux/amd64,linux/arm/v6,linux/arm/v7,linux/386,linux/ppc64le,linux/s390x .
|
--platform linux/arm64/v8,linux/amd64,linux/arm/v6,linux/arm/v7,linux/386,linux/ppc64le,linux/s390x .
|
||||||
|
|||||||
13
.github/workflows/pr_dns.yml
vendored
13
.github/workflows/pr_dns.yml
vendored
@@ -4,8 +4,6 @@ on:
|
|||||||
pull_request_target:
|
pull_request_target:
|
||||||
types:
|
types:
|
||||||
- opened
|
- opened
|
||||||
branches:
|
|
||||||
- 'dev'
|
|
||||||
paths:
|
paths:
|
||||||
- 'dnsapi/*.sh'
|
- 'dnsapi/*.sh'
|
||||||
|
|
||||||
@@ -22,9 +20,14 @@ jobs:
|
|||||||
owner: context.repo.owner,
|
owner: context.repo.owner,
|
||||||
repo: context.repo.repo,
|
repo: context.repo.repo,
|
||||||
body: `**Welcome**
|
body: `**Welcome**
|
||||||
Please make sure you're read our [DNS API Dev Guide](../wiki/DNS-API-Dev-Guide) and [DNS-API-Test](../wiki/DNS-API-Test).
|
READ ME !!!!!
|
||||||
Then reply on this message, otherwise, your code will not be reviewed or merged.
|
Read me !!!!!!
|
||||||
We look forward to reviewing your Pull request shortly ✨
|
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, 都必须确保通过这个测试.
|
||||||
`
|
`
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
4
.github/workflows/pr_notify.yml
vendored
4
.github/workflows/pr_notify.yml
vendored
@@ -1,4 +1,4 @@
|
|||||||
name: Check dns api
|
name: Check notify api
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request_target:
|
pull_request_target:
|
||||||
@@ -22,7 +22,7 @@ jobs:
|
|||||||
owner: context.repo.owner,
|
owner: context.repo.owner,
|
||||||
repo: context.repo.repo,
|
repo: context.repo.repo,
|
||||||
body: `**Welcome**
|
body: `**Welcome**
|
||||||
Please make sure you're read our [Code-of-conduct](../wiki/Code-of-conduct) and add the usage here: [notify](../wiki/notify).
|
Please make sure you've read our [Code-of-conduct](../wiki/Code-of-conduct) and add the usage here: [notify](../wiki/notify).
|
||||||
Then reply on this message, otherwise, your code will not be reviewed or merged.
|
Then reply on this message, otherwise, your code will not be reviewed or merged.
|
||||||
We look forward to reviewing your Pull request shortly ✨
|
We look forward to reviewing your Pull request shortly ✨
|
||||||
`
|
`
|
||||||
|
|||||||
4
.github/workflows/shellcheck.yml
vendored
4
.github/workflows/shellcheck.yml
vendored
@@ -22,7 +22,7 @@ jobs:
|
|||||||
ShellCheck:
|
ShellCheck:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Install Shellcheck
|
- name: Install Shellcheck
|
||||||
run: sudo apt-get install -y shellcheck
|
run: sudo apt-get install -y shellcheck
|
||||||
- name: DoShellcheck
|
- name: DoShellcheck
|
||||||
@@ -31,7 +31,7 @@ jobs:
|
|||||||
shfmt:
|
shfmt:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Install shfmt
|
- name: Install shfmt
|
||||||
run: curl -sSL https://github.com/mvdan/sh/releases/download/v3.1.2/shfmt_v3.1.2_linux_amd64 -o ~/shfmt && chmod +x ~/shfmt
|
run: curl -sSL https://github.com/mvdan/sh/releases/download/v3.1.2/shfmt_v3.1.2_linux_amd64 -o ~/shfmt && chmod +x ~/shfmt
|
||||||
- name: shfmt
|
- name: shfmt
|
||||||
|
|||||||
65
.github/workflows/wiki-monitor.yml
vendored
Normal file
65
.github/workflows/wiki-monitor.yml
vendored
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
name: Notify via Issue on Wiki Edit
|
||||||
|
|
||||||
|
on:
|
||||||
|
gollum:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
notify:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout wiki repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
repository: ${{ github.repository }}.wiki
|
||||||
|
path: wiki
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Generate wiki change message
|
||||||
|
run: |
|
||||||
|
actor="${{ github.actor }}"
|
||||||
|
sender_url=$(jq -r '.sender.html_url' "$GITHUB_EVENT_PATH")
|
||||||
|
page_name=$(jq -r '.pages[0].page_name' "$GITHUB_EVENT_PATH")
|
||||||
|
page_sha=$(jq -r '.pages[0].sha' "$GITHUB_EVENT_PATH")
|
||||||
|
page_url=$(jq -r '.pages[0].html_url' "$GITHUB_EVENT_PATH")
|
||||||
|
page_action=$(jq -r '.pages[0].action' "$GITHUB_EVENT_PATH")
|
||||||
|
page_summary=$(jq -r '.pages[0].summary' "$GITHUB_EVENT_PATH")
|
||||||
|
now="$(date '+%Y-%m-%d %H:%M:%S')"
|
||||||
|
|
||||||
|
cd wiki
|
||||||
|
prev_sha=$(git rev-list $page_sha^ -- "$page_name.md" | head -n 1)
|
||||||
|
if [ -n "$prev_sha" ]; then
|
||||||
|
git diff $prev_sha $page_sha -- "$page_name.md" > ../wiki.diff || echo "(No diff found)" > ../wiki.diff
|
||||||
|
else
|
||||||
|
echo "(no diff)" > ../wiki.diff
|
||||||
|
fi
|
||||||
|
cd ..
|
||||||
|
{
|
||||||
|
echo "Wiki edited"
|
||||||
|
echo -n "User: "
|
||||||
|
echo "@$actor [$actor]($sender_url)"
|
||||||
|
echo "Time: $now"
|
||||||
|
echo "Page: [$page_name]($page_url) (Action: $page_action)"
|
||||||
|
echo "Comment: $page_summary"
|
||||||
|
echo "[Click here to Revert](${page_url}/_history)"
|
||||||
|
echo ""
|
||||||
|
echo "----"
|
||||||
|
echo "### diff:"
|
||||||
|
echo '```diff'
|
||||||
|
cat wiki.diff
|
||||||
|
echo '```'
|
||||||
|
} > wiki-change-msg.txt
|
||||||
|
|
||||||
|
- name: Create issue to notify Neilpang
|
||||||
|
uses: peter-evans/create-issue-from-file@v5
|
||||||
|
with:
|
||||||
|
title: "Wiki edited"
|
||||||
|
content-filepath: ./wiki-change-msg.txt
|
||||||
|
assignees: Neilpang
|
||||||
|
env:
|
||||||
|
TZ: Asia/Shanghai
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
21
Dockerfile
21
Dockerfile
@@ -1,4 +1,4 @@
|
|||||||
FROM alpine:3.17
|
FROM alpine:3.22
|
||||||
|
|
||||||
RUN apk --no-cache add -f \
|
RUN apk --no-cache add -f \
|
||||||
openssl \
|
openssl \
|
||||||
@@ -13,20 +13,27 @@ RUN apk --no-cache add -f \
|
|||||||
tar \
|
tar \
|
||||||
libidn \
|
libidn \
|
||||||
jq \
|
jq \
|
||||||
|
yq-go \
|
||||||
cronie
|
cronie
|
||||||
|
|
||||||
ENV LE_CONFIG_HOME /acme.sh
|
ENV LE_WORKING_DIR=/acmebin
|
||||||
|
|
||||||
|
ENV LE_CONFIG_HOME=/acme.sh
|
||||||
|
|
||||||
ARG AUTO_UPGRADE=1
|
ARG AUTO_UPGRADE=1
|
||||||
|
|
||||||
ENV AUTO_UPGRADE $AUTO_UPGRADE
|
ENV AUTO_UPGRADE=$AUTO_UPGRADE
|
||||||
|
|
||||||
#Install
|
#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/
|
RUN cd /install_acme.sh && ([ -f /install_acme.sh/acme.sh ] && /install_acme.sh/acme.sh --install || curl https://get.acme.sh | sh) && rm -rf /install_acme.sh/
|
||||||
|
|
||||||
|
|
||||||
RUN ln -s /root/.acme.sh/acme.sh /usr/local/bin/acme.sh && crontab -l | grep acme.sh | sed 's#> /dev/null#> /proc/1/fd/1 2>/proc/1/fd/2#' | crontab -
|
RUN ln -s $LE_WORKING_DIR/acme.sh /usr/local/bin/acme.sh && crontab -l | grep acme.sh | sed 's#> /dev/null#> /proc/1/fd/1 2>/proc/1/fd/2#' | crontab -
|
||||||
|
|
||||||
RUN for verb in help \
|
RUN for verb in help \
|
||||||
version \
|
version \
|
||||||
@@ -60,7 +67,7 @@ RUN for verb in help \
|
|||||||
set-default-ca \
|
set-default-ca \
|
||||||
set-default-chain \
|
set-default-chain \
|
||||||
; do \
|
; do \
|
||||||
printf -- "%b" "#!/usr/bin/env sh\n/root/.acme.sh/acme.sh --${verb} --config-home /acme.sh \"\$@\"" >/usr/local/bin/--${verb} && chmod +x /usr/local/bin/--${verb} \
|
printf -- "%b" "#!/usr/bin/env sh\n$LE_WORKING_DIR/acme.sh --${verb} --config-home $LE_CONFIG_HOME \"\$@\"" >/usr/local/bin/--${verb} && chmod +x /usr/local/bin/--${verb} \
|
||||||
; done
|
; done
|
||||||
|
|
||||||
RUN printf "%b" '#!'"/usr/bin/env sh\n \
|
RUN printf "%b" '#!'"/usr/bin/env sh\n \
|
||||||
@@ -68,7 +75,7 @@ if [ \"\$1\" = \"daemon\" ]; then \n \
|
|||||||
exec crond -n -s -m off \n \
|
exec crond -n -s -m off \n \
|
||||||
else \n \
|
else \n \
|
||||||
exec -- \"\$@\"\n \
|
exec -- \"\$@\"\n \
|
||||||
fi\n" >/entry.sh && chmod +x /entry.sh
|
fi\n" >/entry.sh && chmod +x /entry.sh && chmod -R o+rwx $LE_WORKING_DIR && chmod -R o+rwx $LE_CONFIG_HOME
|
||||||
|
|
||||||
VOLUME /acme.sh
|
VOLUME /acme.sh
|
||||||
|
|
||||||
|
|||||||
439
README.md
439
README.md
@@ -1,52 +1,71 @@
|
|||||||
# An ACME Shell script: acme.sh
|
<p align="center">
|
||||||
|
<a href="https://zerossl.com/?fromacme.sh">
|
||||||
|
<img src="https://github.com/user-attachments/assets/7531085e-399b-4ac2-82a2-90d14a0b7f05" alt="zerossl.com">
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
[](https://github.com/acmesh-official/acme.sh/actions/workflows/FreeBSD.yml)
|
<h1 align="center">🔐 acme.sh</h1>
|
||||||
[](https://github.com/acmesh-official/acme.sh/actions/workflows/OpenBSD.yml)
|
<h3 align="center">An ACME Protocol Client Written Purely in Shell</h3>
|
||||||
[](https://github.com/acmesh-official/acme.sh/actions/workflows/NetBSD.yml)
|
|
||||||
[](https://github.com/acmesh-official/acme.sh/actions/workflows/MacOS.yml)
|
<p align="center">
|
||||||
[](https://github.com/acmesh-official/acme.sh/actions/workflows/Ubuntu.yml)
|
<a href="https://github.com/acmesh-official/acme.sh/actions/workflows/FreeBSD.yml"><img src="https://github.com/acmesh-official/acme.sh/actions/workflows/FreeBSD.yml/badge.svg" alt="FreeBSD"></a>
|
||||||
[](https://github.com/acmesh-official/acme.sh/actions/workflows/Windows.yml)
|
<a href="https://github.com/acmesh-official/acme.sh/actions/workflows/OpenBSD.yml"><img src="https://github.com/acmesh-official/acme.sh/actions/workflows/OpenBSD.yml/badge.svg" alt="OpenBSD"></a>
|
||||||
[](https://github.com/acmesh-official/acme.sh/actions/workflows/Solaris.yml)
|
<a href="https://github.com/acmesh-official/acme.sh/actions/workflows/NetBSD.yml"><img src="https://github.com/acmesh-official/acme.sh/actions/workflows/NetBSD.yml/badge.svg" alt="NetBSD"></a>
|
||||||
[](https://github.com/acmesh-official/acme.sh/actions/workflows/DragonFlyBSD.yml)
|
<a href="https://github.com/acmesh-official/acme.sh/actions/workflows/MacOS.yml"><img src="https://github.com/acmesh-official/acme.sh/actions/workflows/MacOS.yml/badge.svg" alt="MacOS"></a>
|
||||||
|
<a href="https://github.com/acmesh-official/acme.sh/actions/workflows/Ubuntu.yml"><img src="https://github.com/acmesh-official/acme.sh/actions/workflows/Ubuntu.yml/badge.svg" alt="Ubuntu"></a>
|
||||||
|
<a href="https://github.com/acmesh-official/acme.sh/actions/workflows/Windows.yml"><img src="https://github.com/acmesh-official/acme.sh/actions/workflows/Windows.yml/badge.svg" alt="Windows"></a>
|
||||||
|
<a href="https://github.com/acmesh-official/acme.sh/actions/workflows/Solaris.yml"><img src="https://github.com/acmesh-official/acme.sh/actions/workflows/Solaris.yml/badge.svg" alt="Solaris"></a>
|
||||||
|
<a href="https://github.com/acmesh-official/acme.sh/actions/workflows/DragonFlyBSD.yml"><img src="https://github.com/acmesh-official/acme.sh/actions/workflows/DragonFlyBSD.yml/badge.svg" alt="DragonFlyBSD"></a>
|
||||||
|
<a href="https://github.com/acmesh-official/acme.sh/actions/workflows/Omnios.yml"><img src="https://github.com/acmesh-official/acme.sh/actions/workflows/Omnios.yml/badge.svg" alt="Omnios"></a>
|
||||||
|
<a href="https://github.com/acmesh-official/acme.sh/actions/workflows/OpenIndiana.yml"><img src="https://github.com/acmesh-official/acme.sh/actions/workflows/OpenIndiana.yml/badge.svg" alt="OpenIndiana"></a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="https://github.com/acmesh-official/acme.sh/workflows/Shellcheck/badge.svg" alt="Shellcheck">
|
||||||
|
<img src="https://github.com/acmesh-official/acme.sh/workflows/PebbleStrict/badge.svg" alt="PebbleStrict">
|
||||||
|
<img src="https://github.com/acmesh-official/acme.sh/workflows/Build%20DockerHub/badge.svg" alt="DockerHub">
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://opencollective.com/acmesh"><img src="https://opencollective.com/acmesh/all/badge.svg?label=financial+contributors" alt="Financial Contributors on Open Collective"></a>
|
||||||
|
<a href="https://gitter.im/acme-sh/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge"><img src="https://badges.gitter.im/acme-sh/Lobby.svg" alt="Join the chat at Gitter"></a>
|
||||||
|
<a href="https://hub.docker.com/r/neilpang/acme.sh" title="Click to view the image on Docker Hub"><img src="https://img.shields.io/docker/stars/neilpang/acme.sh.svg" alt="Docker stars"></a>
|
||||||
|
<a href="https://hub.docker.com/r/neilpang/acme.sh" title="Click to view the image on Docker Hub"><img src="https://img.shields.io/docker/pulls/neilpang/acme.sh.svg" alt="Docker pulls"></a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
|
||||||

|
---
|
||||||

|
|
||||||

|
|
||||||
|
|
||||||
|
## ✨ Features
|
||||||
|
|
||||||
<a href="https://opencollective.com/acmesh" alt="Financial Contributors on Open Collective"><img src="https://opencollective.com/acmesh/all/badge.svg?label=financial+contributors" /></a>
|
- 🐚 An ACME protocol client written **purely in Shell** (Unix shell) language
|
||||||
[](https://gitter.im/acme-sh/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
- 📜 Full ACME protocol implementation
|
||||||
[](https://hub.docker.com/r/neilpang/acme.sh "Click to view the image on Docker Hub")
|
- 🔑 Support **ECDSA** certificates
|
||||||
[](https://hub.docker.com/r/neilpang/acme.sh "Click to view the image on Docker Hub")
|
- 🌐 Support **SAN** and **wildcard** certificates
|
||||||
|
- ⚡ Simple, powerful and very easy to use — only **3 minutes** to learn!
|
||||||
|
- 🔧 Compatible with **Bash**, **dash** and **sh**
|
||||||
|
- 🚫 No dependencies on Python
|
||||||
|
- 🔄 One script to issue, renew and install your certificates automatically
|
||||||
|
- 👤 **DOES NOT** require `root/sudoer` access
|
||||||
|
- 🐳 Docker ready
|
||||||
|
- 🌍 IPv6 ready
|
||||||
|
- 📧 Cron job notifications for renewal or error
|
||||||
|
|
||||||
|
> 💡 It's probably the **easiest & smartest** shell script to automatically issue & renew free certificates.
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://github.com/acmesh-official/acme.sh/wiki"><strong>📚 Wiki</strong></a> •
|
||||||
|
<a href="https://github.com/acmesh-official/acme.sh/wiki/Run-acme.sh-in-docker"><strong>🐳 Docker Guide</strong></a> •
|
||||||
|
<a href="https://twitter.com/neilpangxa"><strong>🐦 Twitter</strong></a>
|
||||||
|
</p>
|
||||||
|
|
||||||
- An ACME protocol client written purely in Shell (Unix shell) language.
|
---
|
||||||
- Full ACME protocol implementation.
|
|
||||||
- Support ECDSA certs
|
|
||||||
- Support SAN and wildcard certs
|
|
||||||
- Simple, powerful and very easy to use. You only need 3 minutes to learn it.
|
|
||||||
- Bash, dash and sh compatible.
|
|
||||||
- Purely written in Shell with no dependencies on python.
|
|
||||||
- Just one script to issue, renew and install your certificates automatically.
|
|
||||||
- DOES NOT require `root/sudoer` access.
|
|
||||||
- Docker ready
|
|
||||||
- IPv6 ready
|
|
||||||
- Cron job notifications for renewal or error etc.
|
|
||||||
|
|
||||||
It's probably the `easiest & smartest` shell script to automatically issue & renew the free certificates.
|
## 🌏 [中文说明](https://github.com/acmesh-official/acme.sh/wiki/%E8%AF%B4%E6%98%8E)
|
||||||
|
|
||||||
Wiki: https://github.com/acmesh-official/acme.sh/wiki
|
---
|
||||||
|
|
||||||
For Docker Fans: [acme.sh :two_hearts: Docker ](https://github.com/acmesh-official/acme.sh/wiki/Run-acme.sh-in-docker)
|
## 🏆 Who Uses acme.sh?
|
||||||
|
|
||||||
Twitter: [@neilpangxa](https://twitter.com/neilpangxa)
|
|
||||||
|
|
||||||
|
|
||||||
# [中文说明](https://github.com/acmesh-official/acme.sh/wiki/%E8%AF%B4%E6%98%8E)
|
|
||||||
|
|
||||||
# Who:
|
|
||||||
- [FreeBSD.org](https://blog.crashed.org/letsencrypt-in-freebsd-org/)
|
- [FreeBSD.org](https://blog.crashed.org/letsencrypt-in-freebsd-org/)
|
||||||
- [ruby-china.org](https://ruby-china.org/topics/31983)
|
- [ruby-china.org](https://ruby-china.org/topics/31983)
|
||||||
- [Proxmox](https://pve.proxmox.com/wiki/Certificate_Management)
|
- [Proxmox](https://pve.proxmox.com/wiki/Certificate_Management)
|
||||||
@@ -60,7 +79,9 @@ Twitter: [@neilpangxa](https://twitter.com/neilpangxa)
|
|||||||
- [lnmp.org](https://lnmp.org/)
|
- [lnmp.org](https://lnmp.org/)
|
||||||
- [more...](https://github.com/acmesh-official/acme.sh/wiki/Blogs-and-tutorials)
|
- [more...](https://github.com/acmesh-official/acme.sh/wiki/Blogs-and-tutorials)
|
||||||
|
|
||||||
# Tested OS
|
---
|
||||||
|
|
||||||
|
## 🖥️ Tested OS
|
||||||
|
|
||||||
| NO | Status| Platform|
|
| NO | Status| Platform|
|
||||||
|----|-------|---------|
|
|----|-------|---------|
|
||||||
@@ -73,66 +94,76 @@ Twitter: [@neilpangxa](https://twitter.com/neilpangxa)
|
|||||||
|7|[](https://github.com/acmesh-official/acme.sh/actions/workflows/OpenBSD.yml)|OpenBSD
|
|7|[](https://github.com/acmesh-official/acme.sh/actions/workflows/OpenBSD.yml)|OpenBSD
|
||||||
|8|[](https://github.com/acmesh-official/acme.sh/actions/workflows/NetBSD.yml)|NetBSD
|
|8|[](https://github.com/acmesh-official/acme.sh/actions/workflows/NetBSD.yml)|NetBSD
|
||||||
|9|[](https://github.com/acmesh-official/acme.sh/actions/workflows/DragonFlyBSD.yml)|DragonFlyBSD
|
|9|[](https://github.com/acmesh-official/acme.sh/actions/workflows/DragonFlyBSD.yml)|DragonFlyBSD
|
||||||
|10|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)| Debian
|
|10|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Omnios.yml)|Omnios
|
||||||
|11|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|CentOS
|
|11|[](https://github.com/acmesh-official/acme.sh/actions/workflows/OpenIndiana.yml)|OpenIndiana
|
||||||
|12|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|openSUSE
|
|12|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)| Debian
|
||||||
|13|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Alpine Linux (with curl)
|
|13|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|openSUSE
|
||||||
|14|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Archlinux
|
|14|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Alpine Linux (with curl)
|
||||||
|15|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|fedora
|
|15|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Archlinux
|
||||||
|16|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Kali Linux
|
|16|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|fedora
|
||||||
|17|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Oracle Linux
|
|17|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Kali Linux
|
||||||
|18|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Mageia
|
|18|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Oracle Linux
|
||||||
|19|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Gentoo Linux
|
|19|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Mageia
|
||||||
|10|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|ClearLinux
|
|20|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Gentoo Linux
|
||||||
|11|-----| Cloud Linux https://github.com/acmesh-official/acme.sh/issues/111
|
|21|-----| Cloud Linux https://github.com/acmesh-official/acme.sh/issues/111
|
||||||
|22|-----| OpenWRT: Tested and working. See [wiki page](https://github.com/acmesh-official/acme.sh/wiki/How-to-run-on-OpenWRT)
|
|22|-----| OpenWRT: Tested and working. See [wiki page](https://github.com/acmesh-official/acme.sh/wiki/How-to-run-on-OpenWRT)
|
||||||
|23|[](https://github.com/acmesh-official/letest#here-are-the-latest-status)| Proxmox: See Proxmox VE Wiki. Version [4.x, 5.0, 5.1](https://pve.proxmox.com/wiki/HTTPS_Certificate_Configuration_(Version_4.x,_5.0_and_5.1)#Let.27s_Encrypt_using_acme.sh), version [5.2 and up](https://pve.proxmox.com/wiki/Certificate_Management)
|
|23|[](https://github.com/acmesh-official/letest#here-are-the-latest-status)| Proxmox: See Proxmox VE Wiki. Version [4.x, 5.0, 5.1](https://pve.proxmox.com/wiki/HTTPS_Certificate_Configuration_(Version_4.x,_5.0_and_5.1)#Let.27s_Encrypt_using_acme.sh), version [5.2 and up](https://pve.proxmox.com/wiki/Certificate_Management)
|
||||||
|
|
||||||
|
|
||||||
Check our [testing project](https://github.com/acmesh-official/acmetest):
|
> 🧪 Check our [testing project](https://github.com/acmesh-official/acmetest)
|
||||||
|
>
|
||||||
|
> 🖥️ The testing VMs are supported by [vmactions.org](https://vmactions.org)
|
||||||
|
|
||||||
https://github.com/acmesh-official/acmetest
|
---
|
||||||
|
|
||||||
# Supported CA
|
## 🏛️ Supported CA
|
||||||
|
|
||||||
- [ZeroSSL.com CA](https://github.com/acmesh-official/acme.sh/wiki/ZeroSSL.com-CA)(default)
|
| CA | Status |
|
||||||
- Letsencrypt.org CA
|
|---|---|
|
||||||
- [BuyPass.com CA](https://github.com/acmesh-official/acme.sh/wiki/BuyPass.com-CA)
|
| [ZeroSSL.com CA](https://github.com/acmesh-official/acme.sh/wiki/ZeroSSL.com-CA) | ⭐ **Default** |
|
||||||
- [SSL.com CA](https://github.com/acmesh-official/acme.sh/wiki/SSL.com-CA)
|
| Letsencrypt.org CA | ✅ Supported |
|
||||||
- [Google.com Public CA](https://github.com/acmesh-official/acme.sh/wiki/Google-Public-CA)
|
| [SSL.com CA](https://github.com/acmesh-official/acme.sh/wiki/SSL.com-CA) | ✅ Supported |
|
||||||
- [Pebble strict Mode](https://github.com/letsencrypt/pebble)
|
| [Google.com Public CA](https://github.com/acmesh-official/acme.sh/wiki/Google-Public-CA) | ✅ Supported |
|
||||||
- Any other [RFC8555](https://tools.ietf.org/html/rfc8555)-compliant CA
|
| [Actalis.com CA](https://github.com/acmesh-official/acme.sh/wiki/Actalis.com-CA) | ✅ Supported |
|
||||||
|
| [Pebble strict Mode](https://github.com/letsencrypt/pebble) | ✅ Supported |
|
||||||
|
| Any [RFC8555](https://tools.ietf.org/html/rfc8555)-compliant CA | ✅ Supported |
|
||||||
|
|
||||||
# Supported modes
|
---
|
||||||
|
|
||||||
- Webroot mode
|
## ⚙️ Supported Modes
|
||||||
- Standalone mode
|
|
||||||
- Standalone tls-alpn mode
|
|
||||||
- Apache mode
|
|
||||||
- Nginx mode
|
|
||||||
- DNS mode
|
|
||||||
- [DNS alias mode](https://github.com/acmesh-official/acme.sh/wiki/DNS-alias-mode)
|
|
||||||
- [Stateless mode](https://github.com/acmesh-official/acme.sh/wiki/Stateless-Mode)
|
|
||||||
|
|
||||||
|
| Mode | Description |
|
||||||
|
|------|-------------|
|
||||||
|
| 📁 Webroot mode | Use existing webroot directory |
|
||||||
|
| 🖥️ Standalone mode | Built-in webserver on port 80 |
|
||||||
|
| 🔐 Standalone tls-alpn mode | Built-in webserver on port 443 |
|
||||||
|
| 🪶 Apache mode | Use Apache for verification |
|
||||||
|
| ⚡ Nginx mode | Use Nginx for verification |
|
||||||
|
| 🌐 DNS mode | Use DNS TXT records |
|
||||||
|
| 🔗 [DNS alias mode](https://github.com/acmesh-official/acme.sh/wiki/DNS-alias-mode) | Use DNS alias for verification |
|
||||||
|
| 📡 [Stateless mode](https://github.com/acmesh-official/acme.sh/wiki/Stateless-Mode) | Stateless verification |
|
||||||
|
|
||||||
# 1. How to install
|
---
|
||||||
|
|
||||||
### 1. Install online
|
## 📖 Usage Guide
|
||||||
|
|
||||||
Check this project: https://github.com/acmesh-official/get.acme.sh
|
### 1️⃣ How to Install
|
||||||
|
|
||||||
|
#### 📥 Install Online
|
||||||
|
|
||||||
|
> Check this project: https://github.com/acmesh-official/get.acme.sh
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl https://get.acme.sh | sh -s email=my@example.com
|
curl https://get.acme.sh | sh -s email=my@example.com
|
||||||
```
|
```
|
||||||
|
|
||||||
Or:
|
**Or:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
wget -O - https://get.acme.sh | sh -s email=my@example.com
|
wget -O - https://get.acme.sh | sh -s email=my@example.com
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### 📦 Install from Git
|
||||||
### 2. Or, Install from git
|
|
||||||
|
|
||||||
Clone this project and launch installation:
|
Clone this project and launch installation:
|
||||||
|
|
||||||
@@ -142,11 +173,11 @@ cd ./acme.sh
|
|||||||
./acme.sh --install -m my@example.com
|
./acme.sh --install -m my@example.com
|
||||||
```
|
```
|
||||||
|
|
||||||
You `don't have to be root` then, although `it is recommended`.
|
> 💡 You `don't have to be root` then, although `it is recommended`.
|
||||||
|
|
||||||
Advanced Installation: https://github.com/acmesh-official/acme.sh/wiki/How-to-install
|
📚 **Advanced Installation:** https://github.com/acmesh-official/acme.sh/wiki/How-to-install
|
||||||
|
|
||||||
The installer will perform 3 actions:
|
**The installer will perform 3 actions:**
|
||||||
|
|
||||||
1. Create and copy `acme.sh` to your home dir (`$HOME`): `~/.acme.sh/`.
|
1. Create and copy `acme.sh` to your home dir (`$HOME`): `~/.acme.sh/`.
|
||||||
All certs will be placed in this folder too.
|
All certs will be placed in this folder too.
|
||||||
@@ -159,17 +190,19 @@ Cron entry example:
|
|||||||
0 0 * * * "/home/user/.acme.sh"/acme.sh --cron --home "/home/user/.acme.sh" > /dev/null
|
0 0 * * * "/home/user/.acme.sh"/acme.sh --cron --home "/home/user/.acme.sh" > /dev/null
|
||||||
```
|
```
|
||||||
|
|
||||||
After the installation, you must close the current terminal and reopen it to make the alias take effect.
|
> ⚠️ After the installation, you must close the current terminal and reopen it to make the alias take effect.
|
||||||
|
|
||||||
Ok, you are ready to issue certs now.
|
✅ **You are ready to issue certs now!**
|
||||||
|
|
||||||
Show help message:
|
**Show help message:**
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
root@v1:~# acme.sh -h
|
acme.sh -h
|
||||||
```
|
```
|
||||||
|
|
||||||
# 2. Just issue a cert
|
---
|
||||||
|
|
||||||
|
### 2️⃣ Issue a Certificate
|
||||||
|
|
||||||
**Example 1:** Single domain.
|
**Example 1:** Single domain.
|
||||||
|
|
||||||
@@ -204,17 +237,21 @@ You must point and bind all the domains to the same webroot dir: `/home/wwwroot/
|
|||||||
|
|
||||||
The certs will be placed in `~/.acme.sh/example.com/`
|
The certs will be placed in `~/.acme.sh/example.com/`
|
||||||
|
|
||||||
The certs will be renewed automatically every **60** days.
|
> 🔄 The certs will be renewed automatically every **30** days.
|
||||||
|
|
||||||
More examples: https://github.com/acmesh-official/acme.sh/wiki/How-to-issue-a-cert
|
> 🔐 The certs will default to **ECC** certificates.
|
||||||
|
|
||||||
|
📚 **More examples:** https://github.com/acmesh-official/acme.sh/wiki/How-to-issue-a-cert
|
||||||
|
|
||||||
# 3. Install the cert to Apache/Nginx etc.
|
---
|
||||||
|
|
||||||
|
### 3️⃣ Install the Certificate to Apache/Nginx
|
||||||
|
|
||||||
After the cert is generated, you probably want to install/copy the cert to your Apache/Nginx or other servers.
|
After the cert is generated, you probably want to install/copy the cert to your Apache/Nginx or other servers.
|
||||||
You **MUST** use this command to copy the certs to the target files, **DO NOT** use the certs files in **~/.acme.sh/** folder, they are for internal use only, the folder structure may change in the future.
|
|
||||||
|
|
||||||
**Apache** example:
|
> ⚠️ **IMPORTANT:** You **MUST** use this command to copy the certs to the target files. **DO NOT** use the certs files in `~/.acme.sh/` folder — they are for internal use only, the folder structure may change in the future.
|
||||||
|
|
||||||
|
#### 🪶 Apache Example:
|
||||||
```bash
|
```bash
|
||||||
acme.sh --install-cert -d example.com \
|
acme.sh --install-cert -d example.com \
|
||||||
--cert-file /path/to/certfile/in/apache/cert.pem \
|
--cert-file /path/to/certfile/in/apache/cert.pem \
|
||||||
@@ -223,7 +260,7 @@ acme.sh --install-cert -d example.com \
|
|||||||
--reloadcmd "service apache2 force-reload"
|
--reloadcmd "service apache2 force-reload"
|
||||||
```
|
```
|
||||||
|
|
||||||
**Nginx** example:
|
#### ⚡ Nginx Example:
|
||||||
```bash
|
```bash
|
||||||
acme.sh --install-cert -d example.com \
|
acme.sh --install-cert -d example.com \
|
||||||
--key-file /path/to/keyfile/in/nginx/key.pem \
|
--key-file /path/to/keyfile/in/nginx/key.pem \
|
||||||
@@ -237,91 +274,89 @@ The ownership and permission info of existing files are preserved. You can pre-c
|
|||||||
|
|
||||||
Install/copy the cert/key to the production Apache or Nginx path.
|
Install/copy the cert/key to the production Apache or Nginx path.
|
||||||
|
|
||||||
The cert will be renewed every **60** days by default (which is configurable). Once the cert is renewed, the Apache/Nginx service will be reloaded automatically by the command: `service apache2 force-reload` or `service nginx force-reload`.
|
> 🔄 The cert will be renewed every **30** days by default (configurable). Once renewed, the Apache/Nginx service will be reloaded automatically.
|
||||||
|
|
||||||
|
> ⚠️ **IMPORTANT:** The `reloadcmd` is very important. The cert can be automatically renewed, but without a correct `reloadcmd`, the cert may not be flushed to your server (like nginx or apache), then your website will not be able to show the renewed cert.
|
||||||
|
|
||||||
**Please take care: The reloadcmd is very important. The cert can be automatically renewed, but, without a correct 'reloadcmd' the cert may not be flushed to your server(like nginx or apache), then your website will not be able to show renewed cert in 60 days.**
|
---
|
||||||
|
|
||||||
# 4. Use Standalone server to issue cert
|
### 4️⃣ Use Standalone Server to Issue Certificate
|
||||||
|
|
||||||
**(requires you to be root/sudoer or have permission to listen on port 80 (TCP))**
|
> 🔐 Requires root/sudoer or permission to listen on port **80** (TCP)
|
||||||
|
|
||||||
Port `80` (TCP) **MUST** be free to listen on, otherwise you will be prompted to free it and try again.
|
> ⚠️ Port `80` (TCP) **MUST** be free to listen on, otherwise you will be prompted to free it and try again.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
acme.sh --issue --standalone -d example.com -d www.example.com -d cp.example.com
|
acme.sh --issue --standalone -d example.com -d www.example.com -d cp.example.com
|
||||||
```
|
```
|
||||||
|
|
||||||
More examples: https://github.com/acmesh-official/acme.sh/wiki/How-to-issue-a-cert
|
📚 **More examples:** https://github.com/acmesh-official/acme.sh/wiki/How-to-issue-a-cert
|
||||||
|
|
||||||
# 5. Use Standalone ssl server to issue cert
|
---
|
||||||
|
|
||||||
**(requires you to be root/sudoer or have permission to listen on port 443 (TCP))**
|
### 5️⃣ Use Standalone TLS Server to Issue Certificate
|
||||||
|
|
||||||
Port `443` (TCP) **MUST** be free to listen on, otherwise you will be prompted to free it and try again.
|
> 🔐 Requires root/sudoer or permission to listen on port **443** (TCP)
|
||||||
|
|
||||||
|
> ⚠️ Port `443` (TCP) **MUST** be free to listen on, otherwise you will be prompted to free it and try again.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
acme.sh --issue --alpn -d example.com -d www.example.com -d cp.example.com
|
acme.sh --issue --alpn -d example.com -d www.example.com -d cp.example.com
|
||||||
```
|
```
|
||||||
|
|
||||||
More examples: https://github.com/acmesh-official/acme.sh/wiki/How-to-issue-a-cert
|
📚 **More examples:** https://github.com/acmesh-official/acme.sh/wiki/How-to-issue-a-cert
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
# 6. Use Apache mode
|
### 6️⃣ Use Apache Mode
|
||||||
|
|
||||||
**(requires you to be root/sudoer, since it is required to interact with Apache server)**
|
> 🔐 Requires root/sudoer to interact with Apache server
|
||||||
|
|
||||||
If you are running a web server, it is recommended to use the `Webroot mode`.
|
If you are running a web server, it is recommended to use the `Webroot mode`.
|
||||||
|
|
||||||
Particularly, if you are running an Apache server, you can use Apache mode instead. This mode doesn't write any files to your web root folder.
|
Particularly, if you are running an Apache server, you can use Apache mode instead. This mode doesn't write any files to your web root folder.
|
||||||
|
|
||||||
Just set string "apache" as the second argument and it will force use of apache plugin automatically.
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
acme.sh --issue --apache -d example.com -d www.example.com -d cp.example.com
|
acme.sh --issue --apache -d example.com -d www.example.com -d cp.example.com
|
||||||
```
|
```
|
||||||
|
|
||||||
**This apache mode is only to issue the cert, it will not change your apache config files.
|
> 💡 **Note:** This Apache mode is only to issue the cert, it will **not** change your Apache config files. You will need to configure your website config files to use the cert by yourself. We don't want to mess with your Apache server, don't worry!
|
||||||
You will need to configure your website config files to use the cert by yourself.
|
|
||||||
We don't want to mess with your apache server, don't worry.**
|
|
||||||
|
|
||||||
More examples: https://github.com/acmesh-official/acme.sh/wiki/How-to-issue-a-cert
|
📚 **More examples:** https://github.com/acmesh-official/acme.sh/wiki/How-to-issue-a-cert
|
||||||
|
|
||||||
# 7. Use Nginx mode
|
---
|
||||||
|
|
||||||
**(requires you to be root/sudoer, since it is required to interact with Nginx server)**
|
### 7️⃣ Use Nginx Mode
|
||||||
|
|
||||||
|
> 🔐 Requires root/sudoer to interact with Nginx server
|
||||||
|
|
||||||
If you are running a web server, it is recommended to use the `Webroot mode`.
|
If you are running a web server, it is recommended to use the `Webroot mode`.
|
||||||
|
|
||||||
Particularly, if you are running an nginx server, you can use nginx mode instead. This mode doesn't write any files to your web root folder.
|
Particularly, if you are running an Nginx server, you can use Nginx mode instead. This mode doesn't write any files to your web root folder.
|
||||||
|
|
||||||
Just set string "nginx" as the second argument.
|
It will configure Nginx server automatically to verify the domain and then restore the Nginx config to the original version. So, the config is not changed.
|
||||||
|
|
||||||
It will configure nginx server automatically to verify the domain and then restore the nginx config to the original version.
|
|
||||||
|
|
||||||
So, the config is not changed.
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
acme.sh --issue --nginx -d example.com -d www.example.com -d cp.example.com
|
acme.sh --issue --nginx -d example.com -d www.example.com -d cp.example.com
|
||||||
```
|
```
|
||||||
|
|
||||||
**This nginx mode is only to issue the cert, it will not change your nginx config files.
|
> 💡 **Note:** This Nginx mode is only to issue the cert, it will **not** change your Nginx config files. You will need to configure your website config files to use the cert by yourself. We don't want to mess with your Nginx server, don't worry!
|
||||||
You will need to configure your website config files to use the cert by yourself.
|
|
||||||
We don't want to mess with your nginx server, don't worry.**
|
|
||||||
|
|
||||||
More examples: https://github.com/acmesh-official/acme.sh/wiki/How-to-issue-a-cert
|
📚 **More examples:** https://github.com/acmesh-official/acme.sh/wiki/How-to-issue-a-cert
|
||||||
|
|
||||||
# 8. Automatic DNS API integration
|
---
|
||||||
|
|
||||||
|
### 8️⃣ Automatic DNS API Integration
|
||||||
|
|
||||||
If your DNS provider supports API access, we can use that API to automatically issue the certs.
|
If your DNS provider supports API access, we can use that API to automatically issue the certs.
|
||||||
|
|
||||||
You don't have to do anything manually!
|
> ✨ **You don't have to do anything manually!**
|
||||||
|
|
||||||
### Currently acme.sh supports most of the dns providers:
|
📚 **Currently acme.sh supports most DNS providers:** https://github.com/acmesh-official/acme.sh/wiki/dnsapi
|
||||||
|
|
||||||
https://github.com/acmesh-official/acme.sh/wiki/dnsapi
|
---
|
||||||
|
|
||||||
# 9. Use DNS manual mode:
|
### 9️⃣ Use DNS Manual Mode
|
||||||
|
|
||||||
See: https://github.com/acmesh-official/acme.sh/wiki/dns-manual-mode first.
|
See: https://github.com/acmesh-official/acme.sh/wiki/dns-manual-mode first.
|
||||||
|
|
||||||
@@ -351,70 +386,74 @@ Then just rerun with `renew` argument:
|
|||||||
acme.sh --renew -d example.com
|
acme.sh --renew -d example.com
|
||||||
```
|
```
|
||||||
|
|
||||||
Ok, it's done.
|
✅ **Done!**
|
||||||
|
|
||||||
**Take care, this is dns manual mode, it can not be renewed automatically. you will have to add a new txt record to your domain by your hand when you renew your cert.**
|
> ⚠️ **WARNING:** This is DNS manual mode — it **cannot** be renewed automatically. You will have to add a new TXT record to your domain manually when you renew your cert. **Please use DNS API mode instead.**
|
||||||
|
|
||||||
**Please use dns api mode instead.**
|
---
|
||||||
|
|
||||||
# 10. Issue ECC certificates
|
### 🔟 Issue Certificates of Different Key Types (ECC or RSA)
|
||||||
|
|
||||||
Just set the `keylength` parameter with a prefix `ec-`.
|
Just set the `keylength` to a valid, supported value.
|
||||||
|
|
||||||
For example:
|
**Valid values for the `keylength` parameter:**
|
||||||
|
|
||||||
### Single domain ECC certificate
|
| Key Length | Description |
|
||||||
|
|------------|-------------|
|
||||||
|
| `ec-256` | prime256v1, "ECDSA P-256" ⭐ **Default** |
|
||||||
|
| `ec-384` | secp384r1, "ECDSA P-384" |
|
||||||
|
| `ec-521` | secp521r1, "ECDSA P-521" ⚠️ Not supported by Let's Encrypt yet |
|
||||||
|
| `2048` | RSA 2048-bit |
|
||||||
|
| `3072` | RSA 3072-bit |
|
||||||
|
| `4096` | RSA 4096-bit |
|
||||||
|
|
||||||
|
**Examples:**
|
||||||
|
|
||||||
|
#### Single domain with ECDSA P-384 certificate
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
acme.sh --issue -w /home/wwwroot/example.com -d example.com --keylength ec-256
|
acme.sh --issue -w /home/wwwroot/example.com -d example.com --keylength ec-384
|
||||||
```
|
```
|
||||||
|
|
||||||
### SAN multi domain ECC certificate
|
#### SAN multi domain with RSA4096 certificate
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
acme.sh --issue -w /home/wwwroot/example.com -d example.com -d www.example.com --keylength ec-256
|
acme.sh --issue -w /home/wwwroot/example.com -d example.com -d www.example.com --keylength 4096
|
||||||
```
|
```
|
||||||
|
|
||||||
Please look at the `keylength` parameter above.
|
---
|
||||||
|
|
||||||
Valid values are:
|
### 1️⃣1️⃣ Issue Wildcard Certificates
|
||||||
|
|
||||||
1. **ec-256 (prime256v1, "ECDSA P-256", which is the default key type)**
|
It's simple! Just give a wildcard domain as the `-d` parameter:
|
||||||
2. **ec-384 (secp384r1, "ECDSA P-384")**
|
|
||||||
3. **ec-521 (secp521r1, "ECDSA P-521", which is not supported by Let's Encrypt yet.)**
|
|
||||||
4. **2048 (RSA2048)**
|
|
||||||
5. **3072 (RSA3072)**
|
|
||||||
6. **4096 (RSA4096)**
|
|
||||||
|
|
||||||
|
|
||||||
# 11. Issue Wildcard certificates
|
|
||||||
|
|
||||||
It's simple, just give a wildcard domain as the `-d` parameter.
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
acme.sh --issue -d example.com -d '*.example.com' --dns dns_cf
|
acme.sh --issue -d example.com -d '*.example.com' --dns dns_cf
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# 12. How to renew the certs
|
---
|
||||||
|
|
||||||
No, you don't need to renew the certs manually. All the certs will be renewed automatically every **60** days.
|
### 1️⃣2️⃣ How to Renew Certificates
|
||||||
|
|
||||||
However, you can also force to renew a cert:
|
> 🔄 No need to renew manually! All certs will be renewed automatically every **30** days.
|
||||||
|
|
||||||
|
However, you can force a renewal:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
acme.sh --renew -d example.com --force
|
acme.sh --renew -d example.com --force
|
||||||
```
|
```
|
||||||
|
|
||||||
or, for ECC cert:
|
**For ECC cert:**
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
acme.sh --renew -d example.com --force --ecc
|
acme.sh --renew -d example.com --force --ecc
|
||||||
```
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
# 13. How to stop cert renewal
|
### 1️⃣3️⃣ How to Stop Certificate Renewal
|
||||||
|
|
||||||
To stop renewal of a cert, you can execute the following to remove the cert from the renewal list:
|
To stop renewal of a cert, you can execute the following to remove the cert from the renewal list:
|
||||||
|
|
||||||
@@ -424,73 +463,78 @@ acme.sh --remove -d example.com [--ecc]
|
|||||||
|
|
||||||
The cert/key file is not removed from the disk.
|
The cert/key file is not removed from the disk.
|
||||||
|
|
||||||
You can remove the respective directory (e.g. `~/.acme.sh/example.com`) by yourself.
|
> 💡 You can remove the respective directory (e.g. `~/.acme.sh/example.com`) manually.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
# 14. How to upgrade `acme.sh`
|
### 1️⃣4️⃣ How to Upgrade acme.sh
|
||||||
|
|
||||||
acme.sh is in constant development, so it's strongly recommended to use the latest code.
|
> 🚀 acme.sh is in constant development — it's strongly recommended to use the latest code.
|
||||||
|
|
||||||
You can update acme.sh to the latest code:
|
**Update to latest:**
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
acme.sh --upgrade
|
acme.sh --upgrade
|
||||||
```
|
```
|
||||||
|
|
||||||
You can also enable auto upgrade:
|
**Enable auto upgrade:**
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
acme.sh --upgrade --auto-upgrade
|
acme.sh --upgrade --auto-upgrade
|
||||||
```
|
```
|
||||||
|
|
||||||
Then **acme.sh** will be kept up to date automatically.
|
**Disable auto upgrade:**
|
||||||
|
|
||||||
Disable auto upgrade:
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
acme.sh --upgrade --auto-upgrade 0
|
acme.sh --upgrade --auto-upgrade 0
|
||||||
```
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
# 15. Issue a cert from an existing CSR
|
### 1️⃣5️⃣ Issue a Certificate from an Existing CSR
|
||||||
|
|
||||||
https://github.com/acmesh-official/acme.sh/wiki/Issue-a-cert-from-existing-CSR
|
📚 https://github.com/acmesh-official/acme.sh/wiki/Issue-a-cert-from-existing-CSR
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
# 16. Send notifications in cronjob
|
### 1️⃣6️⃣ Send Notifications in Cronjob
|
||||||
|
|
||||||
https://github.com/acmesh-official/acme.sh/wiki/notify
|
📚 https://github.com/acmesh-official/acme.sh/wiki/notify
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
# 17. Under the Hood
|
### 1️⃣7️⃣ Under the Hood
|
||||||
|
|
||||||
Speak ACME language using shell, directly to "Let's Encrypt".
|
> 🔧 Speak ACME language using shell, directly to "Let's Encrypt".
|
||||||
|
|
||||||
TODO:
|
---
|
||||||
|
|
||||||
|
### 1️⃣8️⃣ Acknowledgments
|
||||||
|
|
||||||
# 18. Acknowledgments
|
| Project | Link |
|
||||||
|
|---------|------|
|
||||||
|
| 🙏 Acme-tiny | https://github.com/diafygi/acme-tiny |
|
||||||
|
| 📜 ACME protocol | https://github.com/ietf-wg-acme/acme |
|
||||||
|
|
||||||
1. Acme-tiny: https://github.com/diafygi/acme-tiny
|
---
|
||||||
2. ACME protocol: https://github.com/ietf-wg-acme/acme
|
|
||||||
|
|
||||||
|
## 👥 Contributors
|
||||||
|
|
||||||
## Contributors
|
### 💻 Code Contributors
|
||||||
|
|
||||||
### Code Contributors
|
|
||||||
|
|
||||||
This project exists thanks to all the people who contribute.
|
This project exists thanks to all the people who contribute.
|
||||||
|
|
||||||
<a href="https://github.com/acmesh-official/acme.sh/graphs/contributors"><img src="https://opencollective.com/acmesh/contributors.svg?width=890&button=false" /></a>
|
<a href="https://github.com/acmesh-official/acme.sh/graphs/contributors"><img src="https://opencollective.com/acmesh/contributors.svg?width=890&button=false" /></a>
|
||||||
|
|
||||||
### Financial Contributors
|
### 💰 Financial Contributors
|
||||||
|
|
||||||
Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/acmesh/contribute)]
|
Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/acmesh/contribute)]
|
||||||
|
|
||||||
#### Individuals
|
#### 👤 Individuals
|
||||||
|
|
||||||
<a href="https://opencollective.com/acmesh"><img src="https://opencollective.com/acmesh/individuals.svg?width=890"></a>
|
<a href="https://opencollective.com/acmesh"><img src="https://opencollective.com/acmesh/individuals.svg?width=890"></a>
|
||||||
|
|
||||||
#### Organizations
|
#### 🏢 Organizations
|
||||||
|
|
||||||
Support this project with your organization. Your logo will show up here with a link to your website. [[Contribute](https://opencollective.com/acmesh/contribute)]
|
Support this project with your organization. Your logo will show up here with a link to your website. [[Contribute](https://opencollective.com/acmesh/contribute)]
|
||||||
|
|
||||||
@@ -505,24 +549,43 @@ Support this project with your organization. Your logo will show up here with a
|
|||||||
<a href="https://opencollective.com/acmesh/organization/8/website"><img src="https://opencollective.com/acmesh/organization/8/avatar.svg"></a>
|
<a href="https://opencollective.com/acmesh/organization/8/website"><img src="https://opencollective.com/acmesh/organization/8/avatar.svg"></a>
|
||||||
<a href="https://opencollective.com/acmesh/organization/9/website"><img src="https://opencollective.com/acmesh/organization/9/avatar.svg"></a>
|
<a href="https://opencollective.com/acmesh/organization/9/website"><img src="https://opencollective.com/acmesh/organization/9/avatar.svg"></a>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
#### Sponsors
|
### 1️⃣9️⃣ License & Others
|
||||||
|
|
||||||
[](https://www.quantumca.com.cn/?__utm_source=acmesh-donation)
|
📄 **License:** GPLv3
|
||||||
|
|
||||||
|
⭐ Please **Star** and **Fork** this project!
|
||||||
|
|
||||||
# 19. License & Others
|
🐛 [Issues](https://github.com/acmesh-official/acme.sh/issues) and 🔀 [Pull Requests](https://github.com/acmesh-official/acme.sh/pulls) are welcome.
|
||||||
|
|
||||||
License is GPLv3
|
---
|
||||||
|
|
||||||
Please Star and Fork me.
|
### 2️⃣0️⃣ Donate
|
||||||
|
|
||||||
[Issues](https://github.com/acmesh-official/acme.sh/issues) and [pull requests](https://github.com/acmesh-official/acme.sh/pulls) are welcome.
|
> 💝 Your donation makes **acme.sh** better!
|
||||||
|
|
||||||
|
| Method | Link |
|
||||||
|
|--------|------|
|
||||||
|
| PayPal / Alipay(支付宝) / Wechat(微信) | [https://donate.acme.sh/](https://donate.acme.sh/) |
|
||||||
|
|
||||||
# 20. Donate
|
📜 [Donate List](https://github.com/acmesh-official/acme.sh/wiki/Donate-list)
|
||||||
Your donation makes **acme.sh** better:
|
|
||||||
|
|
||||||
1. PayPal/Alipay(支付宝)/Wechat(微信): [https://donate.acme.sh/](https://donate.acme.sh/)
|
---
|
||||||
|
|
||||||
[Donate List](https://github.com/acmesh-official/acme.sh/wiki/Donate-list)
|
### 2️⃣1️⃣ About This Repository
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> This repository is officially maintained by <strong>ZeroSSL</strong> as part of our commitment to providing secure and reliable SSL/TLS solutions. We welcome contributions and feedback from the community!
|
||||||
|
> For more information about our services, including free and paid SSL/TLS certificates, visit https://zerossl.com.
|
||||||
|
>
|
||||||
|
> All donations made through this repository go directly to the original independent maintainer (Neil Pang), not to ZeroSSL.
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://zerossl.com">
|
||||||
|
<picture>
|
||||||
|
<source media="(prefers-color-scheme: dark)" srcset="https://zerossl.com/assets/images/zerossl_logo_white.svg">
|
||||||
|
<source media="(prefers-color-scheme: light)" srcset="https://zerossl.com/assets/images/zerossl_logo.svg">
|
||||||
|
<img src="https://zerossl.com/assets/images/zerossl_logo.svg" alt="ZeroSSL" width="256">
|
||||||
|
</picture>
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
|||||||
88
deploy/ali_cdn.sh
Normal file
88
deploy/ali_cdn.sh
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034,SC2154
|
||||||
|
|
||||||
|
# Script to create certificate to Alibaba Cloud CDN
|
||||||
|
#
|
||||||
|
# Docs: https://github.com/acmesh-official/acme.sh/wiki/deployhooks#33-deploy-your-certificate-to-cdn-or-dcdn-of-alibaba-cloud-aliyun
|
||||||
|
#
|
||||||
|
# This deployment required following variables
|
||||||
|
# export Ali_Key="ALIACCESSKEY"
|
||||||
|
# export Ali_Secret="ALISECRETKEY"
|
||||||
|
# The credentials are shared with all the Alibaba Cloud deploy hooks and dnsapi
|
||||||
|
#
|
||||||
|
# To specify the CDN domain that is different from the certificate CN, usually used for multi-domain or wildcard certificates
|
||||||
|
# export DEPLOY_ALI_CDN_DOMAIN="cdn.example.com"
|
||||||
|
# If you have multiple CDN domains using the same certificate, just
|
||||||
|
# export DEPLOY_ALI_CDN_DOMAIN="cdn1.example.com cdn2.example.com"
|
||||||
|
#
|
||||||
|
# For DCDN, see ali_dcdn deploy hook
|
||||||
|
|
||||||
|
Ali_CDN_API="https://cdn.aliyuncs.com/"
|
||||||
|
|
||||||
|
ali_cdn_deploy() {
|
||||||
|
_cdomain="$1"
|
||||||
|
_ckey="$2"
|
||||||
|
_ccert="$3"
|
||||||
|
_cca="$4"
|
||||||
|
_cfullchain="$5"
|
||||||
|
|
||||||
|
_debug _cdomain "$_cdomain"
|
||||||
|
_debug _ckey "$_ckey"
|
||||||
|
_debug _ccert "$_ccert"
|
||||||
|
_debug _cca "$_cca"
|
||||||
|
_debug _cfullchain "$_cfullchain"
|
||||||
|
|
||||||
|
# Load dnsapi/dns_ali.sh to reduce the duplicated codes
|
||||||
|
# https://github.com/acmesh-official/acme.sh/pull/5205#issuecomment-2357867276
|
||||||
|
dnsapi_ali="$(_findHook "$_cdomain" "$_SUB_FOLDER_DNSAPI" dns_ali)"
|
||||||
|
# shellcheck source=/dev/null
|
||||||
|
if ! . "$dnsapi_ali"; then
|
||||||
|
_err "Error loading file $dnsapi_ali. Please check your API file and try again."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_prepare_ali_credentials || return 1
|
||||||
|
|
||||||
|
_getdeployconf DEPLOY_ALI_CDN_DOMAIN
|
||||||
|
if [ "$DEPLOY_ALI_CDN_DOMAIN" ]; then
|
||||||
|
_savedeployconf DEPLOY_ALI_CDN_DOMAIN "$DEPLOY_ALI_CDN_DOMAIN"
|
||||||
|
else
|
||||||
|
DEPLOY_ALI_CDN_DOMAIN="$_cdomain"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# read cert and key files and urlencode both
|
||||||
|
_cert=$(_url_encode upper-hex <"$_cfullchain")
|
||||||
|
_key=$(_url_encode upper-hex <"$_ckey")
|
||||||
|
|
||||||
|
_debug2 _cert "$_cert"
|
||||||
|
_debug2 _key "$_key"
|
||||||
|
|
||||||
|
## update domain ssl config
|
||||||
|
for domain in $DEPLOY_ALI_CDN_DOMAIN; do
|
||||||
|
_set_cdn_domain_ssl_certificate_query "$domain" "$_cert" "$_key"
|
||||||
|
if _ali_rest "Set CDN domain SSL certificate for $domain" "" POST; then
|
||||||
|
_info "Domain $domain certificate has been deployed successfully"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# domain pub pri
|
||||||
|
_set_cdn_domain_ssl_certificate_query() {
|
||||||
|
endpoint=$Ali_CDN_API
|
||||||
|
query=''
|
||||||
|
query=$query'AccessKeyId='$Ali_Key
|
||||||
|
query=$query'&Action=SetCdnDomainSSLCertificate'
|
||||||
|
query=$query'&CertType=upload'
|
||||||
|
query=$query'&DomainName='$1
|
||||||
|
query=$query'&Format=json'
|
||||||
|
query=$query'&SSLPri='$3
|
||||||
|
query=$query'&SSLProtocol=on'
|
||||||
|
query=$query'&SSLPub='$2
|
||||||
|
query=$query'&SignatureMethod=HMAC-SHA1'
|
||||||
|
query=$query"&SignatureNonce=$(_ali_nonce)"
|
||||||
|
query=$query'&SignatureVersion=1.0'
|
||||||
|
query=$query'&Timestamp='$(_ali_timestamp)
|
||||||
|
query=$query'&Version=2018-05-10'
|
||||||
|
}
|
||||||
88
deploy/ali_dcdn.sh
Normal file
88
deploy/ali_dcdn.sh
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034,SC2154
|
||||||
|
|
||||||
|
# Script to create certificate to Alibaba Cloud DCDN
|
||||||
|
#
|
||||||
|
# Docs: https://github.com/acmesh-official/acme.sh/wiki/deployhooks#33-deploy-your-certificate-to-cdn-or-dcdn-of-alibaba-cloud-aliyun
|
||||||
|
#
|
||||||
|
# This deployment required following variables
|
||||||
|
# export Ali_Key="ALIACCESSKEY"
|
||||||
|
# export Ali_Secret="ALISECRETKEY"
|
||||||
|
# The credentials are shared with all the Alibaba Cloud deploy hooks and dnsapi
|
||||||
|
#
|
||||||
|
# To specify the DCDN domain that is different from the certificate CN, usually used for multi-domain or wildcard certificates
|
||||||
|
# export DEPLOY_ALI_DCDN_DOMAIN="dcdn.example.com"
|
||||||
|
# If you have multiple CDN domains using the same certificate, just
|
||||||
|
# export DEPLOY_ALI_DCDN_DOMAIN="dcdn1.example.com dcdn2.example.com"
|
||||||
|
#
|
||||||
|
# For regular CDN, see ali_cdn deploy hook
|
||||||
|
|
||||||
|
Ali_DCDN_API="https://dcdn.aliyuncs.com/"
|
||||||
|
|
||||||
|
ali_dcdn_deploy() {
|
||||||
|
_cdomain="$1"
|
||||||
|
_ckey="$2"
|
||||||
|
_ccert="$3"
|
||||||
|
_cca="$4"
|
||||||
|
_cfullchain="$5"
|
||||||
|
|
||||||
|
_debug _cdomain "$_cdomain"
|
||||||
|
_debug _ckey "$_ckey"
|
||||||
|
_debug _ccert "$_ccert"
|
||||||
|
_debug _cca "$_cca"
|
||||||
|
_debug _cfullchain "$_cfullchain"
|
||||||
|
|
||||||
|
# Load dnsapi/dns_ali.sh to reduce the duplicated codes
|
||||||
|
# https://github.com/acmesh-official/acme.sh/pull/5205#issuecomment-2357867276
|
||||||
|
dnsapi_ali="$(_findHook "$_cdomain" "$_SUB_FOLDER_DNSAPI" dns_ali)"
|
||||||
|
# shellcheck source=/dev/null
|
||||||
|
if ! . "$dnsapi_ali"; then
|
||||||
|
_err "Error loading file $dnsapi_ali. Please check your API file and try again."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_prepare_ali_credentials || return 1
|
||||||
|
|
||||||
|
_getdeployconf DEPLOY_ALI_DCDN_DOMAIN
|
||||||
|
if [ "$DEPLOY_ALI_DCDN_DOMAIN" ]; then
|
||||||
|
_savedeployconf DEPLOY_ALI_DCDN_DOMAIN "$DEPLOY_ALI_DCDN_DOMAIN"
|
||||||
|
else
|
||||||
|
DEPLOY_ALI_DCDN_DOMAIN="$_cdomain"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# read cert and key files and urlencode both
|
||||||
|
_cert=$(_url_encode upper-hex <"$_cfullchain")
|
||||||
|
_key=$(_url_encode upper-hex <"$_ckey")
|
||||||
|
|
||||||
|
_debug2 _cert "$_cert"
|
||||||
|
_debug2 _key "$_key"
|
||||||
|
|
||||||
|
## update domain ssl config
|
||||||
|
for domain in $DEPLOY_ALI_DCDN_DOMAIN; do
|
||||||
|
_set_dcdn_domain_ssl_certificate_query "$domain" "$_cert" "$_key"
|
||||||
|
if _ali_rest "Set DCDN domain SSL certificate for $domain" "" POST; then
|
||||||
|
_info "Domain $domain certificate has been deployed successfully"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# domain pub pri
|
||||||
|
_set_dcdn_domain_ssl_certificate_query() {
|
||||||
|
endpoint=$Ali_DCDN_API
|
||||||
|
query=''
|
||||||
|
query=$query'AccessKeyId='$Ali_Key
|
||||||
|
query=$query'&Action=SetDcdnDomainSSLCertificate'
|
||||||
|
query=$query'&CertType=upload'
|
||||||
|
query=$query'&DomainName='$1
|
||||||
|
query=$query'&Format=json'
|
||||||
|
query=$query'&SSLPri='$3
|
||||||
|
query=$query'&SSLProtocol=on'
|
||||||
|
query=$query'&SSLPub='$2
|
||||||
|
query=$query'&SignatureMethod=HMAC-SHA1'
|
||||||
|
query=$query"&SignatureNonce=$(_ali_nonce)"
|
||||||
|
query=$query'&SignatureVersion=1.0'
|
||||||
|
query=$query'&Timestamp='$(_ali_timestamp)
|
||||||
|
query=$query'&Version=2018-01-15'
|
||||||
|
}
|
||||||
56
deploy/cachefly.sh
Normal file
56
deploy/cachefly.sh
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Script to deploy certificate to CacheFly
|
||||||
|
# https://api.cachefly.com/api/2.5/docs#tag/Certificates/paths/~1certificates/post
|
||||||
|
|
||||||
|
# This deployment required following variables
|
||||||
|
# export CACHEFLY_TOKEN="Your CacheFly API Token"
|
||||||
|
|
||||||
|
# returns 0 means success, otherwise error.
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#domain keyfile certfile cafile fullchain
|
||||||
|
CACHEFLY_API_BASE="https://api.cachefly.com/api/2.5"
|
||||||
|
|
||||||
|
cachefly_deploy() {
|
||||||
|
_cdomain="$1"
|
||||||
|
_ckey="$2"
|
||||||
|
_ccert="$3"
|
||||||
|
_cca="$4"
|
||||||
|
_cfullchain="$5"
|
||||||
|
|
||||||
|
_debug _cdomain "$_cdomain"
|
||||||
|
_debug _ckey "$_ckey"
|
||||||
|
_debug _ccert "$_ccert"
|
||||||
|
_debug _cca "$_cca"
|
||||||
|
_debug _cfullchain "$_cfullchain"
|
||||||
|
|
||||||
|
if [ -z "$CACHEFLY_TOKEN" ]; then
|
||||||
|
_err "CACHEFLY_TOKEN is not defined."
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
_savedomainconf CACHEFLY_TOKEN "$CACHEFLY_TOKEN"
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Deploying certificate to CacheFly..."
|
||||||
|
|
||||||
|
## upload certificate
|
||||||
|
string_fullchain=$(sed 's/$/\\n/' "$_cfullchain" | tr -d '\n')
|
||||||
|
string_key=$(sed 's/$/\\n/' "$_ckey" | tr -d '\n')
|
||||||
|
|
||||||
|
_request_body="{\"certificate\":\"$string_fullchain\",\"certificateKey\":\"$string_key\"}"
|
||||||
|
_debug _request_body "$_request_body"
|
||||||
|
_debug CACHEFLY_TOKEN "$CACHEFLY_TOKEN"
|
||||||
|
export _H1="Authorization: Bearer $CACHEFLY_TOKEN"
|
||||||
|
_response=$(_post "$_request_body" "$CACHEFLY_API_BASE/certificates" "" "POST" "application/json")
|
||||||
|
|
||||||
|
if _contains "$_response" "message"; then
|
||||||
|
_err "Error in deploying $_cdomain certificate to CacheFly."
|
||||||
|
_err "$_response"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug response "$_response"
|
||||||
|
_info "Domain $_cdomain certificate successfully deployed to CacheFly."
|
||||||
|
return 0
|
||||||
|
}
|
||||||
86
deploy/directadmin.sh
Normal file
86
deploy/directadmin.sh
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Script to deploy certificate to DirectAdmin
|
||||||
|
# https://docs.directadmin.com/directadmin/customizing-workflow/api-all-about.html#creating-a-login-key
|
||||||
|
# https://docs.directadmin.com/changelog/version-1.24.4.html#cmd-api-catch-all-pop-passwords-frontpage-protected-dirs-ssl-certs
|
||||||
|
|
||||||
|
# This deployment required following variables
|
||||||
|
# export DirectAdmin_SCHEME="https" # Optional, https or http, defaults to https
|
||||||
|
# export DirectAdmin_ENDPOINT="example.com:2222"
|
||||||
|
# export DirectAdmin_USERNAME="Your DirectAdmin Username"
|
||||||
|
# export DirectAdmin_KEY="Your DirectAdmin Login Key or Password"
|
||||||
|
# export DirectAdmin_MAIN_DOMAIN="Your DirectAdmin Main Domain, NOT Subdomain"
|
||||||
|
|
||||||
|
# returns 0 means success, otherwise error.
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#domain keyfile certfile cafile fullchain
|
||||||
|
directadmin_deploy() {
|
||||||
|
_cdomain="$1"
|
||||||
|
_ckey="$2"
|
||||||
|
_ccert="$3"
|
||||||
|
_cca="$4"
|
||||||
|
_cfullchain="$5"
|
||||||
|
|
||||||
|
_debug _cdomain "$_cdomain"
|
||||||
|
_debug _ckey "$_ckey"
|
||||||
|
_debug _ccert "$_ccert"
|
||||||
|
_debug _cca "$_cca"
|
||||||
|
_debug _cfullchain "$_cfullchain"
|
||||||
|
|
||||||
|
if [ -z "$DirectAdmin_ENDPOINT" ]; then
|
||||||
|
_err "DirectAdmin_ENDPOINT is not defined."
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
_savedomainconf DirectAdmin_ENDPOINT "$DirectAdmin_ENDPOINT"
|
||||||
|
fi
|
||||||
|
if [ -z "$DirectAdmin_USERNAME" ]; then
|
||||||
|
_err "DirectAdmin_USERNAME is not defined."
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
_savedomainconf DirectAdmin_USERNAME "$DirectAdmin_USERNAME"
|
||||||
|
fi
|
||||||
|
if [ -z "$DirectAdmin_KEY" ]; then
|
||||||
|
_err "DirectAdmin_KEY is not defined."
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
_savedomainconf DirectAdmin_KEY "$DirectAdmin_KEY"
|
||||||
|
fi
|
||||||
|
if [ -z "$DirectAdmin_MAIN_DOMAIN" ]; then
|
||||||
|
_err "DirectAdmin_MAIN_DOMAIN is not defined."
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
_savedomainconf DirectAdmin_MAIN_DOMAIN "$DirectAdmin_MAIN_DOMAIN"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Optional SCHEME
|
||||||
|
_getdeployconf DirectAdmin_SCHEME
|
||||||
|
# set default values for DirectAdmin_SCHEME
|
||||||
|
[ -n "${DirectAdmin_SCHEME}" ] || DirectAdmin_SCHEME="https"
|
||||||
|
|
||||||
|
_info "Deploying certificate to DirectAdmin..."
|
||||||
|
|
||||||
|
# upload certificate
|
||||||
|
string_cfullchain=$(sed 's/$/\\n/' "$_cfullchain" | tr -d '\n')
|
||||||
|
string_key=$(sed 's/$/\\n/' "$_ckey" | tr -d '\n')
|
||||||
|
|
||||||
|
_request_body="{\"domain\":\"$DirectAdmin_MAIN_DOMAIN\",\"action\":\"save\",\"type\":\"paste\",\"certificate\":\"$string_key\n$string_cfullchain\n\"}"
|
||||||
|
_debug _request_body "$_request_body"
|
||||||
|
_debug DirectAdmin_ENDPOINT "$DirectAdmin_ENDPOINT"
|
||||||
|
_debug DirectAdmin_USERNAME "$DirectAdmin_USERNAME"
|
||||||
|
_debug DirectAdmin_KEY "$DirectAdmin_KEY"
|
||||||
|
_debug DirectAdmin_MAIN_DOMAIN "$DirectAdmin_MAIN_DOMAIN"
|
||||||
|
_response=$(_post "$_request_body" "$DirectAdmin_SCHEME://$DirectAdmin_USERNAME:$DirectAdmin_KEY@$DirectAdmin_ENDPOINT/CMD_API_SSL" "" "POST" "application/json")
|
||||||
|
|
||||||
|
if _contains "$_response" "error=1"; then
|
||||||
|
_err "Error in deploying $_cdomain certificate to DirectAdmin Domain $DirectAdmin_MAIN_DOMAIN."
|
||||||
|
_err "$_response"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "$_response"
|
||||||
|
_info "Domain $_cdomain certificate successfully deployed to DirectAdmin Domain $DirectAdmin_MAIN_DOMAIN."
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
@@ -18,6 +18,7 @@ docker_deploy() {
|
|||||||
_ccert="$3"
|
_ccert="$3"
|
||||||
_cca="$4"
|
_cca="$4"
|
||||||
_cfullchain="$5"
|
_cfullchain="$5"
|
||||||
|
_cpfx="$6"
|
||||||
_debug _cdomain "$_cdomain"
|
_debug _cdomain "$_cdomain"
|
||||||
_getdeployconf DEPLOY_DOCKER_CONTAINER_LABEL
|
_getdeployconf DEPLOY_DOCKER_CONTAINER_LABEL
|
||||||
_debug2 DEPLOY_DOCKER_CONTAINER_LABEL "$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"
|
_savedeployconf DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE "$DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE"
|
||||||
fi
|
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
|
_getdeployconf DEPLOY_DOCKER_CONTAINER_RELOAD_CMD
|
||||||
_debug2 DEPLOY_DOCKER_CONTAINER_RELOAD_CMD "$DEPLOY_DOCKER_CONTAINER_RELOAD_CMD"
|
_debug2 DEPLOY_DOCKER_CONTAINER_RELOAD_CMD "$DEPLOY_DOCKER_CONTAINER_RELOAD_CMD"
|
||||||
if [ "$DEPLOY_DOCKER_CONTAINER_RELOAD_CMD" ]; then
|
if [ "$DEPLOY_DOCKER_CONTAINER_RELOAD_CMD" ]; then
|
||||||
@@ -125,6 +132,12 @@ docker_deploy() {
|
|||||||
fi
|
fi
|
||||||
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
|
if [ "$DEPLOY_DOCKER_CONTAINER_RELOAD_CMD" ]; then
|
||||||
_info "Reloading: $DEPLOY_DOCKER_CONTAINER_RELOAD_CMD"
|
_info "Reloading: $DEPLOY_DOCKER_CONTAINER_RELOAD_CMD"
|
||||||
if ! _docker_exec "$_cid" "$DEPLOY_DOCKER_CONTAINER_RELOAD_CMD"; then
|
if ! _docker_exec "$_cid" "$DEPLOY_DOCKER_CONTAINER_RELOAD_CMD"; then
|
||||||
|
|||||||
86
deploy/edgio.sh
Normal file
86
deploy/edgio.sh
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Here is a script to deploy cert to edgio using its API
|
||||||
|
# https://docs.edg.io/guides/v7/develop/rest_api/authentication
|
||||||
|
# https://docs.edg.io/rest_api/#tag/tls-certs/operation/postConfigV01TlsCerts
|
||||||
|
|
||||||
|
# This deployment required following variables
|
||||||
|
# export EDGIO_CLIENT_ID="Your Edgio Client ID"
|
||||||
|
# export EDGIO_CLIENT_SECRET="Your Edgio Client Secret"
|
||||||
|
# export EDGIO_ENVIRONMENT_ID="Your Edgio Environment ID"
|
||||||
|
|
||||||
|
# If have more than one Environment ID
|
||||||
|
# export EDGIO_ENVIRONMENT_ID="ENVIRONMENT_ID_1 ENVIRONMENT_ID_2"
|
||||||
|
|
||||||
|
# returns 0 means success, otherwise error.
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#domain keyfile certfile cafile fullchain
|
||||||
|
edgio_deploy() {
|
||||||
|
_cdomain="$1"
|
||||||
|
_ckey="$2"
|
||||||
|
_ccert="$3"
|
||||||
|
_cca="$4"
|
||||||
|
_cfullchain="$5"
|
||||||
|
|
||||||
|
_debug _cdomain "$_cdomain"
|
||||||
|
_debug _ckey "$_ckey"
|
||||||
|
_debug _ccert "$_ccert"
|
||||||
|
_debug _cca "$_cca"
|
||||||
|
_debug _cfullchain "$_cfullchain"
|
||||||
|
|
||||||
|
if [ -z "$EDGIO_CLIENT_ID" ]; then
|
||||||
|
_err "EDGIO_CLIENT_ID is not defined."
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
_savedomainconf EDGIO_CLIENT_ID "$EDGIO_CLIENT_ID"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$EDGIO_CLIENT_SECRET" ]; then
|
||||||
|
_err "EDGIO_CLIENT_SECRET is not defined."
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
_savedomainconf EDGIO_CLIENT_SECRET "$EDGIO_CLIENT_SECRET"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$EDGIO_ENVIRONMENT_ID" ]; then
|
||||||
|
_err "EDGIO_ENVIRONMENT_ID is not defined."
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
_savedomainconf EDGIO_ENVIRONMENT_ID "$EDGIO_ENVIRONMENT_ID"
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Getting access token"
|
||||||
|
_data="client_id=$EDGIO_CLIENT_ID&client_secret=$EDGIO_CLIENT_SECRET&grant_type=client_credentials&scope=app.config"
|
||||||
|
_debug Get_access_token_data "$_data"
|
||||||
|
_response=$(_post "$_data" "https://id.edgio.app/connect/token" "" "POST" "application/x-www-form-urlencoded")
|
||||||
|
_debug Get_access_token_response "$_response"
|
||||||
|
_access_token=$(echo "$_response" | _json_decode | _egrep_o '"access_token":"[^"]*' | cut -d : -f 2 | tr -d '"')
|
||||||
|
_debug _access_token "$_access_token"
|
||||||
|
if [ -z "$_access_token" ]; then
|
||||||
|
_err "Error in getting access token"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Uploading certificate"
|
||||||
|
string_ccert=$(sed 's/$/\\n/' "$_ccert" | tr -d '\n')
|
||||||
|
string_cca=$(sed 's/$/\\n/' "$_cca" | tr -d '\n')
|
||||||
|
string_key=$(sed 's/$/\\n/' "$_ckey" | tr -d '\n')
|
||||||
|
|
||||||
|
for ENVIRONMENT_ID in $EDGIO_ENVIRONMENT_ID; do
|
||||||
|
_data="{\"environment_id\":\"$ENVIRONMENT_ID\",\"primary_cert\":\"$string_ccert\",\"intermediate_cert\":\"$string_cca\",\"private_key\":\"$string_key\"}"
|
||||||
|
_debug Upload_certificate_data "$_data"
|
||||||
|
_H1="Authorization: Bearer $_access_token"
|
||||||
|
_response=$(_post "$_data" "https://edgioapis.com/config/v0.1/tls-certs" "" "POST" "application/json")
|
||||||
|
if _contains "$_response" "message"; then
|
||||||
|
_err "Error in deploying $_cdomain certificate to Edgio ENVIRONMENT_ID $ENVIRONMENT_ID."
|
||||||
|
_err "$_response"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug Upload_certificate_response "$_response"
|
||||||
|
_info "Domain $_cdomain certificate successfully deployed to Edgio ENVIRONMENT_ID $ENVIRONMENT_ID."
|
||||||
|
done
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
@@ -109,6 +109,5 @@ exim4_deploy() {
|
|||||||
fi
|
fi
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
return 0
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,19 @@
|
|||||||
# Note: This functionality requires HAProxy was compiled against
|
# Note: This functionality requires HAProxy was compiled against
|
||||||
# a version of OpenSSL that supports this.
|
# a version of OpenSSL that supports this.
|
||||||
#
|
#
|
||||||
|
# export DEPLOY_HAPROXY_HOT_UPDATE="yes"
|
||||||
|
# export DEPLOY_HAPROXY_STATS_SOCKET="UNIX:/run/haproxy/admin.sock"
|
||||||
|
#
|
||||||
|
# OPTIONAL: Deploy the certificate over the HAProxy stats socket without
|
||||||
|
# needing to reload HAProxy. Default is "no".
|
||||||
|
#
|
||||||
|
# Require the socat binary. DEPLOY_HAPROXY_STATS_SOCKET variable uses the socat
|
||||||
|
# address format.
|
||||||
|
#
|
||||||
|
# export DEPLOY_HAPROXY_MASTER_CLI="UNIX:/run/haproxy-master.sock"
|
||||||
|
#
|
||||||
|
# OPTIONAL: To use the master CLI with DEPLOY_HAPROXY_HOT_UPDATE="yes" instead
|
||||||
|
# of a stats socket, use this variable.
|
||||||
|
|
||||||
######## Public functions #####################
|
######## Public functions #####################
|
||||||
|
|
||||||
@@ -46,6 +59,7 @@ haproxy_deploy() {
|
|||||||
_ccert="$3"
|
_ccert="$3"
|
||||||
_cca="$4"
|
_cca="$4"
|
||||||
_cfullchain="$5"
|
_cfullchain="$5"
|
||||||
|
_cmdpfx=""
|
||||||
|
|
||||||
# Some defaults
|
# Some defaults
|
||||||
DEPLOY_HAPROXY_PEM_PATH_DEFAULT="/etc/haproxy"
|
DEPLOY_HAPROXY_PEM_PATH_DEFAULT="/etc/haproxy"
|
||||||
@@ -53,6 +67,8 @@ haproxy_deploy() {
|
|||||||
DEPLOY_HAPROXY_BUNDLE_DEFAULT="no"
|
DEPLOY_HAPROXY_BUNDLE_DEFAULT="no"
|
||||||
DEPLOY_HAPROXY_ISSUER_DEFAULT="no"
|
DEPLOY_HAPROXY_ISSUER_DEFAULT="no"
|
||||||
DEPLOY_HAPROXY_RELOAD_DEFAULT="true"
|
DEPLOY_HAPROXY_RELOAD_DEFAULT="true"
|
||||||
|
DEPLOY_HAPROXY_HOT_UPDATE_DEFAULT="no"
|
||||||
|
DEPLOY_HAPROXY_STATS_SOCKET_DEFAULT="UNIX:/run/haproxy/admin.sock"
|
||||||
|
|
||||||
_debug _cdomain "${_cdomain}"
|
_debug _cdomain "${_cdomain}"
|
||||||
_debug _ckey "${_ckey}"
|
_debug _ckey "${_ckey}"
|
||||||
@@ -86,6 +102,11 @@ haproxy_deploy() {
|
|||||||
_savedomainconf Le_Deploy_haproxy_pem_name "${Le_Deploy_haproxy_pem_name}"
|
_savedomainconf Le_Deploy_haproxy_pem_name "${Le_Deploy_haproxy_pem_name}"
|
||||||
elif [ -z "${Le_Deploy_haproxy_pem_name}" ]; then
|
elif [ -z "${Le_Deploy_haproxy_pem_name}" ]; then
|
||||||
Le_Deploy_haproxy_pem_name="${DEPLOY_HAPROXY_PEM_NAME_DEFAULT}"
|
Le_Deploy_haproxy_pem_name="${DEPLOY_HAPROXY_PEM_NAME_DEFAULT}"
|
||||||
|
# We better not have '*' as the first character
|
||||||
|
if [ "${Le_Deploy_haproxy_pem_name%%"${Le_Deploy_haproxy_pem_name#?}"}" = '*' ]; then
|
||||||
|
# removes the first characters and add a _ instead
|
||||||
|
Le_Deploy_haproxy_pem_name="_${Le_Deploy_haproxy_pem_name#?}"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# BUNDLE is optional. If not provided then assume "${DEPLOY_HAPROXY_BUNDLE_DEFAULT}"
|
# BUNDLE is optional. If not provided then assume "${DEPLOY_HAPROXY_BUNDLE_DEFAULT}"
|
||||||
@@ -118,6 +139,36 @@ haproxy_deploy() {
|
|||||||
Le_Deploy_haproxy_reload="${DEPLOY_HAPROXY_RELOAD_DEFAULT}"
|
Le_Deploy_haproxy_reload="${DEPLOY_HAPROXY_RELOAD_DEFAULT}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# HOT_UPDATE is optional. If not provided then assume "${DEPLOY_HAPROXY_HOT_UPDATE_DEFAULT}"
|
||||||
|
_getdeployconf DEPLOY_HAPROXY_HOT_UPDATE
|
||||||
|
_debug2 DEPLOY_HAPROXY_HOT_UPDATE "${DEPLOY_HAPROXY_HOT_UPDATE}"
|
||||||
|
if [ -n "${DEPLOY_HAPROXY_HOT_UPDATE}" ]; then
|
||||||
|
Le_Deploy_haproxy_hot_update="${DEPLOY_HAPROXY_HOT_UPDATE}"
|
||||||
|
_savedomainconf Le_Deploy_haproxy_hot_update "${Le_Deploy_haproxy_hot_update}"
|
||||||
|
elif [ -z "${Le_Deploy_haproxy_hot_update}" ]; then
|
||||||
|
Le_Deploy_haproxy_hot_update="${DEPLOY_HAPROXY_HOT_UPDATE_DEFAULT}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# STATS_SOCKET is optional. If not provided then assume "${DEPLOY_HAPROXY_STATS_SOCKET_DEFAULT}"
|
||||||
|
_getdeployconf DEPLOY_HAPROXY_STATS_SOCKET
|
||||||
|
_debug2 DEPLOY_HAPROXY_STATS_SOCKET "${DEPLOY_HAPROXY_STATS_SOCKET}"
|
||||||
|
if [ -n "${DEPLOY_HAPROXY_STATS_SOCKET}" ]; then
|
||||||
|
Le_Deploy_haproxy_stats_socket="${DEPLOY_HAPROXY_STATS_SOCKET}"
|
||||||
|
_savedomainconf Le_Deploy_haproxy_stats_socket "${Le_Deploy_haproxy_stats_socket}"
|
||||||
|
elif [ -z "${Le_Deploy_haproxy_stats_socket}" ]; then
|
||||||
|
Le_Deploy_haproxy_stats_socket="${DEPLOY_HAPROXY_STATS_SOCKET_DEFAULT}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# MASTER_CLI is optional. No defaults are used. When the master CLI is used,
|
||||||
|
# all commands are sent with a prefix.
|
||||||
|
_getdeployconf DEPLOY_HAPROXY_MASTER_CLI
|
||||||
|
_debug2 DEPLOY_HAPROXY_MASTER_CLI "${DEPLOY_HAPROXY_MASTER_CLI}"
|
||||||
|
if [ -n "${DEPLOY_HAPROXY_MASTER_CLI}" ]; then
|
||||||
|
Le_Deploy_haproxy_stats_socket="${DEPLOY_HAPROXY_MASTER_CLI}"
|
||||||
|
_savedomainconf Le_Deploy_haproxy_stats_socket "${Le_Deploy_haproxy_stats_socket}"
|
||||||
|
_cmdpfx="@1 " # command prefix used for master CLI only.
|
||||||
|
fi
|
||||||
|
|
||||||
# Set the suffix depending if we are creating a bundle or not
|
# Set the suffix depending if we are creating a bundle or not
|
||||||
if [ "${Le_Deploy_haproxy_bundle}" = "yes" ]; then
|
if [ "${Le_Deploy_haproxy_bundle}" = "yes" ]; then
|
||||||
_info "Bundle creation requested"
|
_info "Bundle creation requested"
|
||||||
@@ -142,12 +193,13 @@ haproxy_deploy() {
|
|||||||
_issuer="${_pem}.issuer"
|
_issuer="${_pem}.issuer"
|
||||||
_ocsp="${_pem}.ocsp"
|
_ocsp="${_pem}.ocsp"
|
||||||
_reload="${Le_Deploy_haproxy_reload}"
|
_reload="${Le_Deploy_haproxy_reload}"
|
||||||
|
_statssock="${Le_Deploy_haproxy_stats_socket}"
|
||||||
|
|
||||||
_info "Deploying PEM file"
|
_info "Deploying PEM file"
|
||||||
# Create a temporary PEM file
|
# Create a temporary PEM file
|
||||||
_temppem="$(_mktemp)"
|
_temppem="$(_mktemp)"
|
||||||
_debug _temppem "${_temppem}"
|
_debug _temppem "${_temppem}"
|
||||||
cat "${_ckey}" "${_ccert}" "${_cca}" >"${_temppem}"
|
cat "${_ccert}" "${_cca}" "${_ckey}" | grep . >"${_temppem}"
|
||||||
_ret="$?"
|
_ret="$?"
|
||||||
|
|
||||||
# Check that we could create the temporary file
|
# Check that we could create the temporary file
|
||||||
@@ -265,15 +317,86 @@ haproxy_deploy() {
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Reload HAProxy
|
if [ "${Le_Deploy_haproxy_hot_update}" = "yes" ]; then
|
||||||
_debug _reload "${_reload}"
|
# set the socket name for messages
|
||||||
eval "${_reload}"
|
if [ -n "${_cmdpfx}" ]; then
|
||||||
_ret=$?
|
_socketname="master CLI"
|
||||||
if [ "${_ret}" != "0" ]; then
|
else
|
||||||
_err "Error code ${_ret} during reload"
|
_socketname="stats socket"
|
||||||
return ${_ret}
|
fi
|
||||||
|
|
||||||
|
# Update certificate over HAProxy stats socket or master CLI.
|
||||||
|
if _exists socat; then
|
||||||
|
# look for the certificate on the stats socket, to chose between updating or creating one
|
||||||
|
_socat_cert_cmd="echo '${_cmdpfx}show ssl cert' | socat '${_statssock}' - | grep -q '^${_pem}$'"
|
||||||
|
_debug _socat_cert_cmd "${_socat_cert_cmd}"
|
||||||
|
eval "${_socat_cert_cmd}"
|
||||||
|
_ret=$?
|
||||||
|
if [ "${_ret}" != "0" ]; then
|
||||||
|
_newcert="1"
|
||||||
|
_info "Creating new certificate '${_pem}' over HAProxy ${_socketname}."
|
||||||
|
# certificate wasn't found, it's a new one. We should check if the crt-list exists and creates/inserts the certificate.
|
||||||
|
_socat_crtlist_show_cmd="echo '${_cmdpfx}show ssl crt-list' | socat '${_statssock}' - | grep -q '^${Le_Deploy_haproxy_pem_path}$'"
|
||||||
|
_debug _socat_crtlist_show_cmd "${_socat_crtlist_show_cmd}"
|
||||||
|
eval "${_socat_crtlist_show_cmd}"
|
||||||
|
_ret=$?
|
||||||
|
if [ "${_ret}" != "0" ]; then
|
||||||
|
_err "Couldn't find '${Le_Deploy_haproxy_pem_path}' in haproxy 'show ssl crt-list'"
|
||||||
|
return "${_ret}"
|
||||||
|
fi
|
||||||
|
# create a new certificate
|
||||||
|
_socat_new_cmd="echo '${_cmdpfx}new ssl cert ${_pem}' | socat '${_statssock}' - | grep -q 'New empty'"
|
||||||
|
_debug _socat_new_cmd "${_socat_new_cmd}"
|
||||||
|
eval "${_socat_new_cmd}"
|
||||||
|
_ret=$?
|
||||||
|
if [ "${_ret}" != "0" ]; then
|
||||||
|
_err "Couldn't create '${_pem}' in haproxy"
|
||||||
|
return "${_ret}"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
_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'"
|
||||||
|
_secure_debug _socat_cert_set_cmd "${_socat_cert_set_cmd}"
|
||||||
|
eval "${_socat_cert_set_cmd}"
|
||||||
|
_ret=$?
|
||||||
|
if [ "${_ret}" != "0" ]; then
|
||||||
|
_err "Can't update '${_pem}' in haproxy"
|
||||||
|
return "${_ret}"
|
||||||
|
fi
|
||||||
|
_socat_cert_commit_cmd="echo '${_cmdpfx}commit ssl cert ${_pem}' | socat '${_statssock}' - | grep -q '^Success!$'"
|
||||||
|
_debug _socat_cert_commit_cmd "${_socat_cert_commit_cmd}"
|
||||||
|
eval "${_socat_cert_commit_cmd}"
|
||||||
|
_ret=$?
|
||||||
|
if [ "${_ret}" != "0" ]; then
|
||||||
|
_err "Can't commit '${_pem}' in haproxy"
|
||||||
|
return ${_ret}
|
||||||
|
fi
|
||||||
|
if [ "${_newcert}" = "1" ]; then
|
||||||
|
# if this is a new certificate, it needs to be inserted into the crt-list`
|
||||||
|
_socat_cert_add_cmd="echo '${_cmdpfx}add ssl crt-list ${Le_Deploy_haproxy_pem_path} ${_pem}' | socat '${_statssock}' - | grep -q 'Success!'"
|
||||||
|
_debug _socat_cert_add_cmd "${_socat_cert_add_cmd}"
|
||||||
|
eval "${_socat_cert_add_cmd}"
|
||||||
|
_ret=$?
|
||||||
|
if [ "${_ret}" != "0" ]; then
|
||||||
|
_err "Can't update '${_pem}' in haproxy"
|
||||||
|
return "${_ret}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
_err "'socat' is not available, couldn't update over ${_socketname}"
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
_info "Reload successful"
|
# Reload HAProxy
|
||||||
|
_debug _reload "${_reload}"
|
||||||
|
eval "${_reload}"
|
||||||
|
_ret=$?
|
||||||
|
if [ "${_ret}" != "0" ]; then
|
||||||
|
_err "Error code ${_ret} during reload"
|
||||||
|
return ${_ret}
|
||||||
|
else
|
||||||
|
_info "Reload successful"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|||||||
98
deploy/kemplm.sh
Executable file
98
deploy/kemplm.sh
Executable file
@@ -0,0 +1,98 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
#Here is a script to deploy cert to a Kemp Loadmaster.
|
||||||
|
|
||||||
|
#returns 0 means success, otherwise error.
|
||||||
|
|
||||||
|
#DEPLOY_KEMP_TOKEN="token"
|
||||||
|
#DEPLOY_KEMP_URL="https://kemplm.example.com"
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#domain keyfile certfile cafile fullchain
|
||||||
|
kemplm_deploy() {
|
||||||
|
_domain="$1"
|
||||||
|
_key_file="$2"
|
||||||
|
_cert_file="$3"
|
||||||
|
_ca_file="$4"
|
||||||
|
_fullchain_file="$5"
|
||||||
|
|
||||||
|
_debug _domain "$_domain"
|
||||||
|
_debug _key_file "$_key_file"
|
||||||
|
_debug _cert_file "$_cert_file"
|
||||||
|
_debug _ca_file "$_ca_file"
|
||||||
|
_debug _fullchain_file "$_fullchain_file"
|
||||||
|
|
||||||
|
if ! _exists jq; then
|
||||||
|
_err "jq not found"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Rename wildcard certs, kemp accepts only alphanumeric names so we delete '*.' from filename
|
||||||
|
_kemp_domain=$(echo "${_domain}" | sed 's/\*\.//')
|
||||||
|
_debug _kemp_domain "$_kemp_domain"
|
||||||
|
|
||||||
|
# Read config from saved values or env
|
||||||
|
_getdeployconf DEPLOY_KEMP_TOKEN
|
||||||
|
_getdeployconf DEPLOY_KEMP_URL
|
||||||
|
|
||||||
|
_debug DEPLOY_KEMP_URL "$DEPLOY_KEMP_URL"
|
||||||
|
_secure_debug DEPLOY_KEMP_TOKEN "$DEPLOY_KEMP_TOKEN"
|
||||||
|
|
||||||
|
if [ -z "$DEPLOY_KEMP_TOKEN" ]; then
|
||||||
|
_err "Kemp Loadmaster token is not found, please define DEPLOY_KEMP_TOKEN."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if [ -z "$DEPLOY_KEMP_URL" ]; then
|
||||||
|
_err "Kemp Loadmaster URL is not found, please define DEPLOY_KEMP_URL."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Save current values
|
||||||
|
_savedeployconf DEPLOY_KEMP_TOKEN "$DEPLOY_KEMP_TOKEN"
|
||||||
|
_savedeployconf DEPLOY_KEMP_URL "$DEPLOY_KEMP_URL"
|
||||||
|
|
||||||
|
# Check if certificate is already installed
|
||||||
|
_info "Check if certificate is already present"
|
||||||
|
_list_request="{\"cmd\": \"listcert\", \"apikey\": \"${DEPLOY_KEMP_TOKEN}\"}"
|
||||||
|
_debug3 _list_request "${_list_request}"
|
||||||
|
_kemp_cert_count=$(HTTPS_INSECURE=1 _post "${_list_request}" "${DEPLOY_KEMP_URL}/accessv2" | jq -r '.cert[] | .name' | grep -c "${_kemp_domain}")
|
||||||
|
_debug2 _kemp_cert_count "${_kemp_cert_count}"
|
||||||
|
|
||||||
|
_kemp_replace_cert=1
|
||||||
|
if [ "${_kemp_cert_count}" -eq 0 ]; then
|
||||||
|
_kemp_replace_cert=0
|
||||||
|
_info "Certificate does not exist on Kemp Loadmaster"
|
||||||
|
else
|
||||||
|
_info "Certificate already exists on Kemp Loadmaster"
|
||||||
|
fi
|
||||||
|
_debug _kemp_replace_cert "${_kemp_replace_cert}"
|
||||||
|
|
||||||
|
# Upload new certificate to Kemp Loadmaster
|
||||||
|
_kemp_upload_cert=$(_mktemp)
|
||||||
|
cat "${_fullchain_file}" "${_key_file}" | base64 | tr -d '\n' >"${_kemp_upload_cert}"
|
||||||
|
|
||||||
|
_info "Uploading certificate to Kemp Loadmaster"
|
||||||
|
_add_data=$(cat "${_kemp_upload_cert}")
|
||||||
|
_add_request="{\"cmd\": \"addcert\", \"apikey\": \"${DEPLOY_KEMP_TOKEN}\", \"replace\": ${_kemp_replace_cert}, \"cert\": \"${_kemp_domain}\", \"data\": \"${_add_data}\"}"
|
||||||
|
_debug3 _add_request "${_add_request}"
|
||||||
|
_kemp_post_result=$(HTTPS_INSECURE=1 _post "${_add_request}" "${DEPLOY_KEMP_URL}/accessv2")
|
||||||
|
_retval=$?
|
||||||
|
_debug2 _kemp_post_result "${_kemp_post_result}"
|
||||||
|
if [ "${_retval}" -eq 0 ]; then
|
||||||
|
_kemp_post_status=$(echo "${_kemp_post_result}" | jq -r '.status')
|
||||||
|
_kemp_post_message=$(echo "${_kemp_post_result}" | jq -r '.message')
|
||||||
|
if [ "${_kemp_post_status}" = "ok" ]; then
|
||||||
|
_info "Upload successful"
|
||||||
|
else
|
||||||
|
_err "Upload failed: ${_kemp_post_message}"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
_err "Upload failed"
|
||||||
|
_retval=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm "${_kemp_upload_cert}"
|
||||||
|
|
||||||
|
return $_retval
|
||||||
|
}
|
||||||
131
deploy/keyhelp.sh
Normal file
131
deploy/keyhelp.sh
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Script to deploy certificate to KeyHelp
|
||||||
|
# This deployment required following variables
|
||||||
|
# export DEPLOY_KEYHELP_BASEURL="https://keyhelp.example.com"
|
||||||
|
# export DEPLOY_KEYHELP_USERNAME="Your KeyHelp Username"
|
||||||
|
# export DEPLOY_KEYHELP_PASSWORD="Your KeyHelp Password"
|
||||||
|
# export DEPLOY_KEYHELP_DOMAIN_ID="Depoly certificate to this Domain ID"
|
||||||
|
|
||||||
|
# Open the 'Edit domain' page, and you will see id=xxx at the end of the URL. This is the Domain ID.
|
||||||
|
# https://DEPLOY_KEYHELP_BASEURL/index.php?page=domains&action=edit&id=xxx
|
||||||
|
|
||||||
|
# If have more than one domain name
|
||||||
|
# export DEPLOY_KEYHELP_DOMAIN_ID="111 222 333"
|
||||||
|
|
||||||
|
keyhelp_deploy() {
|
||||||
|
_cdomain="$1"
|
||||||
|
_ckey="$2"
|
||||||
|
_ccert="$3"
|
||||||
|
_cca="$4"
|
||||||
|
_cfullchain="$5"
|
||||||
|
|
||||||
|
_debug _cdomain "$_cdomain"
|
||||||
|
_debug _ckey "$_ckey"
|
||||||
|
_debug _ccert "$_ccert"
|
||||||
|
_debug _cca "$_cca"
|
||||||
|
_debug _cfullchain "$_cfullchain"
|
||||||
|
|
||||||
|
if [ -z "$DEPLOY_KEYHELP_BASEURL" ]; then
|
||||||
|
_err "DEPLOY_KEYHELP_BASEURL is not defined."
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
_savedomainconf DEPLOY_KEYHELP_BASEURL "$DEPLOY_KEYHELP_BASEURL"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$DEPLOY_KEYHELP_USERNAME" ]; then
|
||||||
|
_err "DEPLOY_KEYHELP_USERNAME is not defined."
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
_savedomainconf DEPLOY_KEYHELP_USERNAME "$DEPLOY_KEYHELP_USERNAME"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$DEPLOY_KEYHELP_PASSWORD" ]; then
|
||||||
|
_err "DEPLOY_KEYHELP_PASSWORD is not defined."
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
_savedomainconf DEPLOY_KEYHELP_PASSWORD "$DEPLOY_KEYHELP_PASSWORD"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$DEPLOY_KEYHELP_DOMAIN_ID" ]; then
|
||||||
|
_err "DEPLOY_KEYHELP_DOMAIN_ID is not defined."
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
_savedomainconf DEPLOY_KEYHELP_DOMAIN_ID "$DEPLOY_KEYHELP_DOMAIN_ID"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Optional DEPLOY_KEYHELP_ENFORCE_HTTPS
|
||||||
|
_getdeployconf DEPLOY_KEYHELP_ENFORCE_HTTPS
|
||||||
|
# set default values for DEPLOY_KEYHELP_ENFORCE_HTTPS
|
||||||
|
[ -n "${DEPLOY_KEYHELP_ENFORCE_HTTPS}" ] || DEPLOY_KEYHELP_ENFORCE_HTTPS="1"
|
||||||
|
|
||||||
|
_info "Logging in to keyhelp panel"
|
||||||
|
username_encoded="$(printf "%s" "${DEPLOY_KEYHELP_USERNAME}" | _url_encode)"
|
||||||
|
password_encoded="$(printf "%s" "${DEPLOY_KEYHELP_PASSWORD}" | _url_encode)"
|
||||||
|
_H1="Content-Type: application/x-www-form-urlencoded"
|
||||||
|
_response=$(_get "$DEPLOY_KEYHELP_BASEURL/index.php?submit=1&username=$username_encoded&password=$password_encoded" "TRUE")
|
||||||
|
_cookie="$(grep -i '^set-cookie:' "$HTTP_HEADER" | _head_n 1 | cut -d " " -f 2)"
|
||||||
|
|
||||||
|
# If cookies is not empty then logon successful
|
||||||
|
if [ -z "$_cookie" ]; then
|
||||||
|
_err "Fail to get cookie."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug "cookie" "$_cookie"
|
||||||
|
|
||||||
|
_info "Uploading certificate"
|
||||||
|
_date=$(date +"%Y%m%d")
|
||||||
|
encoded_key="$(_url_encode <"$_ckey")"
|
||||||
|
encoded_ccert="$(_url_encode <"$_ccert")"
|
||||||
|
encoded_cca="$(_url_encode <"$_cca")"
|
||||||
|
certificate_name="$_cdomain-$_date"
|
||||||
|
|
||||||
|
_request_body="submit=1&certificate_name=$certificate_name&add_type=upload&text_private_key=$encoded_key&text_certificate=$encoded_ccert&text_ca_certificate=$encoded_cca"
|
||||||
|
_H1="Cookie: $_cookie"
|
||||||
|
_response=$(_post "$_request_body" "$DEPLOY_KEYHELP_BASEURL/index.php?page=ssl_certificates&action=add" "" "POST")
|
||||||
|
_message=$(echo "$_response" | grep -A 2 'message-body' | sed -n '/<div class="message-body ">/,/<\/div>/{//!p;}' | sed 's/<[^>]*>//g' | sed 's/^ *//;s/ *$//')
|
||||||
|
_info "_message" "$_message"
|
||||||
|
if [ -z "$_message" ]; then
|
||||||
|
_err "Fail to upload certificate."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
for DOMAIN_ID in $DEPLOY_KEYHELP_DOMAIN_ID; do
|
||||||
|
_info "Apply certificate to domain id $DOMAIN_ID"
|
||||||
|
_response=$(_get "$DEPLOY_KEYHELP_BASEURL/index.php?page=domains&action=edit&id=$DOMAIN_ID")
|
||||||
|
cert_value=$(echo "$_response" | grep "$certificate_name" | sed -n 's/.*value="\([^"]*\).*/\1/p')
|
||||||
|
target_type=$(echo "$_response" | grep 'target_type' | grep 'checked' | sed -n 's/.*value="\([^"]*\).*/\1/p')
|
||||||
|
if [ "$target_type" = "directory" ]; then
|
||||||
|
path=$(echo "$_response" | awk '/name="path"/{getline; print}' | sed -n 's/.*value="\([^"]*\).*/\1/p')
|
||||||
|
fi
|
||||||
|
echo "$_response" | grep "is_prefer_https" | grep "checked" >/dev/null
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
is_prefer_https=1
|
||||||
|
else
|
||||||
|
is_prefer_https=0
|
||||||
|
fi
|
||||||
|
echo "$_response" | grep "hsts_enabled" | grep "checked" >/dev/null
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
hsts_enabled=1
|
||||||
|
else
|
||||||
|
hsts_enabled=0
|
||||||
|
fi
|
||||||
|
_debug "cert_value" "$cert_value"
|
||||||
|
if [ -z "$cert_value" ]; then
|
||||||
|
_err "Fail to get certificate id."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_request_body="submit=1&id=$DOMAIN_ID&target_type=$target_type&path=$path&is_prefer_https=$is_prefer_https&hsts_enabled=$hsts_enabled&certificate_type=custom&certificate_id=$cert_value&enforce_https=$DEPLOY_KEYHELP_ENFORCE_HTTPS"
|
||||||
|
_response=$(_post "$_request_body" "$DEPLOY_KEYHELP_BASEURL/index.php?page=domains&action=edit" "" "POST")
|
||||||
|
_message=$(echo "$_response" | grep -A 2 'message-body' | sed -n '/<div class="message-body ">/,/<\/div>/{//!p;}' | sed 's/<[^>]*>//g' | sed 's/^ *//;s/ *$//')
|
||||||
|
_info "_message" "$_message"
|
||||||
|
if [ -z "$_message" ]; then
|
||||||
|
_err "Fail to apply certificate."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
_info "Domain $_cdomain certificate successfully deployed to KeyHelp Domain ID $DEPLOY_KEYHELP_DOMAIN_ID."
|
||||||
|
return 0
|
||||||
|
}
|
||||||
86
deploy/keyhelp_api.sh
Normal file
86
deploy/keyhelp_api.sh
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
keyhelp_api_deploy() {
|
||||||
|
_cdomain="$1"
|
||||||
|
_ckey="$2"
|
||||||
|
_ccert="$3"
|
||||||
|
_cca="$4"
|
||||||
|
|
||||||
|
_debug _cdomain "$_cdomain"
|
||||||
|
_debug _ckey "$_ckey"
|
||||||
|
_debug _ccert "$_ccert"
|
||||||
|
_debug _cca "$_cca"
|
||||||
|
|
||||||
|
# Read config from saved values or env
|
||||||
|
_getdeployconf DEPLOY_KEYHELP_HOST
|
||||||
|
_getdeployconf DEPLOY_KEYHELP_API_KEY
|
||||||
|
|
||||||
|
_debug DEPLOY_KEYHELP_HOST "$DEPLOY_KEYHELP_HOST"
|
||||||
|
_secure_debug DEPLOY_KEYHELP_API_KEY "$DEPLOY_KEYHELP_API_KEY"
|
||||||
|
|
||||||
|
if [ -z "$DEPLOY_KEYHELP_HOST" ]; then
|
||||||
|
_err "KeyHelp host not found, please define DEPLOY_KEYHELP_HOST."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if [ -z "$DEPLOY_KEYHELP_API_KEY" ]; then
|
||||||
|
_err "KeyHelp api key not found, please define DEPLOY_KEYHELP_API_KEY."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Save current values
|
||||||
|
_savedeployconf DEPLOY_KEYHELP_HOST "$DEPLOY_KEYHELP_HOST"
|
||||||
|
_savedeployconf DEPLOY_KEYHELP_API_KEY "$DEPLOY_KEYHELP_API_KEY"
|
||||||
|
|
||||||
|
_request_key="$(tr '\n' ':' <"$_ckey" | sed 's/:/\\n/g')"
|
||||||
|
_request_cert="$(tr '\n' ':' <"$_ccert" | sed 's/:/\\n/g')"
|
||||||
|
_request_ca="$(tr '\n' ':' <"$_cca" | sed 's/:/\\n/g')"
|
||||||
|
|
||||||
|
_request_body="{
|
||||||
|
\"name\": \"$_cdomain\",
|
||||||
|
\"components\": {
|
||||||
|
\"private_key\": \"$_request_key\",
|
||||||
|
\"certificate\": \"$_request_cert\",
|
||||||
|
\"ca_certificate\": \"$_request_ca\"
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
|
||||||
|
_hosts="$(echo "$DEPLOY_KEYHELP_HOST" | tr "," " ")"
|
||||||
|
_keys="$(echo "$DEPLOY_KEYHELP_API_KEY" | tr "," " ")"
|
||||||
|
_i=1
|
||||||
|
|
||||||
|
for _host in $_hosts; do
|
||||||
|
_key="$(_getfield "$_keys" "$_i" " ")"
|
||||||
|
_i="$(_math "$_i" + 1)"
|
||||||
|
|
||||||
|
export _H1="X-API-Key: $_key"
|
||||||
|
|
||||||
|
_put_url="$_host/api/v2/certificates/name/$_cdomain"
|
||||||
|
if _post "$_request_body" "$_put_url" "" "PUT" "application/json" >/dev/null; then
|
||||||
|
_code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\r\n")"
|
||||||
|
else
|
||||||
|
_err "Cannot make PUT request to $_put_url"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$_code" = "404" ]; then
|
||||||
|
_info "$_cdomain not found, creating new entry at $_host"
|
||||||
|
|
||||||
|
_post_url="$_host/api/v2/certificates"
|
||||||
|
if _post "$_request_body" "$_post_url" "" "POST" "application/json" >/dev/null; then
|
||||||
|
_code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\r\n")"
|
||||||
|
else
|
||||||
|
_err "Cannot make POST request to $_post_url"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if _startswith "$_code" "2"; then
|
||||||
|
_info "$_cdomain set at $_host"
|
||||||
|
else
|
||||||
|
_err "HTTP status code is $_code"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
276
deploy/multideploy.sh
Normal file
276
deploy/multideploy.sh
Normal file
@@ -0,0 +1,276 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# ACME.sh 3rd party deploy plugin for multiple (same) services
|
||||||
|
################################################################################
|
||||||
|
# Authors: tomo2403 (creator), https://github.com/tomo2403
|
||||||
|
# Updated: 2025-03-01
|
||||||
|
# Issues: https://github.com/acmesh-official/acme.sh/issues and mention @tomo2403
|
||||||
|
################################################################################
|
||||||
|
# Usage (shown values are the examples):
|
||||||
|
# 1. Set optional environment variables
|
||||||
|
# - export MULTIDEPLOY_FILENAME="multideploy.yaml" - "multideploy.yml" will be automatically used if not set"
|
||||||
|
#
|
||||||
|
# 2. Run command:
|
||||||
|
# acme.sh --deploy --deploy-hook multideploy -d example.com
|
||||||
|
################################################################################
|
||||||
|
# Dependencies:
|
||||||
|
# - yq
|
||||||
|
################################################################################
|
||||||
|
# Return value:
|
||||||
|
# 0 means success, otherwise error.
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
MULTIDEPLOY_VERSION="1.0"
|
||||||
|
|
||||||
|
# Description: This function handles the deployment of certificates to multiple services.
|
||||||
|
# It processes the provided certificate files and deploys them according to the
|
||||||
|
# configuration specified in the multideploy file.
|
||||||
|
#
|
||||||
|
# Parameters:
|
||||||
|
# _cdomain - The domain name for which the certificate is issued.
|
||||||
|
# _ckey - The private key file for the certificate.
|
||||||
|
# _ccert - The certificate file.
|
||||||
|
# _cca - The CA (Certificate Authority) file.
|
||||||
|
# _cfullchain - The full chain certificate file.
|
||||||
|
# _cpfx - The PFX (Personal Information Exchange) file.
|
||||||
|
multideploy_deploy() {
|
||||||
|
_cdomain="$1"
|
||||||
|
_ckey="$2"
|
||||||
|
_ccert="$3"
|
||||||
|
_cca="$4"
|
||||||
|
_cfullchain="$5"
|
||||||
|
_cpfx="$6"
|
||||||
|
|
||||||
|
_debug _cdomain "$_cdomain"
|
||||||
|
_debug _ckey "$_ckey"
|
||||||
|
_debug _ccert "$_ccert"
|
||||||
|
_debug _cca "$_cca"
|
||||||
|
_debug _cfullchain "$_cfullchain"
|
||||||
|
_debug _cpfx "$_cpfx"
|
||||||
|
|
||||||
|
MULTIDEPLOY_FILENAME="${MULTIDEPLOY_FILENAME:-$(_getdeployconf MULTIDEPLOY_FILENAME)}"
|
||||||
|
if [ -z "$MULTIDEPLOY_FILENAME" ]; then
|
||||||
|
MULTIDEPLOY_FILENAME="multideploy.yml"
|
||||||
|
_info "MULTIDEPLOY_FILENAME is not set, so I will use 'multideploy.yml'."
|
||||||
|
else
|
||||||
|
_savedeployconf "MULTIDEPLOY_FILENAME" "$MULTIDEPLOY_FILENAME"
|
||||||
|
_debug2 "MULTIDEPLOY_FILENAME" "$MULTIDEPLOY_FILENAME"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! file=$(_preprocess_deployfile "$MULTIDEPLOY_FILENAME"); then
|
||||||
|
_err "Failed to preprocess deploy file."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug3 "File" "$file"
|
||||||
|
|
||||||
|
# Deploy to services
|
||||||
|
_deploy_services "$file"
|
||||||
|
_exitCode="$?"
|
||||||
|
|
||||||
|
return "$_exitCode"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Description:
|
||||||
|
# This function preprocesses the deploy file by checking if 'yq' is installed,
|
||||||
|
# verifying the existence of the deploy file, and ensuring only one deploy file is present.
|
||||||
|
# Arguments:
|
||||||
|
# $@ - Posible deploy file names.
|
||||||
|
# Usage:
|
||||||
|
# _preprocess_deployfile "<deploy_file1>" "<deploy_file2>?"
|
||||||
|
_preprocess_deployfile() {
|
||||||
|
# Check if yq is installed
|
||||||
|
if ! command -v yq >/dev/null 2>&1; then
|
||||||
|
_err "yq is not installed! Please install yq and try again."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug3 "yq is installed."
|
||||||
|
|
||||||
|
# Check if deploy file exists
|
||||||
|
for file in "$@"; do
|
||||||
|
_debug3 "Checking file" "$DOMAIN_PATH/$file"
|
||||||
|
if [ -f "$DOMAIN_PATH/$file" ]; then
|
||||||
|
_debug3 "File found"
|
||||||
|
if [ -n "$found_file" ]; then
|
||||||
|
_err "Multiple deploy files found. Please keep only one deploy file."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
found_file="$file"
|
||||||
|
else
|
||||||
|
_debug3 "File not found"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -z "$found_file" ]; then
|
||||||
|
_err "Deploy file not found. Go to https://github.com/acmesh-official/acme.sh/wiki/deployhooks#36-deploying-to-multiple-services-with-the-same-hooks to see how to create one."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if ! _check_deployfile "$DOMAIN_PATH/$found_file"; then
|
||||||
|
_err "Deploy file is not valid: $DOMAIN_PATH/$found_file"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$DOMAIN_PATH/$found_file"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Description:
|
||||||
|
# This function checks the deploy file for version compatibility and the existence of the specified configuration and services.
|
||||||
|
# Arguments:
|
||||||
|
# $1 - The path to the deploy configuration file.
|
||||||
|
# $2 - The name of the deploy configuration to use.
|
||||||
|
# Usage:
|
||||||
|
# _check_deployfile "<deploy_file_path>"
|
||||||
|
_check_deployfile() {
|
||||||
|
_deploy_file="$1"
|
||||||
|
_debug2 "check: Deploy file" "$_deploy_file"
|
||||||
|
|
||||||
|
# Check version
|
||||||
|
_deploy_file_version=$(yq -r '.version' "$_deploy_file")
|
||||||
|
if [ "$MULTIDEPLOY_VERSION" != "$_deploy_file_version" ]; then
|
||||||
|
_err "As of $PROJECT_NAME $VER, the deploy file needs version $MULTIDEPLOY_VERSION! Your current deploy file is of version $_deploy_file_version."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug2 "check: Deploy file version is compatible: $_deploy_file_version"
|
||||||
|
|
||||||
|
# Extract all services from config
|
||||||
|
_services=$(yq -r '.services[].name' "$_deploy_file")
|
||||||
|
|
||||||
|
if [ -z "$_services" ]; then
|
||||||
|
_err "Config does not have any services to deploy to."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug2 "check: Config has services."
|
||||||
|
echo "$_services" | while read -r _service; do
|
||||||
|
_debug3 " - $_service"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Check if extracted services exist in services list
|
||||||
|
echo "$_services" | while read -r _service; do
|
||||||
|
_debug2 "check: Checking service: $_service"
|
||||||
|
# Check if service exists
|
||||||
|
_service_config=$(yq -r ".services[] | select(.name == \"$_service\")" "$_deploy_file")
|
||||||
|
if [ -z "$_service_config" ] || [ "$_service_config" = "null" ]; then
|
||||||
|
_err "Service '$_service' not found."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_service_hook=$(echo "$_service_config" | yq -r ".hook" -)
|
||||||
|
if [ -z "$_service_hook" ] || [ "$_service_hook" = "null" ]; then
|
||||||
|
_err "Service '$_service' does not have a hook."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_service_environment=$(echo "$_service_config" | yq -r ".environment" -)
|
||||||
|
if [ -z "$_service_environment" ] || [ "$_service_environment" = "null" ]; then
|
||||||
|
_err "Service '$_service' does not have an environment."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# Description: This function takes a list of environment variables in YAML format,
|
||||||
|
# parses them, and exports each key-value pair as environment variables.
|
||||||
|
# Arguments:
|
||||||
|
# $1 - A string containing the list of environment variables in YAML format.
|
||||||
|
# Usage:
|
||||||
|
# _export_envs "$env_list"
|
||||||
|
_export_envs() {
|
||||||
|
_env_list="$1"
|
||||||
|
|
||||||
|
_secure_debug3 "Exporting envs" "$_env_list"
|
||||||
|
|
||||||
|
echo "$_env_list" | yq -r 'to_entries | .[] | .key + "=" + .value' | while IFS='=' read -r _key _value; do
|
||||||
|
# Using eval to expand nested variables in the configuration file
|
||||||
|
_value=$(eval 'echo "'"$_value"'"')
|
||||||
|
_savedeployconf "$_key" "$_value"
|
||||||
|
_secure_debug3 "Saved $_key" "$_value"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# Description:
|
||||||
|
# This function takes a YAML formatted string of environment variables, parses it,
|
||||||
|
# and clears each environment variable. It logs the process of clearing each variable.
|
||||||
|
#
|
||||||
|
# Note: Environment variables for a hook may be optional and differ between
|
||||||
|
# services using the same hook.
|
||||||
|
# If one service sets optional environment variables and another does not, the
|
||||||
|
# variables may persist and affect subsequent deployments.
|
||||||
|
# Clearing these variables after each service ensures that only the
|
||||||
|
# environment variables explicitly specified for each service in the deploy
|
||||||
|
# file are used.
|
||||||
|
# Arguments:
|
||||||
|
# $1 - A YAML formatted string containing environment variable key-value pairs.
|
||||||
|
# Usage:
|
||||||
|
# _clear_envs "<yaml_string>"
|
||||||
|
_clear_envs() {
|
||||||
|
_env_list="$1"
|
||||||
|
|
||||||
|
_secure_debug3 "Clearing envs" "$_env_list"
|
||||||
|
env_pairs=$(echo "$_env_list" | yq -r 'to_entries | .[] | .key + "=" + .value')
|
||||||
|
|
||||||
|
echo "$env_pairs" | while IFS='=' read -r _key _value; do
|
||||||
|
_debug3 "Deleting key" "$_key"
|
||||||
|
_cleardomainconf "SAVED_$_key"
|
||||||
|
unset -v "$_key"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# Description:
|
||||||
|
# This function deploys services listed in the deploy configuration file.
|
||||||
|
# Arguments:
|
||||||
|
# $1 - The path to the deploy configuration file.
|
||||||
|
# $2 - The list of services to deploy.
|
||||||
|
# Usage:
|
||||||
|
# _deploy_services "<deploy_file_path>" "<services_list>"
|
||||||
|
_deploy_services() {
|
||||||
|
_deploy_file="$1"
|
||||||
|
_debug3 "Deploy file" "$_deploy_file"
|
||||||
|
|
||||||
|
_tempfile=$(mktemp)
|
||||||
|
trap 'rm -f $_tempfile' EXIT
|
||||||
|
|
||||||
|
yq -r '.services[].name' "$_deploy_file" >"$_tempfile"
|
||||||
|
_debug3 "Services" "$(cat "$_tempfile")"
|
||||||
|
|
||||||
|
_failedServices=""
|
||||||
|
_failedCount=0
|
||||||
|
while read -r _service <&3; do
|
||||||
|
_debug2 "Service" "$_service"
|
||||||
|
_hook=$(yq -r ".services[] | select(.name == \"$_service\").hook" "$_deploy_file")
|
||||||
|
_envs=$(yq -r ".services[] | select(.name == \"$_service\").environment" "$_deploy_file")
|
||||||
|
|
||||||
|
_export_envs "$_envs"
|
||||||
|
if ! _deploy_service "$_service" "$_hook"; then
|
||||||
|
_failedServices="$_service, $_failedServices"
|
||||||
|
_failedCount=$((_failedCount + 1))
|
||||||
|
fi
|
||||||
|
_clear_envs "$_envs"
|
||||||
|
done 3<"$_tempfile"
|
||||||
|
|
||||||
|
_debug3 "Failed services" "$_failedServices"
|
||||||
|
_debug2 "Failed count" "$_failedCount"
|
||||||
|
if [ -n "$_failedServices" ]; then
|
||||||
|
_info "$(__red "Deployment failed") for services: $_failedServices"
|
||||||
|
else
|
||||||
|
_debug "All services deployed successfully."
|
||||||
|
fi
|
||||||
|
|
||||||
|
return "$_failedCount"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Description: Deploys a service using the specified hook.
|
||||||
|
# Arguments:
|
||||||
|
# $1 - The name of the service to deploy.
|
||||||
|
# $2 - The hook to use for deployment.
|
||||||
|
# Usage:
|
||||||
|
# _deploy_service <service_name> <hook>
|
||||||
|
_deploy_service() {
|
||||||
|
_name="$1"
|
||||||
|
_hook="$2"
|
||||||
|
|
||||||
|
_debug2 "SERVICE" "$_name"
|
||||||
|
_debug2 "HOOK" "$_hook"
|
||||||
|
|
||||||
|
_info "$(__green "Deploying") to '$_name' using '$_hook'"
|
||||||
|
_deploy "$_cdomain" "$_hook"
|
||||||
|
}
|
||||||
69
deploy/netlify.sh
Normal file
69
deploy/netlify.sh
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Script to deploy certificate to Netlify
|
||||||
|
# https://docs.netlify.com/api/get-started/#authentication
|
||||||
|
# https://open-api.netlify.com/#tag/sniCertificate
|
||||||
|
|
||||||
|
# This deployment required following variables
|
||||||
|
# export Netlify_ACCESS_TOKEN="Your Netlify Access Token"
|
||||||
|
# export Netlify_SITE_ID="Your Netlify Site ID"
|
||||||
|
|
||||||
|
# If have more than one SITE ID
|
||||||
|
# export Netlify_SITE_ID="SITE_ID_1 SITE_ID_2"
|
||||||
|
|
||||||
|
# returns 0 means success, otherwise error.
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#domain keyfile certfile cafile fullchain
|
||||||
|
netlify_deploy() {
|
||||||
|
_cdomain="$1"
|
||||||
|
_ckey="$2"
|
||||||
|
_ccert="$3"
|
||||||
|
_cca="$4"
|
||||||
|
_cfullchain="$5"
|
||||||
|
|
||||||
|
_debug _cdomain "$_cdomain"
|
||||||
|
_debug _ckey "$_ckey"
|
||||||
|
_debug _ccert "$_ccert"
|
||||||
|
_debug _cca "$_cca"
|
||||||
|
_debug _cfullchain "$_cfullchain"
|
||||||
|
|
||||||
|
if [ -z "$Netlify_ACCESS_TOKEN" ]; then
|
||||||
|
_err "Netlify_ACCESS_TOKEN is not defined."
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
_savedomainconf Netlify_ACCESS_TOKEN "$Netlify_ACCESS_TOKEN"
|
||||||
|
fi
|
||||||
|
if [ -z "$Netlify_SITE_ID" ]; then
|
||||||
|
_err "Netlify_SITE_ID is not defined."
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
_savedomainconf Netlify_SITE_ID "$Netlify_SITE_ID"
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Deploying certificate to Netlify..."
|
||||||
|
|
||||||
|
## upload certificate
|
||||||
|
string_ccert=$(sed 's/$/\\n/' "$_ccert" | tr -d '\n')
|
||||||
|
string_cca=$(sed 's/$/\\n/' "$_cca" | tr -d '\n')
|
||||||
|
string_key=$(sed 's/$/\\n/' "$_ckey" | tr -d '\n')
|
||||||
|
|
||||||
|
for SITE_ID in $Netlify_SITE_ID; do
|
||||||
|
_request_body="{\"certificate\":\"$string_ccert\",\"key\":\"$string_key\",\"ca_certificates\":\"$string_cca\"}"
|
||||||
|
_debug _request_body "$_request_body"
|
||||||
|
_debug Netlify_ACCESS_TOKEN "$Netlify_ACCESS_TOKEN"
|
||||||
|
export _H1="Authorization: Bearer $Netlify_ACCESS_TOKEN"
|
||||||
|
_response=$(_post "$_request_body" "https://api.netlify.com/api/v1/sites/$SITE_ID/ssl" "" "POST" "application/json")
|
||||||
|
|
||||||
|
if _contains "$_response" "\"error\""; then
|
||||||
|
_err "Error in deploying $_cdomain certificate to Netlify SITE_ID $SITE_ID."
|
||||||
|
_err "$_response"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug response "$_response"
|
||||||
|
_info "Domain $_cdomain certificate successfully deployed to Netlify SITE_ID $SITE_ID."
|
||||||
|
done
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
275
deploy/panos.sh
275
deploy/panos.sh
@@ -7,13 +7,27 @@
|
|||||||
#
|
#
|
||||||
# Firewall admin with superuser and IP address is required.
|
# Firewall admin with superuser and IP address is required.
|
||||||
#
|
#
|
||||||
# export PANOS_USER="" # required
|
# REQUIRED:
|
||||||
# export PANOS_PASS="" # required
|
# export PANOS_HOST=""
|
||||||
# export PANOS_HOST="" # required
|
# export PANOS_USER="" #User *MUST* have Commit and Import Permissions in XML API for Admin Role
|
||||||
|
# export PANOS_PASS=""
|
||||||
|
#
|
||||||
|
# OPTIONAL
|
||||||
|
# export PANOS_TEMPLATE="" # Template Name of panorama managed devices
|
||||||
|
# export PANOS_TEMPLATE_STACK="" # set a Template Stack if certificate should also be pushed automatically
|
||||||
|
# export PANOS_VSYS="Shared" # name of the vsys to import the certificate
|
||||||
|
# export PANOS_CERTNAME="" # use a custom certificate name to work around Panorama's 31-character limit
|
||||||
|
#
|
||||||
|
# The script will automatically generate a new API key if
|
||||||
|
# no key is found, or if a saved key has expired or is invalid.
|
||||||
|
|
||||||
# This function is to parse the XML
|
_COMMIT_WAIT_INTERVAL=30 # query commit status every 30 seconds
|
||||||
|
_COMMIT_WAIT_ITERATIONS=20 # query commit status 20 times (20*30 = 600 seconds = 10 minutes)
|
||||||
|
|
||||||
|
# This function is to parse the XML response from the firewall
|
||||||
parse_response() {
|
parse_response() {
|
||||||
type=$2
|
type=$2
|
||||||
|
_debug "API Response: $1"
|
||||||
if [ "$type" = 'keygen' ]; then
|
if [ "$type" = 'keygen' ]; then
|
||||||
status=$(echo "$1" | sed 's/^.*\(['\'']\)\([a-z]*\)'\''.*/\2/g')
|
status=$(echo "$1" | sed 's/^.*\(['\'']\)\([a-z]*\)'\''.*/\2/g')
|
||||||
if [ "$status" = "success" ]; then
|
if [ "$status" = "success" ]; then
|
||||||
@@ -23,25 +37,52 @@ parse_response() {
|
|||||||
message="PAN-OS Key could not be set."
|
message="PAN-OS Key could not be set."
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
status=$(echo "$1" | sed 's/^.*"\([a-z]*\)".*/\1/g')
|
if [ "$type" = 'commit' ]; then
|
||||||
message=$(echo "$1" | sed 's/^.*<result>\(.*\)<\/result.*/\1/g')
|
job_id=$(echo "$1" | sed 's/^.*\(<job>\)\(.*\)<\/job>.*/\2/g')
|
||||||
|
_commit_job_id=$job_id
|
||||||
|
elif [ "$type" = 'job_status' ]; then
|
||||||
|
job_status=$(echo "$1" | tr -d '\n' | sed 's/^.*<result>\([^<]*\)<\/result>.*/\1/g')
|
||||||
|
_commit_job_status=$job_status
|
||||||
|
fi
|
||||||
|
status=$(echo "$1" | tr -d '\n' | sed 's/^.*"\([a-z]*\)".*/\1/g')
|
||||||
|
message=$(echo "$1" | tr -d '\n' | sed 's/.*\(<result>\|<msg>\|<line>\)\([^<]*\).*/\2/g')
|
||||||
|
_debug "Firewall message: $message"
|
||||||
|
if [ "$type" = 'keytest' ] && [ "$status" != "success" ]; then
|
||||||
|
_debug "**** API Key has EXPIRED or is INVALID ****"
|
||||||
|
unset _panos_key
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#This function is used to deploy to the firewall
|
||||||
deployer() {
|
deployer() {
|
||||||
content=""
|
content=""
|
||||||
type=$1 # Types are keygen, cert, key, commit
|
type=$1 # Types are keytest, keygen, cert, key, commit, job_status, push
|
||||||
_debug "**** Deploying $type *****"
|
|
||||||
panos_url="https://$_panos_host/api/"
|
panos_url="https://$_panos_host/api/"
|
||||||
|
export _H1="Content-Type: application/x-www-form-urlencoded"
|
||||||
|
|
||||||
|
#Test API Key by performing a lookup
|
||||||
|
if [ "$type" = 'keytest' ]; then
|
||||||
|
_debug "**** Testing saved API Key ****"
|
||||||
|
# Get Version Info to test key
|
||||||
|
content="type=version&key=$_panos_key"
|
||||||
|
## Exclude all scopes for the empty commit
|
||||||
|
#_exclude_scope="<policy-and-objects>exclude</policy-and-objects><device-and-network>exclude</device-and-network><shared-object>exclude</shared-object>"
|
||||||
|
#content="type=commit&action=partial&key=$_panos_key&cmd=<commit><partial>$_exclude_scope<admin><member>acmekeytest</member></admin></partial></commit>"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Generate API Key
|
||||||
if [ "$type" = 'keygen' ]; then
|
if [ "$type" = 'keygen' ]; then
|
||||||
_H1="Content-Type: application/x-www-form-urlencoded"
|
_debug "**** Generating new API Key ****"
|
||||||
content="type=keygen&user=$_panos_user&password=$_panos_pass"
|
content="type=keygen&user=$_panos_user&password=$_panos_pass"
|
||||||
# content="$content${nl}--$delim${nl}Content-Disposition: form-data; type=\"keygen\"; user=\"$_panos_user\"; password=\"$_panos_pass\"${nl}Content-Type: application/octet-stream${nl}${nl}"
|
# content="$content${nl}--$delim${nl}Content-Disposition: form-data; type=\"keygen\"; user=\"$_panos_user\"; password=\"$_panos_pass\"${nl}Content-Type: application/octet-stream${nl}${nl}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Deploy Cert or Key
|
||||||
if [ "$type" = 'cert' ] || [ "$type" = 'key' ]; then
|
if [ "$type" = 'cert' ] || [ "$type" = 'key' ]; then
|
||||||
#Generate DEIM
|
_debug "**** Deploying $type ****"
|
||||||
|
#Generate DELIM
|
||||||
delim="-----MultipartDelimiter$(date "+%s%N")"
|
delim="-----MultipartDelimiter$(date "+%s%N")"
|
||||||
nl="\015\012"
|
nl="\015\012"
|
||||||
#Set Header
|
#Set Header
|
||||||
@@ -49,19 +90,31 @@ deployer() {
|
|||||||
if [ "$type" = 'cert' ]; then
|
if [ "$type" = 'cert' ]; then
|
||||||
panos_url="${panos_url}?type=import"
|
panos_url="${panos_url}?type=import"
|
||||||
content="--$delim${nl}Content-Disposition: form-data; name=\"category\"\r\n\r\ncertificate"
|
content="--$delim${nl}Content-Disposition: form-data; name=\"category\"\r\n\r\ncertificate"
|
||||||
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"certificate-name\"\r\n\r\n$_cdomain"
|
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"certificate-name\"\r\n\r\n$_panos_certname"
|
||||||
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"key\"\r\n\r\n$_panos_key"
|
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"key\"\r\n\r\n$_panos_key"
|
||||||
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"format\"\r\n\r\npem"
|
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"format\"\r\n\r\npem"
|
||||||
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"file\"; filename=\"$(basename "$_cfullchain")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_cfullchain")"
|
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"file\"; filename=\"$(basename "$_cfullchain")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_cfullchain")"
|
||||||
|
if [ "$_panos_template" ]; then
|
||||||
|
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"target-tpl\"\r\n\r\n$_panos_template"
|
||||||
|
fi
|
||||||
|
if [ "$_panos_vsys" ]; then
|
||||||
|
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"target-tpl-vsys\"\r\n\r\n$_panos_vsys"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
if [ "$type" = 'key' ]; then
|
if [ "$type" = 'key' ]; then
|
||||||
panos_url="${panos_url}?type=import"
|
panos_url="${panos_url}?type=import"
|
||||||
content="--$delim${nl}Content-Disposition: form-data; name=\"category\"\r\n\r\nprivate-key"
|
content="--$delim${nl}Content-Disposition: form-data; name=\"category\"\r\n\r\nprivate-key"
|
||||||
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"certificate-name\"\r\n\r\n$_cdomain"
|
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"certificate-name\"\r\n\r\n$_panos_certname"
|
||||||
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"key\"\r\n\r\n$_panos_key"
|
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"key\"\r\n\r\n$_panos_key"
|
||||||
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"format\"\r\n\r\npem"
|
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"format\"\r\n\r\npem"
|
||||||
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"passphrase\"\r\n\r\n123456"
|
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"passphrase\"\r\n\r\n123456"
|
||||||
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"file\"; filename=\"$(basename "$_ckey")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_ckey")"
|
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"file\"; filename=\"$(basename "$_panos_certname.key")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_ckey")"
|
||||||
|
if [ "$_panos_template" ]; then
|
||||||
|
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"target-tpl\"\r\n\r\n$_panos_template"
|
||||||
|
fi
|
||||||
|
if [ "$_panos_vsys" ]; then
|
||||||
|
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"target-tpl-vsys\"\r\n\r\n$_panos_vsys"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
#Close multipart
|
#Close multipart
|
||||||
content="$content${nl}--$delim--${nl}${nl}"
|
content="$content${nl}--$delim--${nl}${nl}"
|
||||||
@@ -69,20 +122,44 @@ deployer() {
|
|||||||
content=$(printf %b "$content")
|
content=$(printf %b "$content")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Commit changes
|
||||||
if [ "$type" = 'commit' ]; then
|
if [ "$type" = 'commit' ]; then
|
||||||
export _H1="Content-Type: application/x-www-form-urlencoded"
|
_debug "**** Committing changes ****"
|
||||||
cmd=$(printf "%s" "<commit><partial><$_panos_user></$_panos_user></partial></commit>" | _url_encode)
|
#Check for force commit - will commit ALL uncommited changes to the firewall. Use with caution!
|
||||||
content="type=commit&key=$_panos_key&cmd=$cmd"
|
if [ "$FORCE" ]; then
|
||||||
|
_debug "Force switch detected. Committing ALL changes to the firewall."
|
||||||
|
cmd=$(printf "%s" "<commit><partial><force><admin><member>$_panos_user</member></admin></force></partial></commit>" | _url_encode)
|
||||||
|
else
|
||||||
|
_exclude_scope="<policy-and-objects>exclude</policy-and-objects><device-and-network>exclude</device-and-network>"
|
||||||
|
cmd=$(printf "%s" "<commit><partial>$_exclude_scope<admin><member>$_panos_user</member></admin></partial></commit>" | _url_encode)
|
||||||
|
fi
|
||||||
|
content="type=commit&action=partial&key=$_panos_key&cmd=$cmd"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Query job status
|
||||||
|
if [ "$type" = 'job_status' ]; then
|
||||||
|
echo "**** Querying job $_commit_job_id status ****"
|
||||||
|
cmd=$(printf "%s" "<show><jobs><id>$_commit_job_id</id></jobs></show>" | _url_encode)
|
||||||
|
content="type=op&key=$_panos_key&cmd=$cmd"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Push changes
|
||||||
|
if [ "$type" = 'push' ]; then
|
||||||
|
echo "**** Pushing changes ****"
|
||||||
|
cmd=$(printf "%s" "<commit-all><template-stack><name>$_panos_template_stack</name><admin><member>$_panos_user</member></admin></template-stack></commit-all>" | _url_encode)
|
||||||
|
content="type=commit&action=all&key=$_panos_key&cmd=$cmd"
|
||||||
|
fi
|
||||||
|
|
||||||
response=$(_post "$content" "$panos_url" "" "POST")
|
response=$(_post "$content" "$panos_url" "" "POST")
|
||||||
parse_response "$response" "$type"
|
parse_response "$response" "$type"
|
||||||
# Saving response to variables
|
# Saving response to variables
|
||||||
response_status=$status
|
response_status=$status
|
||||||
#DEBUG
|
|
||||||
_debug response_status "$response_status"
|
_debug response_status "$response_status"
|
||||||
if [ "$response_status" = "success" ]; then
|
if [ "$response_status" = "success" ]; then
|
||||||
_debug "Successfully deployed $type"
|
_debug "Successfully deployed $type"
|
||||||
return 0
|
return 0
|
||||||
|
elif [ "$_commit_job_status" ]; then
|
||||||
|
_debug "Commit Job Status = $_commit_job_status"
|
||||||
else
|
else
|
||||||
_err "Deploy of type $type failed. Try deploying with --debug to troubleshoot."
|
_err "Deploy of type $type failed. Try deploying with --debug to troubleshoot."
|
||||||
_debug "$message"
|
_debug "$message"
|
||||||
@@ -92,48 +169,150 @@ deployer() {
|
|||||||
|
|
||||||
# This is the main function that will call the other functions to deploy everything.
|
# This is the main function that will call the other functions to deploy everything.
|
||||||
panos_deploy() {
|
panos_deploy() {
|
||||||
_cdomain="$1"
|
_cdomain=$(echo "$1" | sed 's/*/WILDCARD_/g') #Wildcard Safe Filename
|
||||||
_ckey="$2"
|
_ckey="$2"
|
||||||
_cfullchain="$5"
|
_cfullchain="$5"
|
||||||
# PANOS ENV VAR check
|
|
||||||
if [ -z "$PANOS_USER" ] || [ -z "$PANOS_PASS" ] || [ -z "$PANOS_HOST" ]; then
|
# VALID FILE CHECK
|
||||||
_debug "No ENV variables found lets check for saved variables"
|
if [ ! -f "$_ckey" ] || [ ! -f "$_cfullchain" ]; then
|
||||||
_getdeployconf PANOS_USER
|
_err "Unable to find a valid key and/or cert. If this is an ECDSA/ECC cert, use the --ecc flag when deploying."
|
||||||
_getdeployconf PANOS_PASS
|
return 1
|
||||||
_getdeployconf PANOS_HOST
|
|
||||||
_panos_user=$PANOS_USER
|
|
||||||
_panos_pass=$PANOS_PASS
|
|
||||||
_panos_host=$PANOS_HOST
|
|
||||||
if [ -z "$_panos_user" ] && [ -z "$_panos_pass" ] && [ -z "$_panos_host" ]; then
|
|
||||||
_err "No host, user and pass found.. If this is the first time deploying please set PANOS_HOST, PANOS_USER and PANOS_PASS in environment variables. Delete them after you have succesfully deployed certs."
|
|
||||||
return 1
|
|
||||||
else
|
|
||||||
_debug "Using saved env variables."
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
_debug "Detected ENV variables to be saved to the deploy conf."
|
|
||||||
# Encrypt and save user
|
|
||||||
_savedeployconf PANOS_USER "$PANOS_USER" 1
|
|
||||||
_savedeployconf PANOS_PASS "$PANOS_PASS" 1
|
|
||||||
_savedeployconf PANOS_HOST "$PANOS_HOST" 1
|
|
||||||
_panos_user="$PANOS_USER"
|
|
||||||
_panos_pass="$PANOS_PASS"
|
|
||||||
_panos_host="$PANOS_HOST"
|
|
||||||
fi
|
fi
|
||||||
_debug "Let's use username and pass to generate token."
|
|
||||||
if [ -z "$_panos_user" ] || [ -z "$_panos_pass" ] || [ -z "$_panos_host" ]; then
|
# PANOS_HOST
|
||||||
_err "Please pass username and password and host as env variables PANOS_USER, PANOS_PASS and PANOS_HOST"
|
if [ "$PANOS_HOST" ]; then
|
||||||
|
_debug "Detected ENV variable PANOS_HOST. Saving to file."
|
||||||
|
_savedeployconf PANOS_HOST "$PANOS_HOST" 1
|
||||||
|
else
|
||||||
|
_debug "Attempting to load variable PANOS_HOST from file."
|
||||||
|
_getdeployconf PANOS_HOST
|
||||||
|
fi
|
||||||
|
|
||||||
|
# PANOS USER
|
||||||
|
if [ "$PANOS_USER" ]; then
|
||||||
|
_debug "Detected ENV variable PANOS_USER. Saving to file."
|
||||||
|
_savedeployconf PANOS_USER "$PANOS_USER" 1
|
||||||
|
else
|
||||||
|
_debug "Attempting to load variable PANOS_USER from file."
|
||||||
|
_getdeployconf PANOS_USER
|
||||||
|
fi
|
||||||
|
|
||||||
|
# PANOS_PASS
|
||||||
|
if [ "$PANOS_PASS" ]; then
|
||||||
|
_debug "Detected ENV variable PANOS_PASS. Saving to file."
|
||||||
|
_savedeployconf PANOS_PASS "$PANOS_PASS" 1
|
||||||
|
else
|
||||||
|
_debug "Attempting to load variable PANOS_PASS from file."
|
||||||
|
_getdeployconf PANOS_PASS
|
||||||
|
fi
|
||||||
|
|
||||||
|
# PANOS_KEY
|
||||||
|
_getdeployconf PANOS_KEY
|
||||||
|
if [ "$PANOS_KEY" ]; then
|
||||||
|
_debug "Detected saved key."
|
||||||
|
_panos_key=$PANOS_KEY
|
||||||
|
else
|
||||||
|
_debug "No key detected"
|
||||||
|
unset _panos_key
|
||||||
|
fi
|
||||||
|
|
||||||
|
# PANOS_TEMPLATE
|
||||||
|
if [ "$PANOS_TEMPLATE" ]; then
|
||||||
|
_debug "Detected ENV variable PANOS_TEMPLATE. Saving to file."
|
||||||
|
_savedeployconf PANOS_TEMPLATE "$PANOS_TEMPLATE" 1
|
||||||
|
else
|
||||||
|
_debug "Attempting to load variable PANOS_TEMPLATE from file."
|
||||||
|
_getdeployconf PANOS_TEMPLATE
|
||||||
|
fi
|
||||||
|
|
||||||
|
# PANOS_TEMPLATE_STACK
|
||||||
|
if [ "$PANOS_TEMPLATE_STACK" ]; then
|
||||||
|
_debug "Detected ENV variable PANOS_TEMPLATE_STACK. Saving to file."
|
||||||
|
_savedeployconf PANOS_TEMPLATE_STACK "$PANOS_TEMPLATE_STACK" 1
|
||||||
|
else
|
||||||
|
_debug "Attempting to load variable PANOS_TEMPLATE_STACK from file."
|
||||||
|
_getdeployconf PANOS_TEMPLATE_STACK
|
||||||
|
fi
|
||||||
|
|
||||||
|
# PANOS_TEMPLATE_STACK
|
||||||
|
if [ "$PANOS_VSYS" ]; then
|
||||||
|
_debug "Detected ENV variable PANOS_VSYS. Saving to file."
|
||||||
|
_savedeployconf PANOS_VSYS "$PANOS_VSYS" 1
|
||||||
|
else
|
||||||
|
_debug "Attempting to load variable PANOS_VSYS from file."
|
||||||
|
_getdeployconf PANOS_VSYS
|
||||||
|
fi
|
||||||
|
|
||||||
|
# PANOS_CERTNAME
|
||||||
|
if [ "$PANOS_CERTNAME" ]; then
|
||||||
|
_debug "Detected ENV variable PANOS_CERTNAME. Saving to file."
|
||||||
|
_savedeployconf PANOS_CERTNAME "$PANOS_CERTNAME" 1
|
||||||
|
else
|
||||||
|
_debug "Attempting to load variable PANOS_CERTNAME from file."
|
||||||
|
_getdeployconf PANOS_CERTNAME
|
||||||
|
fi
|
||||||
|
|
||||||
|
#Store variables
|
||||||
|
_panos_host=$PANOS_HOST
|
||||||
|
_panos_user=$PANOS_USER
|
||||||
|
_panos_pass=$PANOS_PASS
|
||||||
|
_panos_template=$PANOS_TEMPLATE
|
||||||
|
_panos_template_stack=$PANOS_TEMPLATE_STACK
|
||||||
|
_panos_vsys=$PANOS_VSYS
|
||||||
|
_panos_certname=$PANOS_CERTNAME
|
||||||
|
|
||||||
|
#Test API Key if found. If the key is invalid, the variable _panos_key will be unset.
|
||||||
|
if [ "$_panos_host" ] && [ "$_panos_key" ]; then
|
||||||
|
_debug "**** Testing API KEY ****"
|
||||||
|
deployer keytest
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check for valid variables
|
||||||
|
if [ -z "$_panos_host" ]; then
|
||||||
|
_err "No host found. If this is your first time deploying, please set PANOS_HOST in ENV variables. You can delete it after you have successfully deployed the certs."
|
||||||
|
return 1
|
||||||
|
elif [ -z "$_panos_user" ]; then
|
||||||
|
_err "No user found. If this is your first time deploying, please set PANOS_USER in ENV variables. You can delete it after you have successfully deployed the certs."
|
||||||
|
return 1
|
||||||
|
elif [ -z "$_panos_pass" ]; then
|
||||||
|
_err "No password found. If this is your first time deploying, please set PANOS_PASS in ENV variables. You can delete it after you have successfully deployed the certs."
|
||||||
return 1
|
return 1
|
||||||
else
|
else
|
||||||
_debug "Getting PANOS KEY"
|
# Use certificate name based on the first domain on the certificate if no custom certificate name is set
|
||||||
deployer keygen
|
if [ -z "$_panos_certname" ]; then
|
||||||
|
_panos_certname="$_cdomain"
|
||||||
|
_savedeployconf PANOS_CERTNAME "$_panos_certname" 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Generate a new API key if no valid API key is found
|
||||||
if [ -z "$_panos_key" ]; then
|
if [ -z "$_panos_key" ]; then
|
||||||
_err "Missing apikey."
|
_debug "**** Generating new PANOS API KEY ****"
|
||||||
|
deployer keygen
|
||||||
|
_savedeployconf PANOS_KEY "$_panos_key" 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Confirm that a valid key was generated
|
||||||
|
if [ -z "$_panos_key" ]; then
|
||||||
|
_err "Unable to generate an API key. The user and pass may be invalid or not authorized to generate a new key. Please check the PANOS_USER and PANOS_PASS credentials and try again"
|
||||||
return 1
|
return 1
|
||||||
else
|
else
|
||||||
deployer cert
|
deployer cert
|
||||||
deployer key
|
deployer key
|
||||||
deployer commit
|
deployer commit
|
||||||
|
if [ "$_panos_template_stack" ]; then
|
||||||
|
# try to get job status for 20 times in 30 sec interval
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt $_COMMIT_WAIT_ITERATIONS ]; do
|
||||||
|
deployer job_status
|
||||||
|
if [ "$_commit_job_status" = "OK" ]; then
|
||||||
|
echo "Commit finished!"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
sleep $_COMMIT_WAIT_INTERVAL
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
deployer push
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|||||||
130
deploy/proxmoxbs.sh
Normal file
130
deploy/proxmoxbs.sh
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
#!/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}"
|
||||||
|
response=$(_post "$_json_payload" "$_target_url" "" POST "application/json")
|
||||||
|
_retval=$?
|
||||||
|
if [ "${_retval}" -eq 0 ]; then
|
||||||
|
_debug3 response "$response"
|
||||||
|
_info "Certificate successfully deployed"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
_err "Certificate deployment failed"
|
||||||
|
_debug "Response" "$response"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
}
|
||||||
@@ -99,11 +99,11 @@ proxmoxve_deploy() {
|
|||||||
_proxmoxve_api_token_key="$DEPLOY_PROXMOXVE_API_TOKEN_KEY"
|
_proxmoxve_api_token_key="$DEPLOY_PROXMOXVE_API_TOKEN_KEY"
|
||||||
_savedeployconf DEPLOY_PROXMOXVE_API_TOKEN_KEY "$DEPLOY_PROXMOXVE_API_TOKEN_KEY"
|
_savedeployconf DEPLOY_PROXMOXVE_API_TOKEN_KEY "$DEPLOY_PROXMOXVE_API_TOKEN_KEY"
|
||||||
fi
|
fi
|
||||||
_debug2 DEPLOY_PROXMOXVE_API_TOKEN_KEY _proxmoxve_api_token_key
|
_debug2 DEPLOY_PROXMOXVE_API_TOKEN_KEY "$_proxmoxve_api_token_key"
|
||||||
|
|
||||||
# PVE API Token header value. Used in "Authorization: PVEAPIToken".
|
# PVE API Token header value. Used in "Authorization: PVEAPIToken".
|
||||||
_proxmoxve_header_api_token="${_proxmoxve_user}@${_proxmoxve_user_realm}!${_proxmoxve_api_token_name}=${_proxmoxve_api_token_key}"
|
_proxmoxve_header_api_token="${_proxmoxve_user}@${_proxmoxve_user_realm}!${_proxmoxve_api_token_name}=${_proxmoxve_api_token_key}"
|
||||||
_debug2 "Auth Header" _proxmoxve_header_api_token
|
_debug2 "Auth Header" "$_proxmoxve_header_api_token"
|
||||||
|
|
||||||
# Ugly. I hate putting heredocs inside functions because heredocs don't
|
# Ugly. I hate putting heredocs inside functions because heredocs don't
|
||||||
# account for whitespace correctly but it _does_ work and is several times
|
# account for whitespace correctly but it _does_ work and is several times
|
||||||
@@ -124,9 +124,19 @@ HEREDOC
|
|||||||
)
|
)
|
||||||
_debug2 Payload "$_json_payload"
|
_debug2 Payload "$_json_payload"
|
||||||
|
|
||||||
# Push certificates to server.
|
_info "Push certificates to server"
|
||||||
export _HTTPS_INSECURE=1
|
export HTTPS_INSECURE=1
|
||||||
export _H1="Authorization: PVEAPIToken=${_proxmoxve_header_api_token}"
|
export _H1="Authorization: PVEAPIToken=${_proxmoxve_header_api_token}"
|
||||||
_post "$_json_payload" "$_target_url" "" POST "application/json"
|
response=$(_post "$_json_payload" "$_target_url" "" POST "application/json")
|
||||||
|
_retval=$?
|
||||||
|
if [ "${_retval}" -eq 0 ]; then
|
||||||
|
_debug3 response "$response"
|
||||||
|
_info "Certificate successfully deployed"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
_err "Certificate deployment failed"
|
||||||
|
_debug "Response" "$response"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -137,17 +137,18 @@ routeros_deploy() {
|
|||||||
return $_err_code
|
return $_err_code
|
||||||
fi
|
fi
|
||||||
|
|
||||||
DEPLOY_SCRIPT_CMD="/system script add name=\"LE Cert Deploy - $_cdomain\" owner=$ROUTER_OS_USERNAME \
|
DEPLOY_SCRIPT_CMD=":do {/system script remove \"LECertDeploy-$_cdomain\" } on-error={ }; \
|
||||||
|
/system script add name=\"LECertDeploy-$_cdomain\" owner=$ROUTER_OS_USERNAME \
|
||||||
comment=\"generated by routeros deploy script in acme.sh\" \
|
comment=\"generated by routeros deploy script in acme.sh\" \
|
||||||
source=\"/certificate remove [ find name=$_cdomain.cer_0 ];\
|
source=\"/certificate remove [ find name=$_cdomain.cer_0 ];\
|
||||||
\n/certificate remove [ find name=$_cdomain.cer_1 ];\
|
\n/certificate remove [ find name=$_cdomain.cer_1 ];\
|
||||||
\n/certificate remove [ find name=$_cdomain.cer_2 ];\
|
\n/certificate remove [ find name=$_cdomain.cer_2 ];\
|
||||||
\ndelay 1;\
|
\ndelay 1;\
|
||||||
\n/certificate import file-name=$_cdomain.cer passphrase=\\\"\\\";\
|
\n/certificate import file-name=\\\"$_cdomain.cer\\\" passphrase=\\\"\\\";\
|
||||||
\n/certificate import file-name=$_cdomain.key passphrase=\\\"\\\";\
|
\n/certificate import file-name=\\\"$_cdomain.key\\\" passphrase=\\\"\\\";\
|
||||||
\ndelay 1;\
|
\ndelay 1;\
|
||||||
\n/file remove $_cdomain.cer;\
|
\n:do {/file remove $_cdomain.cer; } on-error={ }\
|
||||||
\n/file remove $_cdomain.key;\
|
\n:do {/file remove $_cdomain.key; } on-error={ }\
|
||||||
\ndelay 2;\
|
\ndelay 2;\
|
||||||
\n/ip service set www-ssl certificate=$_cdomain.cer_0;\
|
\n/ip service set www-ssl certificate=$_cdomain.cer_0;\
|
||||||
\n$ROUTER_OS_ADDITIONAL_SERVICES;\
|
\n$ROUTER_OS_ADDITIONAL_SERVICES;\
|
||||||
@@ -158,11 +159,11 @@ source=\"/certificate remove [ find name=$_cdomain.cer_0 ];\
|
|||||||
return $_err_code
|
return $_err_code
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! _ssh_remote_cmd "/system script run \"LE Cert Deploy - $_cdomain\""; then
|
if ! _ssh_remote_cmd "/system script run \"LECertDeploy-$_cdomain\""; then
|
||||||
return $_err_code
|
return $_err_code
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! _ssh_remote_cmd "/system script remove \"LE Cert Deploy - $_cdomain\""; then
|
if ! _ssh_remote_cmd "/system script remove \"LECertDeploy-$_cdomain\""; then
|
||||||
return $_err_code
|
return $_err_code
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
200
deploy/ruckus.sh
Executable file
200
deploy/ruckus.sh
Executable 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
|
||||||
|
}
|
||||||
@@ -10,46 +10,89 @@
|
|||||||
|
|
||||||
#domain keyfile certfile cafile fullchain
|
#domain keyfile certfile cafile fullchain
|
||||||
strongswan_deploy() {
|
strongswan_deploy() {
|
||||||
_cdomain="$1"
|
_cdomain="${1}"
|
||||||
_ckey="$2"
|
_ckey="${2}"
|
||||||
_ccert="$3"
|
_ccert="${3}"
|
||||||
_cca="$4"
|
_cca="${4}"
|
||||||
_cfullchain="$5"
|
_cfullchain="${5}"
|
||||||
|
|
||||||
_info "Using strongswan"
|
_info "Using strongswan"
|
||||||
|
if _exists ipsec; then
|
||||||
if [ -x /usr/sbin/ipsec ]; then
|
_ipsec=ipsec
|
||||||
_ipsec=/usr/sbin/ipsec
|
elif _exists strongswan; then
|
||||||
elif [ -x /usr/sbin/strongswan ]; then
|
_ipsec=strongswan
|
||||||
_ipsec=/usr/sbin/strongswan
|
fi
|
||||||
elif [ -x /usr/local/sbin/ipsec ]; then
|
if _exists swanctl; then
|
||||||
_ipsec=/usr/local/sbin/ipsec
|
_swanctl=swanctl
|
||||||
else
|
fi
|
||||||
|
# For legacy stroke mode
|
||||||
|
if [ -n "${_ipsec}" ]; then
|
||||||
|
_info "${_ipsec} command detected"
|
||||||
|
_confdir=$(${_ipsec} --confdir)
|
||||||
|
if [ -z "${_confdir}" ]; then
|
||||||
|
_err "no strongswan --confdir is detected"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_info _confdir "${_confdir}"
|
||||||
|
__deploy_cert "stroke" "${_confdir}" "$@"
|
||||||
|
${_ipsec} reload
|
||||||
|
fi
|
||||||
|
# For modern vici mode
|
||||||
|
if [ -n "${_swanctl}" ]; then
|
||||||
|
_info "${_swanctl} command detected"
|
||||||
|
for _dir in /usr/local/etc/swanctl /etc/swanctl /etc/strongswan/swanctl; do
|
||||||
|
if [ -d ${_dir} ]; then
|
||||||
|
_confdir=${_dir}
|
||||||
|
_info _confdir "${_confdir}"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [ -z "${_confdir}" ]; then
|
||||||
|
_err "no swanctl config dir is found"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
__deploy_cert "vici" "${_confdir}" "$@"
|
||||||
|
${_swanctl} --load-creds
|
||||||
|
fi
|
||||||
|
if [ -z "${_swanctl}" ] && [ -z "${_ipsec}" ]; then
|
||||||
_err "no strongswan or ipsec command is detected"
|
_err "no strongswan or ipsec command is detected"
|
||||||
|
_err "no swanctl is detected"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
}
|
||||||
_info _ipsec "$_ipsec"
|
|
||||||
|
#################### Private functions below ##################################
|
||||||
_confdir=$($_ipsec --confdir)
|
|
||||||
if [ $? -ne 0 ] || [ -z "$_confdir" ]; then
|
__deploy_cert() {
|
||||||
_err "no strongswan --confdir is detected"
|
_swan_mode="${1}"
|
||||||
return 1
|
_confdir="${2}"
|
||||||
fi
|
_cdomain="${3}"
|
||||||
|
_ckey="${4}"
|
||||||
_info _confdir "$_confdir"
|
_ccert="${5}"
|
||||||
|
_cca="${6}"
|
||||||
_debug _cdomain "$_cdomain"
|
_cfullchain="${7}"
|
||||||
_debug _ckey "$_ckey"
|
_debug _cdomain "${_cdomain}"
|
||||||
_debug _ccert "$_ccert"
|
_debug _ckey "${_ckey}"
|
||||||
_debug _cca "$_cca"
|
_debug _ccert "${_ccert}"
|
||||||
_debug _cfullchain "$_cfullchain"
|
_debug _cca "${_cca}"
|
||||||
|
_debug _cfullchain "${_cfullchain}"
|
||||||
cat "$_ckey" >"${_confdir}/ipsec.d/private/$(basename "$_ckey")"
|
_debug _swan_mode "${_swan_mode}"
|
||||||
cat "$_ccert" >"${_confdir}/ipsec.d/certs/$(basename "$_ccert")"
|
_debug _confdir "${_confdir}"
|
||||||
cat "$_cca" >"${_confdir}/ipsec.d/cacerts/$(basename "$_cca")"
|
if [ "${_swan_mode}" = "vici" ]; then
|
||||||
cat "$_cfullchain" >"${_confdir}/ipsec.d/cacerts/$(basename "$_cfullchain")"
|
_dir_private="private"
|
||||||
|
_dir_cert="x509"
|
||||||
$_ipsec reload
|
_dir_ca="x509ca"
|
||||||
|
elif [ "${_swan_mode}" = "stroke" ]; then
|
||||||
|
_dir_private="ipsec.d/private"
|
||||||
|
_dir_cert="ipsec.d/certs"
|
||||||
|
_dir_ca="ipsec.d/cacerts"
|
||||||
|
else
|
||||||
|
_err "unknown StrongSwan mode ${_swan_mode}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
cat "${_ckey}" >"${_confdir}/${_dir_private}/$(basename "${_ckey}")"
|
||||||
|
cat "${_ccert}" >"${_confdir}/${_dir_cert}/$(basename "${_ccert}")"
|
||||||
|
cat "${_cca}" >"${_confdir}/${_dir_ca}/$(basename "${_cca}")"
|
||||||
|
if [ "${_swan_mode}" = "stroke" ]; then
|
||||||
|
cat "${_cfullchain}" >"${_confdir}/${_dir_ca}/$(basename "${_cfullchain}")"
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,34 +1,53 @@
|
|||||||
#!/usr/bin/env sh
|
#!/bin/bash
|
||||||
|
|
||||||
# Here is a script to deploy cert to Synology DSM
|
################################################################################
|
||||||
#
|
# ACME.sh 3rd party deploy plugin for Synology DSM
|
||||||
# It requires following environment variables:
|
################################################################################
|
||||||
#
|
# Authors: Brian Hartvigsen (creator), https://github.com/tresni
|
||||||
# SYNO_Username - Synology Username to login (must be an administrator)
|
# Martin Arndt (contributor), https://troublezone.net/
|
||||||
# SYNO_Password - Synology Password to login
|
# Updated: 2023-07-03
|
||||||
# SYNO_Certificate - Certificate description to target for replacement
|
# Issues: https://github.com/acmesh-official/acme.sh/issues/2727
|
||||||
#
|
################################################################################
|
||||||
# The following environmental variables may be set if you don't like their
|
# Usage (shown values are the examples):
|
||||||
# default values:
|
# 1. Set required environment variables:
|
||||||
#
|
# - use automatically created temp admin user to authenticate
|
||||||
# SYNO_Scheme - defaults to http
|
# export SYNO_USE_TEMP_ADMIN=1
|
||||||
# SYNO_Hostname - defaults to localhost
|
# - or provide your own admin user credential to authenticate
|
||||||
# SYNO_Port - defaults to 5000
|
# 1. export SYNO_USERNAME="adminUser"
|
||||||
# SYNO_DID - device ID to skip OTP - defaults to empty
|
# 2. export SYNO_PASSWORD="adminPassword"
|
||||||
# SYNO_TOTP_SECRET - TOTP secret to generate OTP - defaults to empty
|
# 2. Set optional environment variables
|
||||||
#
|
# - common optional variables
|
||||||
|
# - export SYNO_SCHEME="http" - defaults to "http"
|
||||||
|
# - export SYNO_HOSTNAME="localhost" - defaults to "localhost"
|
||||||
|
# - export SYNO_PORT="5000" - defaults to "5000"
|
||||||
|
# - export SYNO_CREATE=1 - to allow creating the cert if it doesn't exist
|
||||||
|
# - export SYNO_CERTIFICATE="" - to replace a specific cert by its
|
||||||
|
# description
|
||||||
|
# - temp admin optional variables
|
||||||
|
# - export SYNO_LOCAL_HOSTNAME=1 - if set to 1, force to treat hostname is
|
||||||
|
# targeting current local machine (since
|
||||||
|
# this method only locally supported)
|
||||||
|
# - exsiting admin 2FA-OTP optional variables
|
||||||
|
# - export SYNO_OTP_CODE="XXXXXX" - if set, script won't require to
|
||||||
|
# interactive input the OTP code
|
||||||
|
# - export SYNO_DEVICE_NAME="CertRenewal" - if set, script won't require to
|
||||||
|
# interactive input the device name
|
||||||
|
# - export SYNO_DEVICE_ID="" - (deprecated, auth with OTP code instead)
|
||||||
|
# required for omitting 2FA-OTP
|
||||||
|
# 3. Run command:
|
||||||
|
# acme.sh --deploy --deploy-hook synology_dsm -d example.com
|
||||||
|
################################################################################
|
||||||
# Dependencies:
|
# Dependencies:
|
||||||
# -------------
|
# - curl
|
||||||
# - jq and curl
|
# - synouser & synogroup & synosetkeyvalue (Required for SYNO_USE_TEMP_ADMIN=1)
|
||||||
# - oathtool (When using 2 Factor Authentication and SYNO_TOTP_SECRET is set)
|
################################################################################
|
||||||
#
|
# Return value:
|
||||||
#returns 0 means success, otherwise error.
|
# 0 means success, otherwise error.
|
||||||
|
################################################################################
|
||||||
######## Public functions #####################
|
|
||||||
|
|
||||||
|
########## Public functions ####################################################
|
||||||
#domain keyfile certfile cafile fullchain
|
#domain keyfile certfile cafile fullchain
|
||||||
synology_dsm_deploy() {
|
synology_dsm_deploy() {
|
||||||
|
|
||||||
_cdomain="$1"
|
_cdomain="$1"
|
||||||
_ckey="$2"
|
_ckey="$2"
|
||||||
_ccert="$3"
|
_ccert="$3"
|
||||||
@@ -36,148 +55,391 @@ synology_dsm_deploy() {
|
|||||||
|
|
||||||
_debug _cdomain "$_cdomain"
|
_debug _cdomain "$_cdomain"
|
||||||
|
|
||||||
# Get Username and Password, but don't save until we successfully authenticate
|
# Get username and password, but don't save until we authenticated successfully
|
||||||
_getdeployconf SYNO_Username
|
_migratedeployconf SYNO_Username SYNO_USERNAME
|
||||||
_getdeployconf SYNO_Password
|
_migratedeployconf SYNO_Password SYNO_PASSWORD
|
||||||
_getdeployconf SYNO_Create
|
_migratedeployconf SYNO_Device_ID SYNO_DEVICE_ID
|
||||||
_getdeployconf SYNO_DID
|
_migratedeployconf SYNO_Device_Name SYNO_DEVICE_NAME
|
||||||
_getdeployconf SYNO_TOTP_SECRET
|
_getdeployconf SYNO_USERNAME
|
||||||
if [ -z "${SYNO_Username:-}" ] || [ -z "${SYNO_Password:-}" ]; then
|
_getdeployconf SYNO_PASSWORD
|
||||||
_err "SYNO_Username & SYNO_Password must be set"
|
_getdeployconf SYNO_DEVICE_ID
|
||||||
|
_getdeployconf SYNO_DEVICE_NAME
|
||||||
|
|
||||||
|
# Prepare to use temp admin if SYNO_USE_TEMP_ADMIN is set
|
||||||
|
_getdeployconf SYNO_USE_TEMP_ADMIN
|
||||||
|
_check2cleardeployconfexp SYNO_USE_TEMP_ADMIN
|
||||||
|
_debug2 SYNO_USE_TEMP_ADMIN "$SYNO_USE_TEMP_ADMIN"
|
||||||
|
|
||||||
|
if [ -n "$SYNO_USE_TEMP_ADMIN" ]; then
|
||||||
|
if ! _exists synouser || ! _exists synogroup || ! _exists synosetkeyvalue; then
|
||||||
|
_err "Missing required tools to creat temp admin user, please set SYNO_USERNAME and SYNO_PASSWORD instead."
|
||||||
|
_err "Notice: temp admin user authorization method only supports local deployment on DSM."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if synouser --help 2>&1 | grep -q 'Permission denied'; then
|
||||||
|
_err "For creating temp admin user, the deploy script must be run as root."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
[ -n "$SYNO_USERNAME" ] || _savedeployconf SYNO_USERNAME ""
|
||||||
|
[ -n "$SYNO_PASSWORD" ] || _savedeployconf SYNO_PASSWORD ""
|
||||||
|
|
||||||
|
_debug "Setting temp admin user credential..."
|
||||||
|
SYNO_USERNAME=sc-acmesh-tmp
|
||||||
|
SYNO_PASSWORD=$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 16)
|
||||||
|
# Set 2FA-OTP settings to empty consider they won't be needed.
|
||||||
|
SYNO_DEVICE_ID=
|
||||||
|
SYNO_DEVICE_NAME=
|
||||||
|
SYNO_OTP_CODE=
|
||||||
|
else
|
||||||
|
_debug2 SYNO_USERNAME "$SYNO_USERNAME"
|
||||||
|
_secure_debug2 SYNO_PASSWORD "$SYNO_PASSWORD"
|
||||||
|
_debug2 SYNO_DEVICE_NAME "$SYNO_DEVICE_NAME"
|
||||||
|
_secure_debug2 SYNO_DEVICE_ID "$SYNO_DEVICE_ID"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$SYNO_USERNAME" ] || [ -z "$SYNO_PASSWORD" ]; then
|
||||||
|
_err "You must set either SYNO_USE_TEMP_ADMIN, or set both SYNO_USERNAME and SYNO_PASSWORD."
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
_debug2 SYNO_Username "$SYNO_Username"
|
|
||||||
_secure_debug2 SYNO_Password "$SYNO_Password"
|
|
||||||
|
|
||||||
# Optional scheme, hostname, and port for Synology DSM
|
# Optional scheme, hostname and port for Synology DSM
|
||||||
_getdeployconf SYNO_Scheme
|
_migratedeployconf SYNO_Scheme SYNO_SCHEME
|
||||||
_getdeployconf SYNO_Hostname
|
_migratedeployconf SYNO_Hostname SYNO_HOSTNAME
|
||||||
_getdeployconf SYNO_Port
|
_migratedeployconf SYNO_Port SYNO_PORT
|
||||||
|
_getdeployconf SYNO_SCHEME
|
||||||
|
_getdeployconf SYNO_HOSTNAME
|
||||||
|
_getdeployconf SYNO_PORT
|
||||||
|
|
||||||
# default vaules for scheme, hostname, and port
|
# Default values for scheme, hostname and port
|
||||||
# defaulting to localhost and http because it's localhost...
|
# Defaulting to localhost and http, because it's localhost…
|
||||||
[ -n "${SYNO_Scheme}" ] || SYNO_Scheme="http"
|
[ -n "$SYNO_SCHEME" ] || SYNO_SCHEME=http
|
||||||
[ -n "${SYNO_Hostname}" ] || SYNO_Hostname="localhost"
|
[ -n "$SYNO_HOSTNAME" ] || SYNO_HOSTNAME=localhost
|
||||||
[ -n "${SYNO_Port}" ] || SYNO_Port="5000"
|
[ -n "$SYNO_PORT" ] || SYNO_PORT=5000
|
||||||
|
_savedeployconf SYNO_SCHEME "$SYNO_SCHEME"
|
||||||
|
_savedeployconf SYNO_HOSTNAME "$SYNO_HOSTNAME"
|
||||||
|
_savedeployconf SYNO_PORT "$SYNO_PORT"
|
||||||
|
_debug2 SYNO_SCHEME "$SYNO_SCHEME"
|
||||||
|
_debug2 SYNO_HOSTNAME "$SYNO_HOSTNAME"
|
||||||
|
_debug2 SYNO_PORT "$SYNO_PORT"
|
||||||
|
|
||||||
_savedeployconf SYNO_Scheme "$SYNO_Scheme"
|
# Get the certificate description, but don't save it until we verify it's real
|
||||||
_savedeployconf SYNO_Hostname "$SYNO_Hostname"
|
_migratedeployconf SYNO_Certificate SYNO_CERTIFICATE "base64"
|
||||||
_savedeployconf SYNO_Port "$SYNO_Port"
|
_getdeployconf SYNO_CERTIFICATE
|
||||||
|
_check2cleardeployconfexp SYNO_CERTIFICATE
|
||||||
_debug2 SYNO_Scheme "$SYNO_Scheme"
|
_debug SYNO_CERTIFICATE "${SYNO_CERTIFICATE:-}"
|
||||||
_debug2 SYNO_Hostname "$SYNO_Hostname"
|
|
||||||
_debug2 SYNO_Port "$SYNO_Port"
|
|
||||||
|
|
||||||
# Get the certificate description, but don't save it until we verfiy it's real
|
|
||||||
_getdeployconf SYNO_Certificate
|
|
||||||
_debug SYNO_Certificate "${SYNO_Certificate:-}"
|
|
||||||
|
|
||||||
# shellcheck disable=SC1003 # We are not trying to escape a single quote
|
# shellcheck disable=SC1003 # We are not trying to escape a single quote
|
||||||
if printf "%s" "$SYNO_Certificate" | grep '\\'; then
|
if printf "%s" "$SYNO_CERTIFICATE" | grep '\\'; then
|
||||||
_err "Do not use a backslash (\) in your certificate description"
|
_err "Do not use a backslash (\) in your certificate description"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
_base_url="$SYNO_Scheme://$SYNO_Hostname:$SYNO_Port"
|
_debug "Getting API version..."
|
||||||
|
_base_url="$SYNO_SCHEME://$SYNO_HOSTNAME:$SYNO_PORT"
|
||||||
_debug _base_url "$_base_url"
|
_debug _base_url "$_base_url"
|
||||||
|
|
||||||
_debug "Getting API version"
|
|
||||||
response=$(_get "$_base_url/webapi/query.cgi?api=SYNO.API.Info&version=1&method=query&query=SYNO.API.Auth")
|
response=$(_get "$_base_url/webapi/query.cgi?api=SYNO.API.Info&version=1&method=query&query=SYNO.API.Auth")
|
||||||
|
api_path=$(echo "$response" | grep "SYNO.API.Auth" | sed -n 's/.*"path" *: *"\([^"]*\)".*/\1/p')
|
||||||
api_version=$(echo "$response" | grep "SYNO.API.Auth" | sed -n 's/.*"maxVersion" *: *\([0-9]*\).*/\1/p')
|
api_version=$(echo "$response" | grep "SYNO.API.Auth" | sed -n 's/.*"maxVersion" *: *\([0-9]*\).*/\1/p')
|
||||||
_debug3 response "$response"
|
_debug3 response "$response"
|
||||||
|
_debug3 api_path "$api_path"
|
||||||
_debug3 api_version "$api_version"
|
_debug3 api_version "$api_version"
|
||||||
|
|
||||||
# Login, get the token from JSON and session id from cookie
|
# Login, get the session ID and SynoToken from JSON
|
||||||
_info "Logging into $SYNO_Hostname:$SYNO_Port"
|
_info "Logging into $SYNO_HOSTNAME:$SYNO_PORT..."
|
||||||
encoded_username="$(printf "%s" "$SYNO_Username" | _url_encode)"
|
encoded_username="$(printf "%s" "$SYNO_USERNAME" | _url_encode)"
|
||||||
encoded_password="$(printf "%s" "$SYNO_Password" | _url_encode)"
|
encoded_password="$(printf "%s" "$SYNO_PASSWORD" | _url_encode)"
|
||||||
|
|
||||||
|
# ## START ## - DEPRECATED, for backward compatibility
|
||||||
|
_getdeployconf SYNO_TOTP_SECRET
|
||||||
|
|
||||||
otp_code=""
|
|
||||||
if [ -n "$SYNO_TOTP_SECRET" ]; then
|
if [ -n "$SYNO_TOTP_SECRET" ]; then
|
||||||
if _exists oathtool; then
|
_info "WARNING: Usage of SYNO_TOTP_SECRET is deprecated!"
|
||||||
otp_code="$(oathtool --base32 --totp "${SYNO_TOTP_SECRET}" 2>/dev/null)"
|
_info " See synology_dsm.sh script or ACME.sh Wiki page for details:"
|
||||||
else
|
_info " https://github.com/acmesh-official/acme.sh/wiki/Synology-NAS-Guide"
|
||||||
|
if ! _exists oathtool; then
|
||||||
_err "oathtool could not be found, install oathtool to use SYNO_TOTP_SECRET"
|
_err "oathtool could not be found, install oathtool to use SYNO_TOTP_SECRET"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
DEPRECATED_otp_code="$(oathtool --base32 --totp "$SYNO_TOTP_SECRET" 2>/dev/null)"
|
||||||
|
|
||||||
|
if [ -z "$SYNO_DEVICE_ID" ]; then
|
||||||
|
_getdeployconf SYNO_DID
|
||||||
|
[ -n "$SYNO_DID" ] || SYNO_DEVICE_ID="$SYNO_DID"
|
||||||
|
fi
|
||||||
|
if [ -n "$SYNO_DEVICE_ID" ]; then
|
||||||
|
_H1="Cookie: did=$SYNO_DEVICE_ID"
|
||||||
|
export _H1
|
||||||
|
_debug3 H1 "${_H1}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
response=$(_post "method=login&account=$encoded_username&passwd=$encoded_password&api=SYNO.API.Auth&version=$api_version&enable_syno_token=yes&otp_code=$DEPRECATED_otp_code&device_name=certrenewal&device_id=$SYNO_DEVICE_ID" "$_base_url/webapi/$api_path?enable_syno_token=yes")
|
||||||
|
_debug3 response "$response"
|
||||||
|
# ## END ## - DEPRECATED, for backward compatibility
|
||||||
|
# If SYNO_DEVICE_ID or SYNO_OTP_CODE is set, we treat current account enabled 2FA-OTP.
|
||||||
|
# Notice that if SYNO_USE_TEMP_ADMIN=1, both variables will be unset
|
||||||
|
else
|
||||||
|
if [ -n "$SYNO_DEVICE_ID" ] || [ -n "$SYNO_OTP_CODE" ]; then
|
||||||
|
response='{"error":{"code":403}}'
|
||||||
|
# Assume the current account disabled 2FA-OTP, try to log in right away.
|
||||||
|
else
|
||||||
|
if [ -n "$SYNO_USE_TEMP_ADMIN" ]; then
|
||||||
|
_getdeployconf SYNO_LOCAL_HOSTNAME
|
||||||
|
_debug SYNO_LOCAL_HOSTNAME "${SYNO_LOCAL_HOSTNAME:-}"
|
||||||
|
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
|
||||||
|
fi
|
||||||
|
_debug "Creating temp admin user in Synology DSM..."
|
||||||
|
if synogroup --help | grep -q '\-\-memberadd '; then
|
||||||
|
_temp_admin_create "$SYNO_USERNAME" "$SYNO_PASSWORD"
|
||||||
|
synogroup --memberadd administrators "$SYNO_USERNAME" >/dev/null
|
||||||
|
elif synogroup --help | grep -q '\-\-member '; then
|
||||||
|
# For supporting DSM 6.x which only has `--member` parameter.
|
||||||
|
cur_admins=$(synogroup --get administrators | awk -F '[][]' '/Group Members/,0{if(NF>1)printf "%s ", $2}')
|
||||||
|
if [ -n "$cur_admins" ]; then
|
||||||
|
_temp_admin_create "$SYNO_USERNAME" "$SYNO_PASSWORD"
|
||||||
|
_secure_debug3 admin_users "$cur_admins$SYNO_USERNAME"
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
synogroup --member administrators $cur_admins $SYNO_USERNAME >/dev/null
|
||||||
|
else
|
||||||
|
_err "The tool synogroup may be broken, please set SYNO_USERNAME and SYNO_PASSWORD instead."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
_err "Unsupported synogroup tool detected, please set SYNO_USERNAME and SYNO_PASSWORD instead."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
# havig a workaround to temporary disable enforce 2FA-OTP, will restore
|
||||||
|
# it soon (after a single request), though if any accident occurs like
|
||||||
|
# unexpected interruption, this setting can be easily reverted manually.
|
||||||
|
otp_enforce_option=$(synogetkeyvalue /etc/synoinfo.conf otp_enforce_option)
|
||||||
|
if [ -n "$otp_enforce_option" ] && [ "${otp_enforce_option:-"none"}" != "none" ]; then
|
||||||
|
synosetkeyvalue /etc/synoinfo.conf otp_enforce_option none
|
||||||
|
_info "Enforcing 2FA-OTP has been disabled to complete temp admin authentication."
|
||||||
|
_info "Notice: it will be restored soon, if not, you can restore it manually via Control Panel."
|
||||||
|
_info "previous_otp_enforce_option" "$otp_enforce_option"
|
||||||
|
else
|
||||||
|
otp_enforce_option=""
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
response=$(_get "$_base_url/webapi/$api_path?api=SYNO.API.Auth&version=$api_version&method=login&format=sid&account=$encoded_username&passwd=$encoded_password&enable_syno_token=yes")
|
||||||
|
if [ -n "$SYNO_USE_TEMP_ADMIN" ] && [ -n "$otp_enforce_option" ]; then
|
||||||
|
synosetkeyvalue /etc/synoinfo.conf otp_enforce_option "$otp_enforce_option"
|
||||||
|
_info "Restored previous enforce 2FA-OTP option."
|
||||||
|
fi
|
||||||
|
_debug3 response "$response"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -n "$SYNO_DID" ]; then
|
error_code=$(echo "$response" | grep '"error":' | grep -o '"code":[0-9]*' | grep -o '[0-9]*')
|
||||||
_H1="Cookie: did=$SYNO_DID"
|
_debug2 error_code "$error_code"
|
||||||
export _H1
|
# Account has 2FA-OTP enabled, since error 403 reported.
|
||||||
_debug3 H1 "${_H1}"
|
# https://global.download.synology.com/download/Document/Software/DeveloperGuide/Os/DSM/All/enu/DSM_Login_Web_API_Guide_enu.pdf
|
||||||
|
if [ "$error_code" == "403" ]; then
|
||||||
|
if [ -z "$SYNO_DEVICE_NAME" ]; then
|
||||||
|
printf "Enter device name or leave empty for default (CertRenewal): "
|
||||||
|
read -r SYNO_DEVICE_NAME
|
||||||
|
[ -n "$SYNO_DEVICE_NAME" ] || SYNO_DEVICE_NAME="CertRenewal"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$SYNO_DEVICE_ID" ]; then
|
||||||
|
# Omit OTP code with SYNO_DEVICE_ID.
|
||||||
|
response=$(_get "$_base_url/webapi/$api_path?api=SYNO.API.Auth&version=$api_version&method=login&format=sid&account=$encoded_username&passwd=$encoded_password&enable_syno_token=yes&device_name=$SYNO_DEVICE_NAME&device_id=$SYNO_DEVICE_ID")
|
||||||
|
_secure_debug3 response "$response"
|
||||||
|
else
|
||||||
|
# Require the OTP code if still unset.
|
||||||
|
if [ -z "$SYNO_OTP_CODE" ]; then
|
||||||
|
printf "Enter OTP code for user '%s': " "$SYNO_USERNAME"
|
||||||
|
read -r SYNO_OTP_CODE
|
||||||
|
fi
|
||||||
|
_secure_debug SYNO_OTP_CODE "${SYNO_OTP_CODE:-}"
|
||||||
|
|
||||||
|
if [ -z "$SYNO_OTP_CODE" ]; then
|
||||||
|
response='{"error":{"code":404}}'
|
||||||
|
else
|
||||||
|
response=$(_get "$_base_url/webapi/$api_path?api=SYNO.API.Auth&version=$api_version&method=login&format=sid&account=$encoded_username&passwd=$encoded_password&enable_syno_token=yes&enable_device_token=yes&device_name=$SYNO_DEVICE_NAME&otp_code=$SYNO_OTP_CODE")
|
||||||
|
_secure_debug3 response "$response"
|
||||||
|
|
||||||
|
id_property='device_id'
|
||||||
|
[ "${api_version}" -gt '6' ] || id_property='did'
|
||||||
|
SYNO_DEVICE_ID=$(echo "$response" | grep "$id_property" | sed -n 's/.*"'$id_property'" *: *"\([^"]*\).*/\1/p')
|
||||||
|
_secure_debug2 SYNO_DEVICE_ID "$SYNO_DEVICE_ID"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
error_code=$(echo "$response" | grep '"error":' | grep -o '"code":[0-9]*' | grep -o '[0-9]*')
|
||||||
|
_debug2 error_code "$error_code"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
response=$(_post "method=login&account=$encoded_username&passwd=$encoded_password&api=SYNO.API.Auth&version=$api_version&enable_syno_token=yes&otp_code=$otp_code&device_name=certrenewal&device_id=$SYNO_DID" "$_base_url/webapi/auth.cgi?enable_syno_token=yes")
|
if [ -n "$error_code" ]; then
|
||||||
token=$(echo "$response" | grep "synotoken" | sed -n 's/.*"synotoken" *: *"\([^"]*\).*/\1/p')
|
if [ "$error_code" == "403" ] && [ -n "$SYNO_DEVICE_ID" ]; then
|
||||||
_debug3 response "$response"
|
_cleardeployconf SYNO_DEVICE_ID
|
||||||
_debug token "$token"
|
_err "Failed to authenticate with SYNO_DEVICE_ID (may expired or invalid), please try again in a new terminal window."
|
||||||
|
elif [ "$error_code" == "404" ]; then
|
||||||
if [ -z "$token" ]; then
|
_err "Failed to authenticate with provided 2FA-OTP code, please try again in a new terminal window."
|
||||||
_err "Unable to authenticate to $SYNO_Hostname:$SYNO_Port using $SYNO_Scheme."
|
elif [ "$error_code" == "406" ]; then
|
||||||
_err "Check your username and password."
|
if [ -n "$SYNO_USE_TEMP_ADMIN" ]; then
|
||||||
_err "If two-factor authentication is enabled for the user, set SYNO_TOTP_SECRET."
|
_err "Failed with unexcepted error, please report this by providing full log with '--debug 3'."
|
||||||
|
else
|
||||||
|
_err "Enforce auth with 2FA-OTP enabled, please configure the user to enable 2FA-OTP to continue."
|
||||||
|
fi
|
||||||
|
elif [ "$error_code" == "400" ]; then
|
||||||
|
_err "Failed to authenticate, no such account or incorrect password."
|
||||||
|
elif [ "$error_code" == "401" ]; then
|
||||||
|
_err "Failed to authenticate with a non-existent account."
|
||||||
|
elif [ "$error_code" == "408" ] || [ "$error_code" == "409" ] || [ "$error_code" == "410" ]; then
|
||||||
|
_err "Failed to authenticate, the account password has expired or must be changed."
|
||||||
|
else
|
||||||
|
_err "Failed to authenticate with error: $error_code."
|
||||||
|
fi
|
||||||
|
_temp_admin_cleanup "$SYNO_USE_TEMP_ADMIN" "$SYNO_USERNAME"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
sid=$(echo "$response" | grep "sid" | sed -n 's/.*"sid" *: *"\([^"]*\).*/\1/p')
|
sid=$(echo "$response" | grep "sid" | sed -n 's/.*"sid" *: *"\([^"]*\).*/\1/p')
|
||||||
|
token=$(echo "$response" | grep "synotoken" | sed -n 's/.*"synotoken" *: *"\([^"]*\).*/\1/p')
|
||||||
|
_debug "Session ID" "$sid"
|
||||||
|
_debug SynoToken "$token"
|
||||||
|
if [ -z "$sid" ] || [ -z "$token" ]; then
|
||||||
|
# Still can't get necessary info even got no errors, may Synology have API updated?
|
||||||
|
_err "Unable to authenticate to $_base_url, you may report this by providing full log with '--debug 3'."
|
||||||
|
_temp_admin_cleanup "$SYNO_USE_TEMP_ADMIN" "$SYNO_USERNAME"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
_H1="X-SYNO-TOKEN: $token"
|
_H1="X-SYNO-TOKEN: $token"
|
||||||
export _H1
|
export _H1
|
||||||
_debug2 H1 "${_H1}"
|
_debug2 H1 "${_H1}"
|
||||||
|
|
||||||
# Now that we know the username and password are good, save them
|
# Now that we know the username and password are good, save them if not in temp admin mode.
|
||||||
_savedeployconf SYNO_Username "$SYNO_Username"
|
if [ -n "$SYNO_USE_TEMP_ADMIN" ]; then
|
||||||
_savedeployconf SYNO_Password "$SYNO_Password"
|
_cleardeployconf SYNO_USERNAME
|
||||||
_savedeployconf SYNO_DID "$SYNO_DID"
|
_cleardeployconf SYNO_PASSWORD
|
||||||
_savedeployconf SYNO_TOTP_SECRET "$SYNO_TOTP_SECRET"
|
_cleardeployconf SYNO_DEVICE_ID
|
||||||
|
_cleardeployconf SYNO_DEVICE_NAME
|
||||||
|
_savedeployconf SYNO_USE_TEMP_ADMIN "$SYNO_USE_TEMP_ADMIN"
|
||||||
|
_savedeployconf SYNO_LOCAL_HOSTNAME "$SYNO_LOCAL_HOSTNAME"
|
||||||
|
else
|
||||||
|
_savedeployconf SYNO_USERNAME "$SYNO_USERNAME"
|
||||||
|
_savedeployconf SYNO_PASSWORD "$SYNO_PASSWORD"
|
||||||
|
_savedeployconf SYNO_DEVICE_ID "$SYNO_DEVICE_ID"
|
||||||
|
_savedeployconf SYNO_DEVICE_NAME "$SYNO_DEVICE_NAME"
|
||||||
|
fi
|
||||||
|
|
||||||
_info "Getting certificates in Synology DSM"
|
_info "Getting certificates in Synology DSM..."
|
||||||
response=$(_post "api=SYNO.Core.Certificate.CRT&method=list&version=1&_sid=$sid" "$_base_url/webapi/entry.cgi")
|
response=$(_post "api=SYNO.Core.Certificate.CRT&method=list&version=1&_sid=$sid" "$_base_url/webapi/entry.cgi")
|
||||||
_debug3 response "$response"
|
_debug3 response "$response"
|
||||||
escaped_certificate="$(printf "%s" "$SYNO_Certificate" | sed 's/\([].*^$[]\)/\\\1/g;s/"/\\\\"/g')"
|
escaped_certificate="$(printf "%s" "$SYNO_CERTIFICATE" | sed 's/\([].*^$[]\)/\\\1/g;s/"/\\\\"/g')"
|
||||||
_debug escaped_certificate "$escaped_certificate"
|
_debug escaped_certificate "$escaped_certificate"
|
||||||
id=$(echo "$response" | sed -n "s/.*\"desc\":\"$escaped_certificate\",\"id\":\"\([^\"]*\).*/\1/p")
|
id=$(echo "$response" | sed -n "s/.*\"desc\":\"$escaped_certificate\",\"id\":\"\([^\"]*\).*/\1/p")
|
||||||
_debug2 id "$id"
|
_debug2 id "$id"
|
||||||
|
|
||||||
if [ -z "$id" ] && [ -z "${SYNO_Create:-}" ]; then
|
error_code=$(echo "$response" | grep '"error":' | grep -o '"code":[0-9]*' | grep -o '[0-9]*')
|
||||||
_err "Unable to find certificate: $SYNO_Certificate and \$SYNO_Create is not set"
|
_debug2 error_code "$error_code"
|
||||||
|
if [ -n "$error_code" ]; then
|
||||||
|
if [ "$error_code" -eq 105 ]; then
|
||||||
|
_err "Current user is not administrator and does not have sufficient permission for deploying."
|
||||||
|
else
|
||||||
|
_err "Failed to fetch certificate info: $error_code, please try again or contact Synology to learn more."
|
||||||
|
fi
|
||||||
|
_temp_admin_cleanup "$SYNO_USE_TEMP_ADMIN" "$SYNO_USERNAME"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# we've verified this certificate description is a thing, so save it
|
_migratedeployconf SYNO_Create SYNO_CREATE
|
||||||
_savedeployconf SYNO_Certificate "$SYNO_Certificate" "base64"
|
_getdeployconf SYNO_CREATE
|
||||||
|
_debug2 SYNO_CREATE "$SYNO_CREATE"
|
||||||
|
|
||||||
_info "Generate form POST request"
|
if [ -z "$id" ] && [ -z "$SYNO_CREATE" ]; then
|
||||||
|
_err "Unable to find certificate: $SYNO_CERTIFICATE and $SYNO_CREATE is not set."
|
||||||
|
_temp_admin_cleanup "$SYNO_USE_TEMP_ADMIN" "$SYNO_USERNAME"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# We've verified this certificate description is a thing, so save it
|
||||||
|
_savedeployconf SYNO_CERTIFICATE "$SYNO_CERTIFICATE" "base64"
|
||||||
|
|
||||||
|
_info "Generating form POST request..."
|
||||||
nl="\0015\0012"
|
nl="\0015\0012"
|
||||||
delim="--------------------------$(_utc_date | tr -d -- '-: ')"
|
delim="--------------------------$(_utc_date | tr -d -- '-: ')"
|
||||||
content="--$delim${nl}Content-Disposition: form-data; name=\"key\"; filename=\"$(basename "$_ckey")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_ckey")\0012"
|
content="--$delim${nl}Content-Disposition: form-data; name=\"key\"; filename=\"$(basename "$_ckey")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_ckey")\0012"
|
||||||
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"cert\"; filename=\"$(basename "$_ccert")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_ccert")\0012"
|
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"cert\"; filename=\"$(basename "$_ccert")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_ccert")\0012"
|
||||||
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"inter_cert\"; filename=\"$(basename "$_cca")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_cca")\0012"
|
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"inter_cert\"; filename=\"$(basename "$_cca")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_cca")\0012"
|
||||||
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"id\"${nl}${nl}$id"
|
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"id\"${nl}${nl}$id"
|
||||||
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"desc\"${nl}${nl}${SYNO_Certificate}"
|
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"desc\"${nl}${nl}${SYNO_CERTIFICATE}"
|
||||||
if echo "$response" | sed -n "s/.*\"desc\":\"$escaped_certificate\",\([^{]*\).*/\1/p" | grep -- 'is_default":true' >/dev/null; then
|
if echo "$response" | sed -n "s/.*\"desc\":\"$escaped_certificate\",\([^{]*\).*/\1/p" | grep -- 'is_default":true' >/dev/null; then
|
||||||
_debug2 default "this is the default certificate"
|
_debug2 default "This is the default certificate"
|
||||||
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"as_default\"${nl}${nl}true"
|
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"as_default\"${nl}${nl}true"
|
||||||
else
|
else
|
||||||
_debug2 default "this is NOT the default certificate"
|
_debug2 default "This is NOT the default certificate"
|
||||||
fi
|
fi
|
||||||
content="$content${nl}--$delim--${nl}"
|
content="$content${nl}--$delim--${nl}"
|
||||||
content="$(printf "%b_" "$content")"
|
content="$(printf "%b_" "$content")"
|
||||||
content="${content%_}" # protect trailing \n
|
content="${content%_}" # protect trailing \n
|
||||||
|
|
||||||
_info "Upload certificate to the Synology DSM"
|
_info "Upload certificate to the Synology DSM."
|
||||||
response=$(_post "$content" "$_base_url/webapi/entry.cgi?api=SYNO.Core.Certificate&method=import&version=1&SynoToken=$token&_sid=$sid" "" "POST" "multipart/form-data; boundary=${delim}")
|
response=$(_post "$content" "$_base_url/webapi/entry.cgi?api=SYNO.Core.Certificate&method=import&version=1&SynoToken=$token&_sid=$sid" "" "POST" "multipart/form-data; boundary=${delim}")
|
||||||
_debug3 response "$response"
|
_debug3 response "$response"
|
||||||
|
|
||||||
if ! echo "$response" | grep '"error":' >/dev/null; then
|
if ! echo "$response" | grep '"error":' >/dev/null; then
|
||||||
if echo "$response" | grep '"restart_httpd":true' >/dev/null; then
|
if echo "$response" | grep '"restart_httpd":true' >/dev/null; then
|
||||||
_info "http services were restarted"
|
_info "Restart HTTP services succeeded."
|
||||||
else
|
else
|
||||||
_info "http services were NOT restarted"
|
_info "Restart HTTP services failed."
|
||||||
fi
|
fi
|
||||||
|
_temp_admin_cleanup "$SYNO_USE_TEMP_ADMIN" "$SYNO_USERNAME"
|
||||||
|
_logout
|
||||||
return 0
|
return 0
|
||||||
else
|
else
|
||||||
_err "Unable to update certificate, error code $response"
|
_temp_admin_cleanup "$SYNO_USE_TEMP_ADMIN" "$SYNO_USERNAME"
|
||||||
|
_err "Unable to update certificate, got error response: $response."
|
||||||
|
_logout
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#################### Private functions below ##################################
|
||||||
|
_logout() {
|
||||||
|
# Logout CERT user only to not occupy a permanent session, e.g. in DSM's "Connected Users" widget (based on previous variables)
|
||||||
|
response=$(_get "$_base_url/webapi/$api_path?api=SYNO.API.Auth&version=$api_version&method=logout&_sid=$sid")
|
||||||
|
_debug3 response "$response"
|
||||||
|
}
|
||||||
|
|
||||||
|
_temp_admin_create() {
|
||||||
|
_username="$1"
|
||||||
|
_password="$2"
|
||||||
|
synouser --del "$_username" >/dev/null 2>/dev/null
|
||||||
|
synouser --add "$_username" "$_password" "" 0 "" 0 >/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
_temp_admin_cleanup() {
|
||||||
|
_flag=$1
|
||||||
|
_username=$2
|
||||||
|
|
||||||
|
if [ -n "${_flag}" ]; then
|
||||||
|
_debug "Cleanuping temp admin info..."
|
||||||
|
synouser --del "$_username" >/dev/null
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
#_cleardeployconf key
|
||||||
|
_cleardeployconf() {
|
||||||
|
_cleardomainconf "SAVED_$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
# key
|
||||||
|
_check2cleardeployconfexp() {
|
||||||
|
_key="$1"
|
||||||
|
_clear_key="CLEAR_$_key"
|
||||||
|
# Clear saved settings if explicitly requested
|
||||||
|
if [ -n "$(eval echo \$"$_clear_key")" ]; then
|
||||||
|
_debug2 "$_key: value cleared from config, exported value will be ignored."
|
||||||
|
_cleardeployconf "$_key"
|
||||||
|
eval "$_key"=
|
||||||
|
export "$_key"=
|
||||||
|
eval SAVED_"$_key"=
|
||||||
|
export SAVED_"$_key"=
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
#
|
#
|
||||||
# Following environment variables must be set:
|
# Following environment variables must be set:
|
||||||
#
|
#
|
||||||
# export DEPLOY_TRUENAS_APIKEY="<API_KEY_GENERATED_IN_THE_WEB_UI"
|
# export DEPLOY_TRUENAS_APIKEY="<API_KEY_GENERATED_IN_THE_WEB_UI>"
|
||||||
#
|
#
|
||||||
# The following environmental variables may be set if you don't like their
|
# The following environmental variables may be set if you don't like their
|
||||||
# default values:
|
# default values:
|
||||||
@@ -64,6 +64,20 @@ truenas_deploy() {
|
|||||||
_response=$(_get "$_api_url/system/state")
|
_response=$(_get "$_api_url/system/state")
|
||||||
_info "TrueNAS system state: $_response."
|
_info "TrueNAS system state: $_response."
|
||||||
|
|
||||||
|
_info "Getting TrueNAS version"
|
||||||
|
_response=$(_get "$_api_url/system/version")
|
||||||
|
|
||||||
|
if echo "$_response" | grep -q "SCALE"; then
|
||||||
|
_truenas_os=$(echo "$_response" | cut -d '-' -f 2)
|
||||||
|
_truenas_version=$(echo "$_response" | cut -d '-' -f 3 | tr -d '"' | cut -d '.' -f 1,2)
|
||||||
|
else
|
||||||
|
_truenas_os="unknown"
|
||||||
|
_truenas_version="unknown"
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Detected TrueNAS system os: $_truenas_os"
|
||||||
|
_info "Detected TrueNAS system version: $_truenas_version"
|
||||||
|
|
||||||
if [ -z "$_response" ]; then
|
if [ -z "$_response" ]; then
|
||||||
_err "Unable to authenticate to $_api_url."
|
_err "Unable to authenticate to $_api_url."
|
||||||
_err 'Check your connection settings are correct, e.g.'
|
_err 'Check your connection settings are correct, e.g.'
|
||||||
@@ -115,27 +129,106 @@ truenas_deploy() {
|
|||||||
|
|
||||||
_debug3 _activate_result "$_activate_result"
|
_debug3 _activate_result "$_activate_result"
|
||||||
|
|
||||||
_info "Checking if WebDAV certificate is the same as the TrueNAS web UI"
|
_truenas_version_23_10="23.10"
|
||||||
_webdav_list=$(_get "$_api_url/webdav")
|
_truenas_version_24_10="24.10"
|
||||||
_webdav_cert_id=$(echo "$_webdav_list" | grep '"certssl":' | tr -d -- '"certsl: ,')
|
|
||||||
|
|
||||||
if [ "$_webdav_cert_id" = "$_active_cert_id" ]; then
|
_check_version=$(printf "%s\n%s" "$_truenas_version_23_10" "$_truenas_version" | sort -V | head -n 1)
|
||||||
_info "Updating the WebDAV certificate"
|
if [ "$_truenas_os" != "SCALE" ] || [ "$_check_version" != "$_truenas_version_23_10" ]; then
|
||||||
_debug _webdav_cert_id "$_webdav_cert_id"
|
_info "Checking if WebDAV certificate is the same as the TrueNAS web UI"
|
||||||
_webdav_data="{\"certssl\": \"${_cert_id}\"}"
|
_webdav_list=$(_get "$_api_url/webdav")
|
||||||
_activate_webdav_cert="$(_post "$_webdav_data" "$_api_url/webdav" "" "PUT" "application/json")"
|
_webdav_cert_id=$(echo "$_webdav_list" | grep '"certssl":' | tr -d -- '"certsl: ,')
|
||||||
_webdav_new_cert_id=$(echo "$_activate_webdav_cert" | _json_decode | grep '"certssl":' | sed -n 's/.*: \([0-9]\{1,\}\),\{0,1\}$/\1/p')
|
|
||||||
if [ "$_webdav_new_cert_id" -eq "$_cert_id" ]; then
|
if [ "$_webdav_cert_id" = "$_active_cert_id" ]; then
|
||||||
_info "WebDAV certificate updated successfully"
|
_info "Updating the WebDAV certificate"
|
||||||
else
|
_debug _webdav_cert_id "$_webdav_cert_id"
|
||||||
_err "Unable to set WebDAV certificate"
|
_webdav_data="{\"certssl\": \"${_cert_id}\"}"
|
||||||
_debug3 _activate_webdav_cert "$_activate_webdav_cert"
|
_activate_webdav_cert="$(_post "$_webdav_data" "$_api_url/webdav" "" "PUT" "application/json")"
|
||||||
|
_webdav_new_cert_id=$(echo "$_activate_webdav_cert" | _json_decode | grep '"certssl":' | sed -n 's/.*: \([0-9]\{1,\}\),\{0,1\}$/\1/p')
|
||||||
|
if [ "$_webdav_new_cert_id" -eq "$_cert_id" ]; then
|
||||||
|
_info "WebDAV certificate updated successfully"
|
||||||
|
else
|
||||||
|
_err "Unable to set WebDAV certificate"
|
||||||
|
_debug3 _activate_webdav_cert "$_activate_webdav_cert"
|
||||||
|
_debug3 _webdav_new_cert_id "$_webdav_new_cert_id"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
_debug3 _webdav_new_cert_id "$_webdav_new_cert_id"
|
_debug3 _webdav_new_cert_id "$_webdav_new_cert_id"
|
||||||
return 1
|
else
|
||||||
|
_info "WebDAV certificate is not configured or is not the same as TrueNAS web UI"
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Checking if S3 certificate is the same as the TrueNAS web UI"
|
||||||
|
_s3_list=$(_get "$_api_url/s3")
|
||||||
|
_s3_cert_id=$(echo "$_s3_list" | grep '"certificate":' | tr -d -- '"certifa:_ ,')
|
||||||
|
|
||||||
|
if [ "$_s3_cert_id" = "$_active_cert_id" ]; then
|
||||||
|
_info "Updating the S3 certificate"
|
||||||
|
_debug _s3_cert_id "$_s3_cert_id"
|
||||||
|
_s3_data="{\"certificate\": \"${_cert_id}\"}"
|
||||||
|
_activate_s3_cert="$(_post "$_s3_data" "$_api_url/s3" "" "PUT" "application/json")"
|
||||||
|
_s3_new_cert_id=$(echo "$_activate_s3_cert" | _json_decode | grep '"certificate":' | sed -n 's/.*: \([0-9]\{1,\}\),\{0,1\}$/\1/p')
|
||||||
|
if [ "$_s3_new_cert_id" -eq "$_cert_id" ]; then
|
||||||
|
_info "S3 certificate updated successfully"
|
||||||
|
else
|
||||||
|
_err "Unable to set S3 certificate"
|
||||||
|
_debug3 _activate_s3_cert "$_activate_s3_cert"
|
||||||
|
_debug3 _s3_new_cert_id "$_s3_new_cert_id"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug3 _activate_s3_cert "$_activate_s3_cert"
|
||||||
|
else
|
||||||
|
_info "S3 certificate is not configured or is not the same as TrueNAS web UI"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$_truenas_os" = "SCALE" ]; then
|
||||||
|
_check_version=$(printf "%s\n%s" "$_truenas_version_24_10" "$_truenas_version" | sort -V | head -n 1)
|
||||||
|
if [ "$_check_version" != "$_truenas_version_24_10" ]; then
|
||||||
|
_info "Checking if any chart release Apps is using the same certificate as TrueNAS web UI. Tool 'jq' is required"
|
||||||
|
if _exists jq; then
|
||||||
|
_info "Query all chart release"
|
||||||
|
_release_list=$(_get "$_api_url/chart/release")
|
||||||
|
_related_name_list=$(printf "%s" "$_release_list" | jq -r "[.[] | {name,certId: .config.ingress?.main.tls[]?.scaleCert} | select(.certId==$_active_cert_id) | .name ] | unique")
|
||||||
|
_release_length=$(printf "%s" "$_related_name_list" | jq -r "length")
|
||||||
|
_info "Found $_release_length related chart release in list: $_related_name_list"
|
||||||
|
for i in $(seq 0 $((_release_length - 1))); do
|
||||||
|
_release_name=$(echo "$_related_name_list" | jq -r ".[$i]")
|
||||||
|
_info "Updating certificate from $_active_cert_id to $_cert_id for chart release: $_release_name"
|
||||||
|
#Read the chart release configuration
|
||||||
|
_chart_config=$(printf "%s" "$_release_list" | jq -r ".[] | select(.name==\"$_release_name\")")
|
||||||
|
#Replace the old certificate id with the new one in path .config.ingress.main.tls[].scaleCert. Then update .config.ingress
|
||||||
|
_updated_chart_config=$(printf "%s" "$_chart_config" | jq "(.config.ingress?.main.tls[]? | select(.scaleCert==$_active_cert_id) | .scaleCert ) |= $_cert_id | .config.ingress ")
|
||||||
|
_update_chart_result="$(_post "{\"values\" : { \"ingress\" : $_updated_chart_config } }" "$_api_url/chart/release/id/$_release_name" "" "PUT" "application/json")"
|
||||||
|
_debug3 _update_chart_result "$_update_chart_result"
|
||||||
|
done
|
||||||
|
else
|
||||||
|
_info "Tool 'jq' does not exists, skip chart release checking"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
_info "Checking if any app is using the same certificate as TrueNAS web UI. Tool 'jq' is required"
|
||||||
|
if _exists jq; then
|
||||||
|
_info "Query all apps"
|
||||||
|
_app_list=$(_get "$_api_url/app")
|
||||||
|
_app_id_list=$(printf "%s" "$_app_list" | jq -r '.[].name')
|
||||||
|
_app_length=$(echo "$_app_id_list" | wc -l)
|
||||||
|
_info "Found $_app_length apps"
|
||||||
|
_info "Checking for each app if an update is needed"
|
||||||
|
for i in $(seq 1 "$_app_length"); do
|
||||||
|
_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" | 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
|
||||||
|
_update_app_result="$(_post "{\"values\" : { \"network\": { \"certificate_id\": $_cert_id } } }" "$_api_url/app/id/$_app_id" "" "PUT" "application/json")"
|
||||||
|
_debug3 _update_app_result "$_update_app_result"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
else
|
||||||
|
_info "Tool 'jq' does not exists, skip app checking"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
_debug3 _webdav_new_cert_id "$_webdav_new_cert_id"
|
|
||||||
else
|
|
||||||
_info "WebDAV certificate is not configured or is not the same as TrueNAS web UI"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
_info "Checking if FTP certificate is the same as the TrueNAS web UI"
|
_info "Checking if FTP certificate is the same as the TrueNAS web UI"
|
||||||
@@ -161,50 +254,6 @@ truenas_deploy() {
|
|||||||
_info "FTP certificate is not configured or is not the same as TrueNAS web UI"
|
_info "FTP certificate is not configured or is not the same as TrueNAS web UI"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
_info "Checking if S3 certificate is the same as the TrueNAS web UI"
|
|
||||||
_s3_list=$(_get "$_api_url/s3")
|
|
||||||
_s3_cert_id=$(echo "$_s3_list" | grep '"certificate":' | tr -d -- '"certifa:_ ,')
|
|
||||||
|
|
||||||
if [ "$_s3_cert_id" = "$_active_cert_id" ]; then
|
|
||||||
_info "Updating the S3 certificate"
|
|
||||||
_debug _s3_cert_id "$_s3_cert_id"
|
|
||||||
_s3_data="{\"certificate\": \"${_cert_id}\"}"
|
|
||||||
_activate_s3_cert="$(_post "$_s3_data" "$_api_url/s3" "" "PUT" "application/json")"
|
|
||||||
_s3_new_cert_id=$(echo "$_activate_s3_cert" | _json_decode | grep '"certificate":' | sed -n 's/.*: \([0-9]\{1,\}\),\{0,1\}$/\1/p')
|
|
||||||
if [ "$_s3_new_cert_id" -eq "$_cert_id" ]; then
|
|
||||||
_info "S3 certificate updated successfully"
|
|
||||||
else
|
|
||||||
_err "Unable to set S3 certificate"
|
|
||||||
_debug3 _activate_s3_cert "$_activate_s3_cert"
|
|
||||||
_debug3 _s3_new_cert_id "$_s3_new_cert_id"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
_debug3 _activate_s3_cert "$_activate_s3_cert"
|
|
||||||
else
|
|
||||||
_info "S3 certificate is not configured or is not the same as TrueNAS web UI"
|
|
||||||
fi
|
|
||||||
|
|
||||||
_info "Checking if any chart release Apps is using the same certificate as TrueNAS web UI. Tool 'jq' is required"
|
|
||||||
if _exists jq; then
|
|
||||||
_info "Query all chart release"
|
|
||||||
_release_list=$(_get "$_api_url/chart/release")
|
|
||||||
_related_name_list=$(printf "%s" "$_release_list" | jq -r "[.[] | {name,certId: .config.ingress?.main.tls[]?.scaleCert} | select(.certId==$_active_cert_id) | .name ] | unique")
|
|
||||||
_release_length=$(printf "%s" "$_related_name_list" | jq -r "length")
|
|
||||||
_info "Found $_release_length related chart release in list: $_related_name_list"
|
|
||||||
for i in $(seq 0 $((_release_length - 1))); do
|
|
||||||
_release_name=$(echo "$_related_name_list" | jq -r ".[$i]")
|
|
||||||
_info "Updating certificate from $_active_cert_id to $_cert_id for chart release: $_release_name"
|
|
||||||
#Read the chart release configuration
|
|
||||||
_chart_config=$(printf "%s" "$_release_list" | jq -r ".[] | select(.name==\"$_release_name\")")
|
|
||||||
#Replace the old certificate id with the new one in path .config.ingress.main.tls[].scaleCert. Then update .config.ingress
|
|
||||||
_updated_chart_config=$(printf "%s" "$_chart_config" | jq "(.config.ingress?.main.tls[]? | select(.scaleCert==$_active_cert_id) | .scaleCert ) |= $_cert_id | .config.ingress ")
|
|
||||||
_update_chart_result="$(_post "{\"values\" : { \"ingress\" : $_updated_chart_config } }" "$_api_url/chart/release/id/$_release_name" "" "PUT" "application/json")"
|
|
||||||
_debug3 _update_chart_result "$_update_chart_result"
|
|
||||||
done
|
|
||||||
else
|
|
||||||
_info "Tool 'jq' does not exists, skip chart release checking"
|
|
||||||
fi
|
|
||||||
|
|
||||||
_info "Deleting old certificate"
|
_info "Deleting old certificate"
|
||||||
_delete_result="$(_post "" "$_api_url/certificate/id/$_active_cert_id" "" "DELETE" "application/json")"
|
_delete_result="$(_post "" "$_api_url/certificate/id/$_active_cert_id" "" "DELETE" "application/json")"
|
||||||
|
|
||||||
|
|||||||
343
deploy/truenas_ws.sh
Normal file
343
deploy/truenas_ws.sh
Normal file
@@ -0,0 +1,343 @@
|
|||||||
|
#!/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 --uri "$_ws_uri" -K "$DEPLOY_TRUENAS_APIKEY" call "$1" "$2" "$3")
|
||||||
|
fi
|
||||||
|
if [ $# -eq 2 ]; then
|
||||||
|
_ws_response=$(midclt --uri "$_ws_uri" -K "$DEPLOY_TRUENAS_APIKEY" call "$1" "$2")
|
||||||
|
fi
|
||||||
|
if [ $# -eq 1 ]; then
|
||||||
|
_ws_response=$(midclt --uri "$_ws_uri" -K "$DEPLOY_TRUENAS_APIKEY" call "$1")
|
||||||
|
fi
|
||||||
|
_debug "_ws_response" "$_ws_response"
|
||||||
|
printf "%s" "$_ws_response"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Upload certificate with webclient api
|
||||||
|
_ws_upload_cert() {
|
||||||
|
|
||||||
|
/usr/bin/env python - <<EOF
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from truenas_api_client import Client
|
||||||
|
with Client(uri="$_ws_uri") as c:
|
||||||
|
|
||||||
|
### Login with API key
|
||||||
|
print("I:Trying to upload new certificate...")
|
||||||
|
ret = c.call("auth.login_with_api_key", "${DEPLOY_TRUENAS_APIKEY}")
|
||||||
|
if ret:
|
||||||
|
### upload certificate
|
||||||
|
with open('$1', 'r') as file:
|
||||||
|
fullchain = file.read()
|
||||||
|
with open('$2', 'r') as file:
|
||||||
|
privatekey = file.read()
|
||||||
|
ret = c.call("certificate.create", {"name": "$3", "create_type": "CERTIFICATE_CREATE_IMPORTED", "certificate": fullchain, "privatekey": privatekey}, 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
|
||||||
|
_getdeployconf DEPLOY_TRUENAS_HOSTNAME
|
||||||
|
_getdeployconf DEPLOY_TRUENAS_PROTOCOL
|
||||||
|
# Check API Key
|
||||||
|
if [ -z "$DEPLOY_TRUENAS_APIKEY" ]; then
|
||||||
|
_err "TrueNAS API key not found, please set the DEPLOY_TRUENAS_APIKEY environment variable."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
# Check Hostname, default to localhost if not set
|
||||||
|
if [ -z "$DEPLOY_TRUENAS_HOSTNAME" ]; then
|
||||||
|
_info "TrueNAS hostname not set. Using 'localhost'."
|
||||||
|
DEPLOY_TRUENAS_HOSTNAME="localhost"
|
||||||
|
fi
|
||||||
|
# Check protocol, default to ws if not set
|
||||||
|
if [ -z "$DEPLOY_TRUENAS_PROTOCOL" ]; then
|
||||||
|
_info "TrueNAS protocol not set. Using 'ws'."
|
||||||
|
DEPLOY_TRUENAS_PROTOCOL="ws"
|
||||||
|
fi
|
||||||
|
_ws_uri="$DEPLOY_TRUENAS_PROTOCOL://$DEPLOY_TRUENAS_HOSTNAME/websocket"
|
||||||
|
_debug2 DEPLOY_TRUENAS_HOSTNAME "$DEPLOY_TRUENAS_HOSTNAME"
|
||||||
|
_debug2 DEPLOY_TRUENAS_PROTOCOL "$DEPLOY_TRUENAS_PROTOCOL"
|
||||||
|
_debug _ws_uri "$_ws_uri"
|
||||||
|
_secure_debug2 DEPLOY_TRUENAS_APIKEY "$DEPLOY_TRUENAS_APIKEY"
|
||||||
|
_info "Environment variables: OK"
|
||||||
|
|
||||||
|
########## 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"
|
||||||
|
_savedeployconf DEPLOY_TRUENAS_HOSTNAME "$DEPLOY_TRUENAS_HOSTNAME"
|
||||||
|
_savedeployconf DEPLOY_TRUENAS_PROTOCOL "$DEPLOY_TRUENAS_PROTOCOL"
|
||||||
|
_info "TrueNAS health: OK"
|
||||||
|
|
||||||
|
########## System info
|
||||||
|
|
||||||
|
_info "Gather system info..."
|
||||||
|
_ws_response=$(_ws_call "system.info")
|
||||||
|
_truenas_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 15
|
||||||
|
|
||||||
|
########## 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!"
|
||||||
|
|
||||||
|
}
|
||||||
147
deploy/unifi.sh
147
deploy/unifi.sh
@@ -5,6 +5,15 @@
|
|||||||
# - self-hosted Unifi Controller
|
# - self-hosted Unifi Controller
|
||||||
# - Unifi Cloud Key (Gen1/2/2+)
|
# - Unifi Cloud Key (Gen1/2/2+)
|
||||||
# - Unifi Cloud Key running UnifiOS (v2.0.0+, Gen2/2+ only)
|
# - Unifi Cloud Key running UnifiOS (v2.0.0+, Gen2/2+ only)
|
||||||
|
# - Unifi Dream Machine
|
||||||
|
# This has not been tested on other "all-in-one" devices such as
|
||||||
|
# UDM Pro or Unifi Express.
|
||||||
|
#
|
||||||
|
# OS Version v2.0.0+
|
||||||
|
# Network Application version 7.0.0+
|
||||||
|
# OS version ~3.1 removed java and keytool from the UnifiOS.
|
||||||
|
# Using PKCS12 format keystore appears to work fine.
|
||||||
|
#
|
||||||
# Please report bugs to https://github.com/acmesh-official/acme.sh/issues/3359
|
# Please report bugs to https://github.com/acmesh-official/acme.sh/issues/3359
|
||||||
|
|
||||||
#returns 0 means success, otherwise error.
|
#returns 0 means success, otherwise error.
|
||||||
@@ -21,7 +30,9 @@
|
|||||||
# Keystore password (built into Unifi Controller, not a user-set password):
|
# Keystore password (built into Unifi Controller, not a user-set password):
|
||||||
#DEPLOY_UNIFI_KEYPASS="aircontrolenterprise"
|
#DEPLOY_UNIFI_KEYPASS="aircontrolenterprise"
|
||||||
# Command to restart Unifi Controller:
|
# 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):
|
# Settings for Unifi Cloud Key Gen1 (nginx admin pages):
|
||||||
# Directory where cloudkey.crt and cloudkey.key live:
|
# Directory where cloudkey.crt and cloudkey.key live:
|
||||||
@@ -34,7 +45,7 @@
|
|||||||
# Directory where unifi-core.crt and unifi-core.key live:
|
# Directory where unifi-core.crt and unifi-core.key live:
|
||||||
#DEPLOY_UNIFI_CORE_CONFIG="/data/unifi-core/config/"
|
#DEPLOY_UNIFI_CORE_CONFIG="/data/unifi-core/config/"
|
||||||
# Command to restart unifi-core:
|
# 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,
|
# At least one of DEPLOY_UNIFI_KEYSTORE, DEPLOY_UNIFI_CLOUDKEY_CERTDIR,
|
||||||
# or DEPLOY_UNIFI_CORE_CONFIG must exist to receive the deployed certs.
|
# or DEPLOY_UNIFI_CORE_CONFIG must exist to receive the deployed certs.
|
||||||
@@ -60,12 +71,16 @@ unifi_deploy() {
|
|||||||
_getdeployconf DEPLOY_UNIFI_CLOUDKEY_CERTDIR
|
_getdeployconf DEPLOY_UNIFI_CLOUDKEY_CERTDIR
|
||||||
_getdeployconf DEPLOY_UNIFI_CORE_CONFIG
|
_getdeployconf DEPLOY_UNIFI_CORE_CONFIG
|
||||||
_getdeployconf DEPLOY_UNIFI_RELOAD
|
_getdeployconf DEPLOY_UNIFI_RELOAD
|
||||||
|
_getdeployconf DEPLOY_UNIFI_SYSTEM_PROPERTIES
|
||||||
|
_getdeployconf DEPLOY_UNIFI_OS_RELOAD
|
||||||
|
|
||||||
_debug2 DEPLOY_UNIFI_KEYSTORE "$DEPLOY_UNIFI_KEYSTORE"
|
_debug2 DEPLOY_UNIFI_KEYSTORE "$DEPLOY_UNIFI_KEYSTORE"
|
||||||
_debug2 DEPLOY_UNIFI_KEYPASS "$DEPLOY_UNIFI_KEYPASS"
|
_debug2 DEPLOY_UNIFI_KEYPASS "$DEPLOY_UNIFI_KEYPASS"
|
||||||
_debug2 DEPLOY_UNIFI_CLOUDKEY_CERTDIR "$DEPLOY_UNIFI_CLOUDKEY_CERTDIR"
|
_debug2 DEPLOY_UNIFI_CLOUDKEY_CERTDIR "$DEPLOY_UNIFI_CLOUDKEY_CERTDIR"
|
||||||
_debug2 DEPLOY_UNIFI_CORE_CONFIG "$DEPLOY_UNIFI_CORE_CONFIG"
|
_debug2 DEPLOY_UNIFI_CORE_CONFIG "$DEPLOY_UNIFI_CORE_CONFIG"
|
||||||
_debug2 DEPLOY_UNIFI_RELOAD "$DEPLOY_UNIFI_RELOAD"
|
_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:
|
# Space-separated list of environments detected and installed:
|
||||||
_services_updated=""
|
_services_updated=""
|
||||||
@@ -74,14 +89,16 @@ unifi_deploy() {
|
|||||||
_reload_cmd=""
|
_reload_cmd=""
|
||||||
|
|
||||||
# Unifi Controller environment (self hosted or any Cloud Key) --
|
# Unifi Controller environment (self hosted or any Cloud Key) --
|
||||||
# auto-detect by file /usr/lib/unifi/data/keystore:
|
# auto-detect by file /usr/lib/unifi/data/keystore
|
||||||
_unifi_keystore="${DEPLOY_UNIFI_KEYSTORE:-/usr/lib/unifi/data/keystore}"
|
_unifi_keystore="${DEPLOY_UNIFI_KEYSTORE:-/usr/lib/unifi/data/keystore}"
|
||||||
if [ -f "$_unifi_keystore" ]; then
|
if [ -f "$_unifi_keystore" ]; then
|
||||||
_info "Installing certificate for Unifi Controller (Java keystore)"
|
|
||||||
_debug _unifi_keystore "$_unifi_keystore"
|
_debug _unifi_keystore "$_unifi_keystore"
|
||||||
if ! _exists keytool; then
|
if ! _exists keytool; then
|
||||||
_err "keytool not found"
|
_do_keytool=0
|
||||||
return 1
|
_info "Installing certificate for Unifi Controller (PKCS12 keystore)."
|
||||||
|
else
|
||||||
|
_do_keytool=1
|
||||||
|
_info "Installing certificate for Unifi Controller (Java keystore)"
|
||||||
fi
|
fi
|
||||||
if [ ! -w "$_unifi_keystore" ]; then
|
if [ ! -w "$_unifi_keystore" ]; then
|
||||||
_err "The file $_unifi_keystore is not writable, please change the permission."
|
_err "The file $_unifi_keystore is not writable, please change the permission."
|
||||||
@@ -92,6 +109,7 @@ unifi_deploy() {
|
|||||||
|
|
||||||
_debug "Generate import pkcs12"
|
_debug "Generate import pkcs12"
|
||||||
_import_pkcs12="$(_mktemp)"
|
_import_pkcs12="$(_mktemp)"
|
||||||
|
_debug "_toPkcs $_import_pkcs12 $_ckey $_ccert $_cca $_unifi_keypass unifi root"
|
||||||
_toPkcs "$_import_pkcs12" "$_ckey" "$_ccert" "$_cca" "$_unifi_keypass" unifi root
|
_toPkcs "$_import_pkcs12" "$_ckey" "$_ccert" "$_cca" "$_unifi_keypass" unifi root
|
||||||
# shellcheck disable=SC2181
|
# shellcheck disable=SC2181
|
||||||
if [ "$?" != "0" ]; then
|
if [ "$?" != "0" ]; then
|
||||||
@@ -99,22 +117,79 @@ unifi_deploy() {
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
_debug "Import into keystore: $_unifi_keystore"
|
# Save the existing keystore in case something goes wrong.
|
||||||
if keytool -importkeystore \
|
mv -f "${_unifi_keystore}" "${_unifi_keystore}"_original
|
||||||
-deststorepass "$_unifi_keypass" -destkeypass "$_unifi_keypass" -destkeystore "$_unifi_keystore" \
|
_info "Previous keystore saved to ${_unifi_keystore}_original."
|
||||||
-srckeystore "$_import_pkcs12" -srcstoretype PKCS12 -srcstorepass "$_unifi_keypass" \
|
|
||||||
-alias unifi -noprompt; then
|
if [ "$_do_keytool" -eq 1 ]; then
|
||||||
_debug "Import keystore success!"
|
_debug "Import into keystore: $_unifi_keystore"
|
||||||
rm "$_import_pkcs12"
|
if keytool -importkeystore \
|
||||||
|
-deststorepass "$_unifi_keypass" -destkeypass "$_unifi_keypass" -destkeystore "$_unifi_keystore" \
|
||||||
|
-srckeystore "$_import_pkcs12" -srcstoretype PKCS12 -srcstorepass "$_unifi_keypass" \
|
||||||
|
-alias unifi -noprompt; then
|
||||||
|
_debug "Import keystore success!"
|
||||||
|
else
|
||||||
|
_err "Error importing into Unifi Java keystore."
|
||||||
|
_err "Please re-run with --debug and report a bug."
|
||||||
|
_info "Restoring original keystore."
|
||||||
|
mv -f "${_unifi_keystore}"_original "${_unifi_keystore}"
|
||||||
|
rm "$_import_pkcs12"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
_err "Error importing into Unifi Java keystore."
|
_debug "Copying new keystore to $_unifi_keystore"
|
||||||
_err "Please re-run with --debug and report a bug."
|
cp -f "$_import_pkcs12" "$_unifi_keystore"
|
||||||
rm "$_import_pkcs12"
|
|
||||||
return 1
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if systemctl -q is-active unifi; then
|
# correct file ownership according to the directory, the keystore is placed in
|
||||||
_reload_cmd="${_reload_cmd:+$_reload_cmd && }service unifi restart"
|
_unifi_keystore_dir=$(dirname "${_unifi_keystore}")
|
||||||
|
# shellcheck disable=SC2012
|
||||||
|
_unifi_keystore_dir_owner=$(ls -ld "${_unifi_keystore_dir}" | awk '{print $3}')
|
||||||
|
# shellcheck disable=SC2012
|
||||||
|
_unifi_keystore_owner=$(ls -l "${_unifi_keystore}" | awk '{print $3}')
|
||||||
|
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
|
||||||
|
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 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
|
fi
|
||||||
_services_updated="${_services_updated} unifi"
|
_services_updated="${_services_updated} unifi"
|
||||||
_info "Install Unifi Controller certificate success!"
|
_info "Install Unifi Controller certificate success!"
|
||||||
@@ -134,13 +209,24 @@ unifi_deploy() {
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
# Cloud Key expects to load the keystore from /etc/ssl/private/unifi.keystore.jks.
|
# 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
|
# It appears that unifi won't start if this is a symlink, so we'll copy it instead.
|
||||||
# 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
|
# 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'"
|
# _err "Unsupported Cloud Key configuration: keystore not found at '${_cloudkey_certdir}/unifi.keystore.jks'"
|
||||||
return 1
|
# 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
|
fi
|
||||||
|
|
||||||
|
cp "${_unifi_keystore}" "${_cloudkey_certdir}/unifi.keystore.jks"
|
||||||
|
|
||||||
cat "$_cfullchain" >"${_cloudkey_certdir}/cloudkey.crt"
|
cat "$_cfullchain" >"${_cloudkey_certdir}/cloudkey.crt"
|
||||||
cat "$_ckey" >"${_cloudkey_certdir}/cloudkey.key"
|
cat "$_ckey" >"${_cloudkey_certdir}/cloudkey.key"
|
||||||
(cd "$_cloudkey_certdir" && tar -cf cert.tar cloudkey.crt cloudkey.key unifi.keystore.jks)
|
(cd "$_cloudkey_certdir" && tar -cf cert.tar cloudkey.crt cloudkey.key unifi.keystore.jks)
|
||||||
@@ -165,12 +251,17 @@ unifi_deploy() {
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# 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."
|
||||||
|
|
||||||
cat "$_cfullchain" >"${_unifi_core_config}/unifi-core.crt"
|
cat "$_cfullchain" >"${_unifi_core_config}/unifi-core.crt"
|
||||||
cat "$_ckey" >"${_unifi_core_config}/unifi-core.key"
|
cat "$_ckey" >"${_unifi_core_config}/unifi-core.key"
|
||||||
|
|
||||||
if systemctl -q is-active unifi-core; then
|
_unifi_os_reload="${DEPLOY_UNIFI_OS_RELOAD:-systemctl restart unifi-core}"
|
||||||
_reload_cmd="${_reload_cmd:+$_reload_cmd && }systemctl restart unifi-core"
|
_reload_cmd="${_reload_cmd:+$_reload_cmd && }$_unifi_os_reload"
|
||||||
fi
|
|
||||||
_info "Install UnifiOS certificate success!"
|
_info "Install UnifiOS certificate success!"
|
||||||
_services_updated="${_services_updated} unifi-core"
|
_services_updated="${_services_updated} unifi-core"
|
||||||
elif [ "$DEPLOY_UNIFI_CORE_CONFIG" ]; then
|
elif [ "$DEPLOY_UNIFI_CORE_CONFIG" ]; then
|
||||||
@@ -209,6 +300,8 @@ unifi_deploy() {
|
|||||||
_savedeployconf DEPLOY_UNIFI_CLOUDKEY_CERTDIR "$DEPLOY_UNIFI_CLOUDKEY_CERTDIR"
|
_savedeployconf DEPLOY_UNIFI_CLOUDKEY_CERTDIR "$DEPLOY_UNIFI_CLOUDKEY_CERTDIR"
|
||||||
_savedeployconf DEPLOY_UNIFI_CORE_CONFIG "$DEPLOY_UNIFI_CORE_CONFIG"
|
_savedeployconf DEPLOY_UNIFI_CORE_CONFIG "$DEPLOY_UNIFI_CORE_CONFIG"
|
||||||
_savedeployconf DEPLOY_UNIFI_RELOAD "$DEPLOY_UNIFI_RELOAD"
|
_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
|
return 0
|
||||||
}
|
}
|
||||||
|
|||||||
106
deploy/vault.sh
106
deploy/vault.sh
@@ -70,20 +70,25 @@ vault_deploy() {
|
|||||||
|
|
||||||
# JSON does not allow multiline strings.
|
# JSON does not allow multiline strings.
|
||||||
# So replacing new-lines with "\n" here
|
# So replacing new-lines with "\n" here
|
||||||
_ckey=$(sed -z 's/\n/\\n/g' <"$2")
|
_ckey=$(sed -e ':a' -e N -e '$ ! ba' -e 's/\n/\\n/g' <"$2")
|
||||||
_ccert=$(sed -z 's/\n/\\n/g' <"$3")
|
_ccert=$(sed -e ':a' -e N -e '$ ! ba' -e 's/\n/\\n/g' <"$3")
|
||||||
_cca=$(sed -z 's/\n/\\n/g' <"$4")
|
_cca=$(sed -e ':a' -e N -e '$ ! ba' -e 's/\n/\\n/g' <"$4")
|
||||||
_cfullchain=$(sed -z 's/\n/\\n/g' <"$5")
|
_cfullchain=$(sed -e ':a' -e N -e '$ ! ba' -e 's/\n/\\n/g' <"$5")
|
||||||
|
|
||||||
export _H1="X-Vault-Token: $VAULT_TOKEN"
|
export _H1="X-Vault-Token: $VAULT_TOKEN"
|
||||||
|
|
||||||
if [ -n "$VAULT_RENEW_TOKEN" ]; then
|
if [ -n "$VAULT_RENEW_TOKEN" ]; then
|
||||||
URL="$VAULT_ADDR/v1/auth/token/renew-self"
|
URL="$VAULT_ADDR/v1/auth/token/renew-self"
|
||||||
_info "Renew the Vault token to default TTL"
|
_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"
|
_err "Failed to renew the Vault token"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
if echo "$_response" | grep -q '"errors":\['; then
|
||||||
|
_err "Failed to renew the Vault token: $_response"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
URL="$VAULT_ADDR/v1/$VAULT_PREFIX/$_cdomain"
|
URL="$VAULT_ADDR/v1/$VAULT_PREFIX/$_cdomain"
|
||||||
@@ -91,29 +96,85 @@ vault_deploy() {
|
|||||||
if [ -n "$VAULT_FABIO_MODE" ]; then
|
if [ -n "$VAULT_FABIO_MODE" ]; then
|
||||||
_info "Writing certificate and key to $URL in Fabio mode"
|
_info "Writing certificate and key to $URL in Fabio mode"
|
||||||
if [ -n "$VAULT_KV_V2" ]; then
|
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
|
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
|
fi
|
||||||
else
|
else
|
||||||
if [ -n "$VAULT_KV_V2" ]; then
|
if [ -n "$VAULT_KV_V2" ]; then
|
||||||
_info "Writing certificate to $URL/cert.pem"
|
_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"
|
_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"
|
_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"
|
_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
|
else
|
||||||
_info "Writing certificate to $URL/cert.pem"
|
_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"
|
_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"
|
_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"
|
_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
|
fi
|
||||||
|
|
||||||
# To make it compatible with the wrong ca path `chain.pem` which was used in former versions
|
# 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"
|
_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"
|
_info "Updating CA certificate to $URL/chain.pem for backward compatibility"
|
||||||
if [ -n "$VAULT_KV_V2" ]; then
|
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
|
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
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -106,5 +106,5 @@ vsftpd_deploy() {
|
|||||||
fi
|
fi
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
return 0
|
|
||||||
}
|
}
|
||||||
|
|||||||
500
deploy/zyxel_gs1900.sh
Normal file
500
deploy/zyxel_gs1900.sh
Normal file
@@ -0,0 +1,500 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Deploy certificates to Zyxel GS1900 series switches
|
||||||
|
#
|
||||||
|
# This script uses the https web administration interface in order
|
||||||
|
# to upload updated certificates to Zyxel GS1900 series switches.
|
||||||
|
# Only a few models have been tested but untested switches from the
|
||||||
|
# same model line may work as well. If you test and confirm a switch
|
||||||
|
# as working please submit a pull request updating this compatibility
|
||||||
|
# list!
|
||||||
|
#
|
||||||
|
# Known Issues:
|
||||||
|
# 1. This is a consumer grade switch and is a bit underpowered
|
||||||
|
# the longer the RSA key size the slower your switch web UI
|
||||||
|
# will be. RSA 2048 will work, RSA 4096 will work but you may
|
||||||
|
# experience performance problems.
|
||||||
|
# 2. You must use RSA certificates. The switch will reject EC-256
|
||||||
|
# and EC-384 certificates in firmware 2.80
|
||||||
|
# See: https://community.zyxel.com/en/discussion/21506/bug-cannot-import-ssl-cert-on-gs1900-8-and-gs1900-24e-firmware-v2-80/
|
||||||
|
#
|
||||||
|
# Current GS1900 Switch Compatibility:
|
||||||
|
# GS1900-8 - Working as of firmware V2.80
|
||||||
|
# GS1900-8HP - Untested
|
||||||
|
# GS1900-10HP - Untested
|
||||||
|
# GS1900-16 - Untested
|
||||||
|
# GS1900-24 - Untested
|
||||||
|
# GS1900-24E - Working as of firmware V2.80
|
||||||
|
# GS1900-24EP - Untested
|
||||||
|
# GS1900-24HP - Untested
|
||||||
|
# GS1900-48 - Untested
|
||||||
|
# GS1900-48HP - Untested
|
||||||
|
#
|
||||||
|
# Prerequisite Setup Steps:
|
||||||
|
# 1. Install at least firmware V2.80 on your switch
|
||||||
|
# 2. Enable HTTPS web management on your switch
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# 1. Ensure the switch has firmware V2.80 or later.
|
||||||
|
# 2. Ensure the switch has HTTPS management enabled.
|
||||||
|
# 3. Set the appropriate environment variables for your environment.
|
||||||
|
#
|
||||||
|
# DEPLOY_ZYXEL_SWITCH - The switch hostname. (Default: _cdomain)
|
||||||
|
# DEPLOY_ZYXEL_SWITCH_USER - The webadmin user. (Default: admin)
|
||||||
|
# DEPLOY_ZYXEL_SWITCH_PASSWORD - The webadmin password for the switch.
|
||||||
|
# DEPLOY_ZYXEL_SWITCH_REBOOT - If "1" reboot after update. (Default: "0")
|
||||||
|
#
|
||||||
|
# 4. Run the deployment plugin:
|
||||||
|
# acme.sh --deploy --deploy-hook zyxel_gs1900 -d example.com
|
||||||
|
#
|
||||||
|
# returns 0 means success, otherwise error.
|
||||||
|
|
||||||
|
#domain keyfile certfile cafile fullchain
|
||||||
|
zyxel_gs1900_deploy() {
|
||||||
|
_zyxel_gs1900_minimum_firmware_version="v2.80"
|
||||||
|
|
||||||
|
_cdomain="$1"
|
||||||
|
_ckey="$2"
|
||||||
|
_ccert="$3"
|
||||||
|
_cca="$4"
|
||||||
|
_cfullchain="$5"
|
||||||
|
|
||||||
|
_debug _cdomain "$_cdomain"
|
||||||
|
_debug2 _ckey "$_ckey"
|
||||||
|
_debug _ccert "$_ccert"
|
||||||
|
_debug _cca "$_cca"
|
||||||
|
_debug _cfullchain "$_cfullchain"
|
||||||
|
|
||||||
|
_getdeployconf DEPLOY_ZYXEL_SWITCH
|
||||||
|
_getdeployconf DEPLOY_ZYXEL_SWITCH_USER
|
||||||
|
_getdeployconf DEPLOY_ZYXEL_SWITCH_PASSWORD
|
||||||
|
_getdeployconf DEPLOY_ZYXEL_SWITCH_REBOOT
|
||||||
|
|
||||||
|
if [ -z "$DEPLOY_ZYXEL_SWITCH" ]; then
|
||||||
|
DEPLOY_ZYXEL_SWITCH="$_cdomain"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$DEPLOY_ZYXEL_SWITCH_USER" ]; then
|
||||||
|
DEPLOY_ZYXEL_SWITCH_USER="admin"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$DEPLOY_ZYXEL_SWITCH_PASSWORD" ]; then
|
||||||
|
DEPLOY_ZYXEL_SWITCH_PASSWORD="1234"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$DEPLOY_ZYXEL_SWITCH_REBOOT" ]; then
|
||||||
|
DEPLOY_ZYXEL_SWITCH_REBOOT="0"
|
||||||
|
fi
|
||||||
|
|
||||||
|
_savedeployconf DEPLOY_ZYXEL_SWITCH "$DEPLOY_ZYXEL_SWITCH"
|
||||||
|
_savedeployconf DEPLOY_ZYXEL_SWITCH_USER "$DEPLOY_ZYXEL_SWITCH_USER"
|
||||||
|
_savedeployconf DEPLOY_ZYXEL_SWITCH_PASSWORD "$DEPLOY_ZYXEL_SWITCH_PASSWORD"
|
||||||
|
_savedeployconf DEPLOY_ZYXEL_SWITCH_REBOOT "$DEPLOY_ZYXEL_SWITCH_REBOOT"
|
||||||
|
|
||||||
|
_debug DEPLOY_ZYXEL_SWITCH "$DEPLOY_ZYXEL_SWITCH"
|
||||||
|
_debug DEPLOY_ZYXEL_SWITCH_USER "$DEPLOY_ZYXEL_SWITCH_USER"
|
||||||
|
_secure_debug DEPLOY_ZYXEL_SWITCH_PASSWORD "$DEPLOY_ZYXEL_SWITCH_PASSWORD"
|
||||||
|
_debug DEPLOY_ZYXEL_SWITCH_REBOOT "$DEPLOY_ZYXEL_SWITCH_REBOOT"
|
||||||
|
|
||||||
|
_zyxel_switch_base_uri="https://${DEPLOY_ZYXEL_SWITCH}"
|
||||||
|
|
||||||
|
_info "Beginning to deploy to a Zyxel GS1900 series switch at ${_zyxel_switch_base_uri}."
|
||||||
|
_zyxel_gs1900_deployment_precheck || return $?
|
||||||
|
|
||||||
|
_zyxel_gs1900_should_update
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
_info "The switch already has our certificate installed. No update required."
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
_info "The switch does not yet have our certificate installed."
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Logging into the switch web interface."
|
||||||
|
_zyxel_gs1900_login || return $?
|
||||||
|
|
||||||
|
_info "Validating the switch is compatible with this deployment process."
|
||||||
|
_zyxel_gs1900_validate_device_compatibility || return $?
|
||||||
|
|
||||||
|
_info "Uploading the certificate."
|
||||||
|
_zyxel_gs1900_upload_certificate || return $?
|
||||||
|
|
||||||
|
if [ "$DEPLOY_ZYXEL_SWITCH_REBOOT" = "1" ]; then
|
||||||
|
_info "Rebooting the switch."
|
||||||
|
_zyxel_gs1900_trigger_reboot || return $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
_zyxel_gs1900_deployment_precheck() {
|
||||||
|
# Initialize the keylength if it isn't already
|
||||||
|
if [ -z "$Le_Keylength" ]; then
|
||||||
|
Le_Keylength=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
if _isEccKey "$Le_Keylength"; then
|
||||||
|
_info "Warning: Zyxel GS1900 switches are not currently known to work with ECC keys!"
|
||||||
|
_info "You can continue, but your switch may reject your key."
|
||||||
|
elif [ -n "$Le_Keylength" ] && [ "$Le_Keylength" -gt "2048" ]; then
|
||||||
|
_info "Warning: Your RSA key length is greater than 2048!"
|
||||||
|
_info "You can continue, but you may experience performance issues in the web administration interface."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check the server for some common failure modes prior to authentication and certificate upload in order to avoid
|
||||||
|
# sending a certificate when we may not want to.
|
||||||
|
test_login_response=$(_post "username=test&password=test&login=true;" "${_zyxel_switch_base_uri}/cgi-bin/dispatcher.cgi?cmd=0.html" '' "POST" "application/x-www-form-urlencoded" 2>&1)
|
||||||
|
test_login_page_exitcode="$?"
|
||||||
|
_debug3 "Test Login Response: ${test_login_response}"
|
||||||
|
if [ "$test_login_page_exitcode" -ne "0" ]; then
|
||||||
|
if { [ "${ACME_USE_WGET:-0}" = "0" ] && [ "$test_login_page_exitcode" = "60" ]; } || { [ "${ACME_USE_WGET:-0}" = "1" ] && [ "$test_login_page_exitcode" = "5" ]; }; then
|
||||||
|
_err "The SSL certificate at $_zyxel_switch_base_uri could not be validated."
|
||||||
|
_err "Please double check your hostname, port, and that you are actually connecting to your switch."
|
||||||
|
_err "If the problem persists then please ensure that the certificate is not self-signed, has not"
|
||||||
|
_err "expired, and matches the switch hostname. If you expect validation to fail then you can disable"
|
||||||
|
_err "certificate validation by running with --insecure."
|
||||||
|
return 1
|
||||||
|
elif [ "${ACME_USE_WGET:-0}" = "0" ] && [ "$test_login_page_exitcode" = "56" ]; then
|
||||||
|
_debug3 "Intentionally ignore curl exit code 56 in our precheck"
|
||||||
|
else
|
||||||
|
_err "Failed to submit the initial login attempt to $_zyxel_switch_base_uri."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_zyxel_gs1900_login() {
|
||||||
|
# Login to the switch and set the appropriate auth cookie in _H1
|
||||||
|
username_encoded=$(printf "%s" "$DEPLOY_ZYXEL_SWITCH_USER" | _url_encode)
|
||||||
|
password_encoded=$(_zyxel_gs1900_password_obfuscate "$DEPLOY_ZYXEL_SWITCH_PASSWORD" | _url_encode)
|
||||||
|
|
||||||
|
login_response=$(_post "username=${username_encoded}&password=${password_encoded}&login=true;" "${_zyxel_switch_base_uri}/cgi-bin/dispatcher.cgi?cmd=0.html" '' "POST" "application/x-www-form-urlencoded" | tr -d '\n')
|
||||||
|
auth_response=$(_post "authId=${login_response}&login_chk=true" "${_zyxel_switch_base_uri}/cgi-bin/dispatcher.cgi?cmd=0.html" '' "POST" "application/x-www-form-urlencoded" | tr -d '\n')
|
||||||
|
if [ "$auth_response" != "OK" ]; then
|
||||||
|
_err "Login failed due to invalid credentials."
|
||||||
|
_err "Please double check the configured username and password and try again."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
sessionid=$(grep -i '^set-cookie:' "$HTTP_HEADER" | _egrep_o 'HTTPS_XSSID=[^;]*;' | tr -d ';')
|
||||||
|
_secure_debug2 "sessionid" "$sessionid"
|
||||||
|
|
||||||
|
export _H1="Cookie: $sessionid"
|
||||||
|
_secure_debug2 "_H1" "$_H1"
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
_zyxel_gs1900_validate_device_compatibility() {
|
||||||
|
# Check the switches model and firmware version and throw errors
|
||||||
|
# if this script isn't compatible.
|
||||||
|
device_info_html=$(_get "${_zyxel_switch_base_uri}/cgi-bin/dispatcher.cgi?cmd=12" | tr -d '\n')
|
||||||
|
|
||||||
|
model_name=$(_zyxel_gs1900_get_model "$device_info_html")
|
||||||
|
_debug2 "model_name" "$model_name"
|
||||||
|
if [ -z "$model_name" ]; then
|
||||||
|
_err "Could not find the switch model name."
|
||||||
|
_err "Please re-run with --debug and report a bug."
|
||||||
|
return $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! expr "$model_name" : "GS1900-" >/dev/null; then
|
||||||
|
_err "Switch is an unsupported model: $model_name"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
firmware_version=$(_zyxel_gs1900_get_firmware_version "$device_info_html")
|
||||||
|
_debug2 "firmware_version" "$firmware_version"
|
||||||
|
if [ -z "$firmware_version" ]; then
|
||||||
|
_err "Could not find the switch firmware version."
|
||||||
|
_err "Please re-run with --debug and report a bug."
|
||||||
|
return $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug2 "_zyxel_gs1900_minimum_firmware_version" "$_zyxel_gs1900_minimum_firmware_version"
|
||||||
|
minimum_major_version=$(_zyxel_gs1900_parse_major_version "$_zyxel_gs1900_minimum_firmware_version")
|
||||||
|
_debug2 "minimum_major_version" "$minimum_major_version"
|
||||||
|
minimum_minor_version=$(_zyxel_gs1900_parse_minor_version "$_zyxel_gs1900_minimum_firmware_version")
|
||||||
|
_debug2 "minimum_minor_version" "$minimum_minor_version"
|
||||||
|
|
||||||
|
_debug2 "firmware_version" "$firmware_version"
|
||||||
|
firmware_major_version=$(_zyxel_gs1900_parse_major_version "$firmware_version")
|
||||||
|
_debug2 "firmware_major_version" "$firmware_major_version"
|
||||||
|
firmware_minor_version=$(_zyxel_gs1900_parse_minor_version "$firmware_version")
|
||||||
|
_debug2 "firmware_minor_version" "$firmware_minor_version"
|
||||||
|
|
||||||
|
_ret=0
|
||||||
|
if [ "$firmware_major_version" -lt "$minimum_major_version" ]; then
|
||||||
|
_ret=1
|
||||||
|
elif [ "$firmware_major_version" -eq "$minimum_major_version" ] && [ "$firmware_minor_version" -lt "$minimum_minor_version" ]; then
|
||||||
|
_ret=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$_ret" != "0" ]; then
|
||||||
|
_err "Unsupported firmware version $firmware_version. Please upgrade to at least version $_zyxel_gs1900_minimum_firmware_version."
|
||||||
|
fi
|
||||||
|
|
||||||
|
return $?
|
||||||
|
}
|
||||||
|
|
||||||
|
_zyxel_gs1900_should_update() {
|
||||||
|
# Get the remote certificate serial number
|
||||||
|
_remote_cert=$(${ACME_OPENSSL_BIN:-openssl} s_client -showcerts -connect "${DEPLOY_ZYXEL_SWITCH}:443" 2>/dev/null </dev/null | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p')
|
||||||
|
_debug3 "_remote_cert" "$_remote_cert"
|
||||||
|
|
||||||
|
_remote_cert_serial=$(printf "%s" "${_remote_cert}" | ${ACME_OPENSSL_BIN:-openssl} x509 -noout -serial)
|
||||||
|
_debug2 "_remote_cert_serial" "$_remote_cert_serial"
|
||||||
|
|
||||||
|
# Get our certificate serial number
|
||||||
|
_our_cert_serial=$(${ACME_OPENSSL_BIN:-openssl} x509 -noout -serial <"${_ccert}")
|
||||||
|
_debug2 "_our_cert_serial" "$_our_cert_serial"
|
||||||
|
|
||||||
|
[ "${_remote_cert_serial}" != "${_our_cert_serial}" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
_zyxel_gs1900_upload_certificate() {
|
||||||
|
# Generate a PKCS12 certificate with a temporary password since the web interface
|
||||||
|
# requires a password be present. Then upload that certificate.
|
||||||
|
temp_cert_password=$(head /dev/urandom | tr -dc 'A-Za-z0-9' | head -c 64)
|
||||||
|
_secure_debug2 "temp_cert_password" "$temp_cert_password"
|
||||||
|
|
||||||
|
temp_pkcs12="$(_mktemp)"
|
||||||
|
_debug2 "temp_pkcs12" "$temp_pkcs12"
|
||||||
|
_toPkcs "$temp_pkcs12" "$_ckey" "$_ccert" "$_cca" "$temp_cert_password"
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
_err "Failed to generate a pkcs12 certificate."
|
||||||
|
_err "Please re-run with --debug and report a bug."
|
||||||
|
|
||||||
|
# ensure the temporary certificate file is cleaned up
|
||||||
|
[ -f "${temp_pkcs12}" ] && rm -f "${temp_pkcs12}"
|
||||||
|
|
||||||
|
return $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Load the upload page
|
||||||
|
upload_page_html=$(_get "${_zyxel_switch_base_uri}/cgi-bin/dispatcher.cgi?cmd=5914" | tr -d '\n')
|
||||||
|
|
||||||
|
# Get the first instance of XSSID from the upload page
|
||||||
|
form_xss_value=$(printf "%s" "$upload_page_html" | _egrep_o 'name="XSSID"\s*value="[^"]+"' | sed 's/^.*="\([^"]\{1,\}\)"$/\1/g' | head -n 1)
|
||||||
|
_secure_debug2 "form_xss_value" "$form_xss_value"
|
||||||
|
|
||||||
|
_info "Generating the certificate upload request"
|
||||||
|
upload_post_request="$(_mktemp)"
|
||||||
|
upload_post_boundary="---------------------------$(date +%Y%m%d%H%M%S)"
|
||||||
|
|
||||||
|
{
|
||||||
|
printf -- "--%s\r\n" "${upload_post_boundary}"
|
||||||
|
printf "Content-Disposition: form-data; name=\"XSSID\"\r\n\r\n%s\r\n" "${form_xss_value}"
|
||||||
|
printf -- "--%s\r\n" "${upload_post_boundary}"
|
||||||
|
printf "Content-Disposition: form-data; name=\"http_file\"; filename=\"temp_pkcs12.pfx\"\r\n"
|
||||||
|
printf "Content-Type: application/pkcs12\r\n\r\n"
|
||||||
|
cat "${temp_pkcs12}"
|
||||||
|
printf "\r\n"
|
||||||
|
printf -- "--%s\r\n" "${upload_post_boundary}"
|
||||||
|
printf "Content-Disposition: form-data; name=\"pwd\"\r\n\r\n%s\r\n" "${temp_cert_password}"
|
||||||
|
printf -- "--%s\r\n" "${upload_post_boundary}"
|
||||||
|
printf "Content-Disposition: form-data; name=\"cmd\"\r\n\r\n%s\r\n" "31"
|
||||||
|
printf -- "--%s\r\n" "${upload_post_boundary}"
|
||||||
|
printf "Content-Disposition: form-data; name=\"sysSubmit\"\r\n\r\n%s\r\n" "Import"
|
||||||
|
printf -- "--%s--\r\n" "${upload_post_boundary}"
|
||||||
|
} >"${upload_post_request}"
|
||||||
|
|
||||||
|
_info "Upload certificate to the switch"
|
||||||
|
|
||||||
|
# Unfortunately we cannot rely upon the switch response across switch models
|
||||||
|
# to return a consistent body return - so we cannot inspect the result of this
|
||||||
|
# upload to determine success.
|
||||||
|
upload_response=$(_zyxel_upload_pkcs12 "${upload_post_request}" "${upload_post_boundary}" 2>&1)
|
||||||
|
_debug3 "Upload response: ${upload_response}"
|
||||||
|
rm "${upload_post_request}"
|
||||||
|
|
||||||
|
# Pause for a few seconds to give the switch a chance to process the certificate
|
||||||
|
# For some reason I've found this to be necessary on my GS1900-24E
|
||||||
|
_debug2 "Waiting 4 seconds for the switch to process the newly uploaded certificate."
|
||||||
|
sleep "4"
|
||||||
|
|
||||||
|
# Check to see whether or not our update was successful
|
||||||
|
_ret=0
|
||||||
|
_zyxel_gs1900_should_update
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
_info "The certificate was updated successfully"
|
||||||
|
else
|
||||||
|
_ret=1
|
||||||
|
_err "The certificate upload does not appear to have worked."
|
||||||
|
_err "The remote certificate does not match the certificate we tried to upload."
|
||||||
|
_err "Please re-run with --debug 2 and review for unexpected errors. If none can be found please submit a bug."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ensure the temporary files are cleaned up
|
||||||
|
[ -f "${temp_pkcs12}" ] && rm -f "${temp_pkcs12}"
|
||||||
|
|
||||||
|
return $_ret
|
||||||
|
}
|
||||||
|
|
||||||
|
# make the certificate upload request using either
|
||||||
|
# --data binary with @ for file access in CURL
|
||||||
|
# or using --post-file for wget to ensure we upload
|
||||||
|
# the pkcs12 without getting tripped up on null bytes
|
||||||
|
#
|
||||||
|
# Usage _zyxel_upload_pkcs12 [body file name] [post boundary marker]
|
||||||
|
_zyxel_upload_pkcs12() {
|
||||||
|
bodyfilename="$1"
|
||||||
|
multipartformmarker="$2"
|
||||||
|
_post_url="${_zyxel_switch_base_uri}/cgi-bin/httpuploadcert.cgi"
|
||||||
|
httpmethod="POST"
|
||||||
|
_postContentType="multipart/form-data; boundary=${multipartformmarker}"
|
||||||
|
|
||||||
|
if [ -z "$httpmethod" ]; then
|
||||||
|
httpmethod="POST"
|
||||||
|
fi
|
||||||
|
_debug $httpmethod
|
||||||
|
_debug "_post_url" "$_post_url"
|
||||||
|
_debug2 "bodyfilename" "$bodyfilename"
|
||||||
|
_debug2 "_postContentType" "$_postContentType"
|
||||||
|
|
||||||
|
_inithttp
|
||||||
|
|
||||||
|
if [ "$_ACME_CURL" ] && [ "${ACME_USE_WGET:-0}" = "0" ]; then
|
||||||
|
_CURL="$_ACME_CURL"
|
||||||
|
if [ "$HTTPS_INSECURE" ]; then
|
||||||
|
_CURL="$_CURL --insecure "
|
||||||
|
fi
|
||||||
|
if [ "$httpmethod" = "HEAD" ]; then
|
||||||
|
_CURL="$_CURL -I "
|
||||||
|
fi
|
||||||
|
_debug "_CURL" "$_CURL"
|
||||||
|
|
||||||
|
response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data-binary "@${bodyfilename}" "$_post_url")"
|
||||||
|
|
||||||
|
_ret="$?"
|
||||||
|
if [ "$_ret" != "0" ]; then
|
||||||
|
_err "Please refer to https://curl.haxx.se/libcurl/c/libcurl-errors.html for error code: $_ret"
|
||||||
|
if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then
|
||||||
|
_err "Here is the curl dump log:"
|
||||||
|
_err "$(cat "$_CURL_DUMP")"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
elif [ "$_ACME_WGET" ]; then
|
||||||
|
_WGET="$_ACME_WGET"
|
||||||
|
if [ "$HTTPS_INSECURE" ]; then
|
||||||
|
_WGET="$_WGET --no-check-certificate "
|
||||||
|
fi
|
||||||
|
_debug "_WGET" "$_WGET"
|
||||||
|
|
||||||
|
response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --post-file="${bodyfilename}" "$_post_url" 2>"$HTTP_HEADER")"
|
||||||
|
|
||||||
|
_ret="$?"
|
||||||
|
if [ "$_ret" = "8" ]; then
|
||||||
|
_ret=0
|
||||||
|
_debug "wget returned 8 as the server returned a 'Bad Request' response. Let's process the response later."
|
||||||
|
fi
|
||||||
|
if [ "$_ret" != "0" ]; then
|
||||||
|
_err "Please refer to https://www.gnu.org/software/wget/manual/html_node/Exit-Status.html for error code: $_ret"
|
||||||
|
fi
|
||||||
|
if _contains "$_WGET" " -d "; then
|
||||||
|
# Demultiplex wget debug output
|
||||||
|
cat "$HTTP_HEADER" >&2
|
||||||
|
_sed_i '/^[^ ][^ ]/d; /^ *$/d' "$HTTP_HEADER"
|
||||||
|
fi
|
||||||
|
# remove leading whitespaces from header to match curl format
|
||||||
|
_sed_i 's/^ //g' "$HTTP_HEADER"
|
||||||
|
else
|
||||||
|
_ret="$?"
|
||||||
|
_err "Neither curl nor wget have been found, cannot make $httpmethod request."
|
||||||
|
fi
|
||||||
|
_debug "_ret" "$_ret"
|
||||||
|
printf "%s" "$response"
|
||||||
|
return $_ret
|
||||||
|
}
|
||||||
|
|
||||||
|
_zyxel_gs1900_trigger_reboot() {
|
||||||
|
# Trigger a reboot via the management reboot page in the web ui
|
||||||
|
reboot_page_html=$(_get "${_zyxel_switch_base_uri}/cgi-bin/dispatcher.cgi?cmd=5888" | tr -d '\n')
|
||||||
|
reboot_xss_value=$(printf "%s" "$reboot_page_html" | _egrep_o 'name="XSSID"\s*value="[^"]+"' | sed 's/^.*="\([^"]\{1,\}\)"$/\1/g')
|
||||||
|
_secure_debug2 "reboot_xss_value" "$reboot_xss_value"
|
||||||
|
|
||||||
|
reboot_response_html=$(_post "XSSID=${reboot_xss_value}&cmd=5889&sysSubmit=Reboot" "${_zyxel_switch_base_uri}/cgi-bin/dispatcher.cgi" '' "POST" "application/x-www-form-urlencoded")
|
||||||
|
reboot_message=$(printf "%s" "$reboot_response_html" | tr -d '\t\r\n\v\f' | _egrep_o "Rebooting now...")
|
||||||
|
|
||||||
|
if [ -z "$reboot_message" ]; then
|
||||||
|
_err "Failed to trigger switch reboot!"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# password
|
||||||
|
_zyxel_gs1900_password_obfuscate() {
|
||||||
|
# Return the password obfuscated via the same method used by the
|
||||||
|
# switch's web UI login process
|
||||||
|
echo "$1" | awk '{
|
||||||
|
encoded = "";
|
||||||
|
password = $1;
|
||||||
|
allowed = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||||
|
len = length($1);
|
||||||
|
pwi = length($1);
|
||||||
|
|
||||||
|
for (i=1; i <= (321 - pwi); i++)
|
||||||
|
{
|
||||||
|
if (0 == i % 5 && pwi > 0)
|
||||||
|
{
|
||||||
|
encoded = (encoded)(substr(password, pwi--, 1));
|
||||||
|
}
|
||||||
|
else if (i == 123)
|
||||||
|
{
|
||||||
|
if (len < 10)
|
||||||
|
{
|
||||||
|
encoded = (encoded)(0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
encoded = (encoded)(int(len / 10));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (i == 289)
|
||||||
|
{
|
||||||
|
encoded = (encoded)(len % 10)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
encoded = (encoded)(substr(allowed, int(rand() * length(allowed)), 1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("%s", encoded);
|
||||||
|
}'
|
||||||
|
}
|
||||||
|
|
||||||
|
# html label
|
||||||
|
_zyxel_html_table_lookup() {
|
||||||
|
# Look up a value in the html representing the status page of the switch
|
||||||
|
# when provided with the html of the page and the label (i.e. "Model Name:")
|
||||||
|
html="$1"
|
||||||
|
label=$(printf "%s" "$2" | tr -d ' ')
|
||||||
|
lookup_result=$(printf "%s" "$html" | tr -d "\t\r\n\v\f" | sed 's/<tr>/\n<tr>/g' | sed 's/<td[^>]*>/<td>/g' | tr -d ' ' | grep -i "$label" | sed "s/<tr><td>$label<\/td><td>\([^<]\{1,\}\)<\/td><\/tr>/\1/i")
|
||||||
|
printf "%s" "$lookup_result"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# html
|
||||||
|
_zyxel_gs1900_get_model() {
|
||||||
|
html="$1"
|
||||||
|
model_name=$(_zyxel_html_table_lookup "$html" "Model Name:")
|
||||||
|
printf "%s" "$model_name"
|
||||||
|
}
|
||||||
|
|
||||||
|
# html
|
||||||
|
_zyxel_gs1900_get_firmware_version() {
|
||||||
|
html="$1"
|
||||||
|
firmware_version=$(_zyxel_html_table_lookup "$html" "Firmware Version:" | _egrep_o "V[^.]+.[^(]+")
|
||||||
|
printf "%s" "$firmware_version"
|
||||||
|
}
|
||||||
|
|
||||||
|
# version_number
|
||||||
|
_zyxel_gs1900_parse_major_version() {
|
||||||
|
printf "%s" "$1" | sed 's/^V\([0-9]\{1,\}\).\{1,\}$/\1/gi'
|
||||||
|
}
|
||||||
|
|
||||||
|
# version_number
|
||||||
|
_zyxel_gs1900_parse_minor_version() {
|
||||||
|
printf "%s" "$1" | sed 's/^.\{1,\}\.\([0-9]\{1,\}\)$/\1/gi'
|
||||||
|
}
|
||||||
@@ -1,46 +1,42 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
#This file name is "dns_1984hosting.sh"
|
# shellcheck disable=SC2034
|
||||||
#So, here must be a method dns_1984hosting_add()
|
dns_1984hosting_info='1984.hosting
|
||||||
#Which will be called by acme.sh to add the txt record to your api system.
|
Domains: 1984.is
|
||||||
#returns 0 means success, otherwise error.
|
Site: 1984.hosting
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_1984hosting
|
||||||
|
Options:
|
||||||
|
One984HOSTING_Username Username
|
||||||
|
One984HOSTING_Password Password
|
||||||
|
Issues: github.com/acmesh-official/acme.sh/issues/2851
|
||||||
|
Author: Adrian Fedoreanu
|
||||||
|
'
|
||||||
|
|
||||||
#Author: Adrian Fedoreanu
|
######## Public functions #####################
|
||||||
#Report Bugs here: https://github.com/acmesh-official/acme.sh
|
|
||||||
# or here... https://github.com/acmesh-official/acme.sh/issues/2851
|
|
||||||
#
|
|
||||||
######## Public functions #####################
|
|
||||||
|
|
||||||
# Export 1984HOSTING username and password in following variables
|
# Usage: dns_1984hosting_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
#
|
# Add a text record.
|
||||||
# One984HOSTING_Username=username
|
|
||||||
# One984HOSTING_Password=password
|
|
||||||
#
|
|
||||||
# sessionid cookie is saved in ~/.acme.sh/account.conf
|
|
||||||
# username/password need to be set only when changed.
|
|
||||||
|
|
||||||
#Usage: dns_1984hosting_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
|
||||||
dns_1984hosting_add() {
|
dns_1984hosting_add() {
|
||||||
fulldomain=$1
|
fulldomain=$1
|
||||||
txtvalue=$2
|
txtvalue=$2
|
||||||
|
|
||||||
_info "Add TXT record using 1984Hosting"
|
_info "Add TXT record using 1984Hosting."
|
||||||
_debug fulldomain "$fulldomain"
|
_debug fulldomain "$fulldomain"
|
||||||
_debug txtvalue "$txtvalue"
|
_debug txtvalue "$txtvalue"
|
||||||
|
|
||||||
if ! _1984hosting_login; then
|
if ! _1984hosting_login; then
|
||||||
_err "1984Hosting login failed for user $One984HOSTING_Username. Check $HTTP_HEADER file"
|
_err "1984Hosting login failed for user $One984HOSTING_Username. Check $HTTP_HEADER file."
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
_debug "First detect the root zone"
|
_debug "First detect the root zone."
|
||||||
if ! _get_root "$fulldomain"; then
|
if ! _get_root "$fulldomain"; then
|
||||||
_err "invalid domain" "$fulldomain"
|
_err "Invalid domain '$fulldomain'."
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
_debug _sub_domain "$_sub_domain"
|
_debug _sub_domain "$_sub_domain"
|
||||||
_debug _domain "$_domain"
|
_debug _domain "$_domain"
|
||||||
|
|
||||||
_debug "Add TXT record $fulldomain with value '$txtvalue'"
|
_debug "Add TXT record $fulldomain with value '$txtvalue'."
|
||||||
value="$(printf '%s' "$txtvalue" | _url_encode)"
|
value="$(printf '%s' "$txtvalue" | _url_encode)"
|
||||||
url="https://1984.hosting/domains/entry/"
|
url="https://1984.hosting/domains/entry/"
|
||||||
|
|
||||||
@@ -53,102 +49,108 @@ dns_1984hosting_add() {
|
|||||||
_debug2 postdata "$postdata"
|
_debug2 postdata "$postdata"
|
||||||
|
|
||||||
_authpost "$postdata" "$url"
|
_authpost "$postdata" "$url"
|
||||||
response="$(echo "$_response" | _normalizeJson)"
|
if _contains "$_response" '"haserrors": true'; then
|
||||||
_debug2 response "$response"
|
_err "1984Hosting failed to add TXT record for $_sub_domain bad RC from _post."
|
||||||
|
|
||||||
if _contains "$response" '"haserrors": true'; then
|
|
||||||
_err "1984Hosting failed to add TXT record for $_sub_domain bad RC from _post"
|
|
||||||
return 1
|
return 1
|
||||||
elif _contains "$response" "html>"; then
|
elif _contains "$_response" "html>"; then
|
||||||
_err "1984Hosting failed to add TXT record for $_sub_domain. Check $HTTP_HEADER file"
|
_err "1984Hosting failed to add TXT record for $_sub_domain. Check $HTTP_HEADER file."
|
||||||
return 1
|
return 1
|
||||||
elif _contains "$response" '"auth": false'; then
|
elif _contains "$_response" '"auth": false'; then
|
||||||
_err "1984Hosting failed to add TXT record for $_sub_domain. Invalid or expired cookie"
|
_err "1984Hosting failed to add TXT record for $_sub_domain. Invalid or expired cookie."
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
_info "Added acme challenge TXT record for $fulldomain at 1984Hosting"
|
_info "Added acme challenge TXT record for $fulldomain at 1984Hosting."
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
#Usage: fulldomain txtvalue
|
# Usage: fulldomain txtvalue
|
||||||
#Remove the txt record after validation.
|
# Remove the txt record after validation.
|
||||||
dns_1984hosting_rm() {
|
dns_1984hosting_rm() {
|
||||||
fulldomain=$1
|
fulldomain=$1
|
||||||
txtvalue=$2
|
txtvalue=$2
|
||||||
|
|
||||||
_info "Delete TXT record using 1984Hosting"
|
_info "Delete TXT record using 1984Hosting."
|
||||||
_debug fulldomain "$fulldomain"
|
_debug fulldomain "$fulldomain"
|
||||||
_debug txtvalue "$txtvalue"
|
_debug txtvalue "$txtvalue"
|
||||||
|
|
||||||
if ! _1984hosting_login; then
|
if ! _1984hosting_login; then
|
||||||
_err "1984Hosting login failed for user $One984HOSTING_Username. Check $HTTP_HEADER file"
|
_err "1984Hosting login failed for user $One984HOSTING_Username. Check $HTTP_HEADER file."
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
_debug "First detect the root zone"
|
_debug "First detect the root zone."
|
||||||
if ! _get_root "$fulldomain"; then
|
if ! _get_root "$fulldomain"; then
|
||||||
_err "invalid domain" "$fulldomain"
|
_err "Invalid domain '$fulldomain'."
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
_debug _sub_domain "$_sub_domain"
|
_debug _sub_domain "$_sub_domain"
|
||||||
_debug _domain "$_domain"
|
_debug _domain "$_domain"
|
||||||
_debug "Delete $fulldomain TXT record"
|
_debug "Delete $fulldomain TXT record."
|
||||||
|
|
||||||
url="https://1984.hosting/domains"
|
url="https://1984.hosting/domains"
|
||||||
if ! _get_zone_id "$url" "$_domain"; then
|
if ! _get_zone_id "$url" "$_domain"; then
|
||||||
_err "invalid zone" "$_domain"
|
_err "Invalid zone '$_domain'."
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
_htmlget "$url/$_zone_id" "$txtvalue"
|
_htmlget "$url/$_zone_id" "$txtvalue"
|
||||||
_debug2 _response "$_response"
|
|
||||||
entry_id="$(echo "$_response" | _egrep_o 'entry_[0-9]+' | sed 's/entry_//')"
|
entry_id="$(echo "$_response" | _egrep_o 'entry_[0-9]+' | sed 's/entry_//')"
|
||||||
_debug2 entry_id "$entry_id"
|
_debug2 entry_id "$entry_id"
|
||||||
if [ -z "$entry_id" ]; then
|
if [ -z "$entry_id" ]; then
|
||||||
_err "Error getting TXT entry_id for $1"
|
_err "Error getting TXT entry_id for $1."
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
_authpost "entry=$entry_id" "$url/delentry/"
|
_authpost "entry=$entry_id" "$url/delentry/"
|
||||||
response="$(echo "$_response" | _normalizeJson)"
|
if ! _contains "$_response" '"ok": true'; then
|
||||||
_debug2 response "$response"
|
_err "1984Hosting failed to delete TXT record for $entry_id bad RC from _post."
|
||||||
|
|
||||||
if ! _contains "$response" '"ok": true'; then
|
|
||||||
_err "1984Hosting failed to delete TXT record for $entry_id bad RC from _post"
|
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
_info "Deleted acme challenge TXT record for $fulldomain at 1984Hosting"
|
_info "Deleted acme challenge TXT record for $fulldomain at 1984Hosting."
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
#################### Private functions below ##################################
|
#################### Private functions below ##################################
|
||||||
|
|
||||||
# usage: _1984hosting_login username password
|
|
||||||
# returns 0 success
|
|
||||||
_1984hosting_login() {
|
_1984hosting_login() {
|
||||||
if ! _check_credentials; then return 1; fi
|
if ! _check_credentials; then return 1; fi
|
||||||
|
|
||||||
if _check_cookies; then
|
if _check_cookies; then
|
||||||
_debug "Already logged in"
|
_debug "Already logged in."
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
_debug "Login to 1984Hosting as user $One984HOSTING_Username"
|
_debug "Login to 1984Hosting as user $One984HOSTING_Username."
|
||||||
username=$(printf '%s' "$One984HOSTING_Username" | _url_encode)
|
username=$(printf '%s' "$One984HOSTING_Username" | _url_encode)
|
||||||
password=$(printf '%s' "$One984HOSTING_Password" | _url_encode)
|
password=$(printf '%s' "$One984HOSTING_Password" | _url_encode)
|
||||||
url="https://1984.hosting/accounts/checkuserauth/"
|
url="https://1984.hosting/api/auth/"
|
||||||
|
|
||||||
|
_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 'cookie1984nammnamm=[^;]*;' | tr -d ';')"
|
||||||
|
|
||||||
|
if [ -z "$csrftoken" ] || [ -z "$sessionid" ]; then
|
||||||
|
_err "One or more cookies are empty: '$csrftoken', '$sessionid'."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
export _H1="Cookie: $csrftoken; $sessionid"
|
||||||
|
export _H2="Referer: https://1984.hosting/accounts/login/"
|
||||||
|
csrf_header=$(echo "$csrftoken" | sed 's/csrftoken=//' | _head_n 1)
|
||||||
|
export _H3="X-CSRFToken: $csrf_header"
|
||||||
|
|
||||||
response="$(_post "username=$username&password=$password&otpkey=" $url)"
|
response="$(_post "username=$username&password=$password&otpkey=" $url)"
|
||||||
response="$(echo "$response" | _normalizeJson)"
|
response="$(echo "$response" | _normalizeJson)"
|
||||||
_debug2 response "$response"
|
_debug2 response "$response"
|
||||||
|
|
||||||
if _contains "$response" '"loggedin": true'; then
|
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 ';')"
|
One984HOSTING_CSRFTOKEN_COOKIE="$(grep -i '^set-cookie:' "$HTTP_HEADER" | _egrep_o 'csrftoken=[^;]*;' | tr -d ';')"
|
||||||
export One984HOSTING_SESSIONID_COOKIE
|
export One984HOSTING_SESSIONID_COOKIE
|
||||||
export One984HOSTING_CSRFTOKEN_COOKIE
|
export One984HOSTING_CSRFTOKEN_COOKIE
|
||||||
|
_saveaccountconf_mutable One984HOSTING_Username "$One984HOSTING_Username"
|
||||||
|
_saveaccountconf_mutable One984HOSTING_Password "$One984HOSTING_Password"
|
||||||
_saveaccountconf_mutable One984HOSTING_SESSIONID_COOKIE "$One984HOSTING_SESSIONID_COOKIE"
|
_saveaccountconf_mutable One984HOSTING_SESSIONID_COOKIE "$One984HOSTING_SESSIONID_COOKIE"
|
||||||
_saveaccountconf_mutable One984HOSTING_CSRFTOKEN_COOKIE "$One984HOSTING_CSRFTOKEN_COOKIE"
|
_saveaccountconf_mutable One984HOSTING_CSRFTOKEN_COOKIE "$One984HOSTING_CSRFTOKEN_COOKIE"
|
||||||
return 0
|
return 0
|
||||||
@@ -157,9 +159,13 @@ _1984hosting_login() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_check_credentials() {
|
_check_credentials() {
|
||||||
|
One984HOSTING_Username="${One984HOSTING_Username:-$(_readaccountconf_mutable One984HOSTING_Username)}"
|
||||||
|
One984HOSTING_Password="${One984HOSTING_Password:-$(_readaccountconf_mutable One984HOSTING_Password)}"
|
||||||
if [ -z "$One984HOSTING_Username" ] || [ -z "$One984HOSTING_Password" ]; then
|
if [ -z "$One984HOSTING_Username" ] || [ -z "$One984HOSTING_Password" ]; then
|
||||||
One984HOSTING_Username=""
|
One984HOSTING_Username=""
|
||||||
One984HOSTING_Password=""
|
One984HOSTING_Password=""
|
||||||
|
_clearaccountconf_mutable One984HOSTING_Username
|
||||||
|
_clearaccountconf_mutable One984HOSTING_Password
|
||||||
_err "You haven't specified 1984Hosting username or password yet."
|
_err "You haven't specified 1984Hosting username or password yet."
|
||||||
_err "Please export as One984HOSTING_Username / One984HOSTING_Password and try again."
|
_err "Please export as One984HOSTING_Username / One984HOSTING_Password and try again."
|
||||||
return 1
|
return 1
|
||||||
@@ -171,42 +177,43 @@ _check_cookies() {
|
|||||||
One984HOSTING_SESSIONID_COOKIE="${One984HOSTING_SESSIONID_COOKIE:-$(_readaccountconf_mutable One984HOSTING_SESSIONID_COOKIE)}"
|
One984HOSTING_SESSIONID_COOKIE="${One984HOSTING_SESSIONID_COOKIE:-$(_readaccountconf_mutable One984HOSTING_SESSIONID_COOKIE)}"
|
||||||
One984HOSTING_CSRFTOKEN_COOKIE="${One984HOSTING_CSRFTOKEN_COOKIE:-$(_readaccountconf_mutable One984HOSTING_CSRFTOKEN_COOKIE)}"
|
One984HOSTING_CSRFTOKEN_COOKIE="${One984HOSTING_CSRFTOKEN_COOKIE:-$(_readaccountconf_mutable One984HOSTING_CSRFTOKEN_COOKIE)}"
|
||||||
if [ -z "$One984HOSTING_SESSIONID_COOKIE" ] || [ -z "$One984HOSTING_CSRFTOKEN_COOKIE" ]; then
|
if [ -z "$One984HOSTING_SESSIONID_COOKIE" ] || [ -z "$One984HOSTING_CSRFTOKEN_COOKIE" ]; then
|
||||||
_debug "No cached cookie(s) found"
|
_debug "No cached cookie(s) found."
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
_authget "https://1984.hosting/accounts/loginstatus/"
|
_authget "https://1984.hosting/api/auth/"
|
||||||
if _contains "$response" '"ok": true'; then
|
if _contains "$_response" '"ok": true'; then
|
||||||
_debug "Cached cookies still valid"
|
_debug "Cached cookies still valid."
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
_debug "Cached cookies no longer valid"
|
|
||||||
|
_debug "Cached cookies no longer valid. Clearing cookies."
|
||||||
One984HOSTING_SESSIONID_COOKIE=""
|
One984HOSTING_SESSIONID_COOKIE=""
|
||||||
One984HOSTING_CSRFTOKEN_COOKIE=""
|
One984HOSTING_CSRFTOKEN_COOKIE=""
|
||||||
_saveaccountconf_mutable One984HOSTING_SESSIONID_COOKIE "$One984HOSTING_SESSIONID_COOKIE"
|
_clearaccountconf_mutable One984HOSTING_SESSIONID_COOKIE
|
||||||
_saveaccountconf_mutable One984HOSTING_CSRFTOKEN_COOKIE "$One984HOSTING_CSRFTOKEN_COOKIE"
|
_clearaccountconf_mutable One984HOSTING_CSRFTOKEN_COOKIE
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
#_acme-challenge.www.domain.com
|
# _acme-challenge.www.domain.com
|
||||||
#returns
|
# Returns
|
||||||
# _sub_domain=_acme-challenge.www
|
# _sub_domain=_acme-challenge.www
|
||||||
# _domain=domain.com
|
# _domain=domain.com
|
||||||
_get_root() {
|
_get_root() {
|
||||||
domain="$1"
|
domain="$1"
|
||||||
i=1
|
i=1
|
||||||
p=1
|
p=1
|
||||||
while true; do
|
while true; do
|
||||||
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
|
||||||
|
|
||||||
|
# not valid
|
||||||
if [ -z "$h" ]; then
|
if [ -z "$h" ]; then
|
||||||
#not valid
|
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
_authget "https://1984.hosting/domains/soacheck/?zone=$h&nameserver=ns0.1984.is."
|
_authget "https://1984.hosting/domains/zonestatus/$h/?cached=no"
|
||||||
if _contains "$_response" "serial" && ! _contains "$_response" "null"; then
|
if _contains "$_response" '"ok": true'; then
|
||||||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
|
||||||
_domain="$h"
|
_domain="$h"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
@@ -216,46 +223,46 @@ _get_root() {
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
#usage: _get_zone_id url domain.com
|
# Usage: _get_zone_id url domain.com
|
||||||
#returns zone id for domain.com
|
# Returns zone id for domain.com
|
||||||
_get_zone_id() {
|
_get_zone_id() {
|
||||||
url=$1
|
url=$1
|
||||||
domain=$2
|
domain=$2
|
||||||
_htmlget "$url" "$domain"
|
_htmlget "$url" "$domain"
|
||||||
_debug2 _response "$_response"
|
|
||||||
_zone_id="$(echo "$_response" | _egrep_o 'zone\/[0-9]+' | _head_n 1)"
|
_zone_id="$(echo "$_response" | _egrep_o 'zone\/[0-9]+' | _head_n 1)"
|
||||||
_debug2 _zone_id "$_zone_id"
|
_debug2 _zone_id "$_zone_id"
|
||||||
if [ -z "$_zone_id" ]; then
|
if [ -z "$_zone_id" ]; then
|
||||||
_err "Error getting _zone_id for $2"
|
_err "Error getting _zone_id for $2."
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
# add extra headers to request
|
# Add extra headers to request
|
||||||
_authget() {
|
_authget() {
|
||||||
export _H1="Cookie: $One984HOSTING_CSRFTOKEN_COOKIE;$One984HOSTING_SESSIONID_COOKIE"
|
export _H1="Cookie: $One984HOSTING_CSRFTOKEN_COOKIE; $One984HOSTING_SESSIONID_COOKIE"
|
||||||
_response=$(_get "$1" | _normalizeJson)
|
_response=$(_get "$1" | _normalizeJson)
|
||||||
_debug2 _response "$_response"
|
_debug2 _response "$_response"
|
||||||
}
|
}
|
||||||
|
|
||||||
# truncate huge HTML response
|
# Truncate huge HTML response
|
||||||
# echo: Argument list too long
|
|
||||||
_htmlget() {
|
_htmlget() {
|
||||||
export _H1="Cookie: $One984HOSTING_CSRFTOKEN_COOKIE;$One984HOSTING_SESSIONID_COOKIE"
|
export _H1="Cookie: $One984HOSTING_CSRFTOKEN_COOKIE; $One984HOSTING_SESSIONID_COOKIE"
|
||||||
_response=$(_get "$1" | grep "$2")
|
_response=$(_get "$1" | grep "$2")
|
||||||
if _contains "$_response" "@$2"; then
|
if _contains "$_response" "@$2"; then
|
||||||
_response=$(echo "$_response" | grep -v "[@]" | _head_n 1)
|
_response=$(echo "$_response" | grep -v "[@]" | _head_n 1)
|
||||||
fi
|
fi
|
||||||
|
_debug2 _response "$_response"
|
||||||
}
|
}
|
||||||
|
|
||||||
# add extra headers to request
|
# Add extra headers to request
|
||||||
_authpost() {
|
_authpost() {
|
||||||
url="https://1984.hosting/domains"
|
url="https://1984.hosting/domains"
|
||||||
_get_zone_id "$url" "$_domain"
|
_get_zone_id "$url" "$_domain"
|
||||||
csrf_header="$(echo "$One984HOSTING_CSRFTOKEN_COOKIE" | _egrep_o "=[^=][0-9a-zA-Z]*" | tr -d "=")"
|
csrf_header="$(echo "$One984HOSTING_CSRFTOKEN_COOKIE" | _egrep_o "=[^=][0-9a-zA-Z]*" | tr -d "=")"
|
||||||
export _H1="Cookie: $One984HOSTING_CSRFTOKEN_COOKIE;$One984HOSTING_SESSIONID_COOKIE"
|
export _H1="Cookie: $One984HOSTING_CSRFTOKEN_COOKIE; $One984HOSTING_SESSIONID_COOKIE"
|
||||||
export _H2="Referer: https://1984.hosting/domains/$_zone_id"
|
export _H2="Referer: https://1984.hosting/domains/$_zone_id"
|
||||||
export _H3="X-CSRFToken: $csrf_header"
|
export _H3="X-CSRFToken: $csrf_header"
|
||||||
_response=$(_post "$1" "$2")
|
_response="$(_post "$1" "$2" | _normalizeJson)"
|
||||||
|
_debug2 _response "$_response"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
#
|
# shellcheck disable=SC2034
|
||||||
#Author: Wolfgang Ebner
|
dns_acmedns_info='acme-dns Server API
|
||||||
#Author: Sven Neubuaer
|
The acme-dns is a limited DNS server with RESTful API to handle ACME DNS challenges.
|
||||||
#Report Bugs here: https://github.com/dampfklon/acme.sh
|
Site: github.com/joohoi/acme-dns
|
||||||
#
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_acmedns
|
||||||
# Usage:
|
Options:
|
||||||
# export ACMEDNS_BASE_URL="https://auth.acme-dns.io"
|
ACMEDNS_USERNAME Username. Optional.
|
||||||
#
|
ACMEDNS_PASSWORD Password. Optional.
|
||||||
# You can optionally define an already existing account:
|
ACMEDNS_SUBDOMAIN Subdomain. Optional.
|
||||||
#
|
ACMEDNS_BASE_URL API endpoint. Default: "https://auth.acme-dns.io".
|
||||||
# export ACMEDNS_USERNAME="<username>"
|
Issues: github.com/dampfklon/acme.sh
|
||||||
# export ACMEDNS_PASSWORD="<password>"
|
Author: Wolfgang Ebner, Sven Neubuaer
|
||||||
# export ACMEDNS_SUBDOMAIN="<subdomain>"
|
'
|
||||||
#
|
|
||||||
######## Public functions #####################
|
######## Public functions #####################
|
||||||
|
|
||||||
#Usage: dns_acmedns_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
#Usage: dns_acmedns_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
|
|||||||
18
dnsapi/dns_acmeproxy.sh
Normal file → Executable file
18
dnsapi/dns_acmeproxy.sh
Normal file → Executable file
@@ -1,9 +1,17 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
## Acmeproxy DNS provider to be used with acmeproxy (https://github.com/mdbraber/acmeproxy)
|
dns_acmeproxy_info='AcmeProxy Server API
|
||||||
## API integration by Maarten den Braber
|
AcmeProxy can be used to as a single host in your network to request certificates through a DNS API.
|
||||||
##
|
Clients can connect with the one AcmeProxy host so you do not need to store DNS API credentials on every single host.
|
||||||
## Report any bugs via https://github.com/mdbraber/acme.sh
|
Site: github.com/mdbraber/acmeproxy
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_acmeproxy
|
||||||
|
Options:
|
||||||
|
ACMEPROXY_ENDPOINT API Endpoint
|
||||||
|
ACMEPROXY_USERNAME Username
|
||||||
|
ACMEPROXY_PASSWORD Password
|
||||||
|
Issues: github.com/acmesh-official/acme.sh/issues/2251
|
||||||
|
Author: Maarten den Braber
|
||||||
|
'
|
||||||
|
|
||||||
dns_acmeproxy_add() {
|
dns_acmeproxy_add() {
|
||||||
fulldomain="${1}"
|
fulldomain="${1}"
|
||||||
|
|||||||
@@ -1,10 +1,17 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
dns_active24_info='Active24.cz
|
||||||
|
Site: Active24.cz
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_active24
|
||||||
|
Options:
|
||||||
|
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
|
||||||
|
'
|
||||||
|
|
||||||
#ACTIVE24_Token="sdfsdfsdfljlbjkljlkjsdfoiwje"
|
Active24_Api="https://rest.active24.cz"
|
||||||
|
# export Active24_ApiKey=ak48l3h7-ak5d-qn4t-p8gc-b6fs8c3l
|
||||||
ACTIVE24_Api="https://api.active24.com"
|
# export Active24_ApiSecret=ajvkeo3y82ndsu2smvxy3o36496dcascksldncsq
|
||||||
|
|
||||||
######## Public functions #####################
|
|
||||||
|
|
||||||
# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
# Used to add txt record
|
# Used to add txt record
|
||||||
@@ -15,8 +22,8 @@ dns_active24_add() {
|
|||||||
_active24_init
|
_active24_init
|
||||||
|
|
||||||
_info "Adding txt record"
|
_info "Adding txt record"
|
||||||
if _active24_rest POST "dns/$_domain/txt/v1" "{\"name\":\"$_sub_domain\",\"text\":\"$txtvalue\",\"ttl\":0}"; then
|
if _active24_rest POST "/v2/service/$_service_id/dns/record" "{\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"content\":\"$txtvalue\",\"ttl\":300}"; then
|
||||||
if _contains "$response" "errors"; then
|
if _contains "$response" "error"; then
|
||||||
_err "Add txt record error."
|
_err "Add txt record error."
|
||||||
return 1
|
return 1
|
||||||
else
|
else
|
||||||
@@ -24,6 +31,7 @@ dns_active24_add() {
|
|||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
_err "Add txt record error."
|
_err "Add txt record error."
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
@@ -37,19 +45,25 @@ dns_active24_rm() {
|
|||||||
_active24_init
|
_active24_init
|
||||||
|
|
||||||
_debug "Getting txt records"
|
_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"
|
_err "Error"
|
||||||
return 1
|
return 1
|
||||||
fi
|
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
|
for redord_id in $record_ids; do
|
||||||
_debug "Removing hash_id" "$hash_id"
|
_debug "Removing record_id" "$redord_id"
|
||||||
if _active24_rest DELETE "dns/$_domain/$hash_id/v1" ""; then
|
_debug "txtvalue" "$txtvalue"
|
||||||
if _contains "$response" "errors"; then
|
if _active24_rest DELETE "/v2/service/$_service_id/dns/record/$redord_id" ""; then
|
||||||
|
if _contains "$response" "error"; then
|
||||||
_err "Unable to remove txt record."
|
_err "Unable to remove txt record."
|
||||||
return 1
|
return 1
|
||||||
else
|
else
|
||||||
@@ -63,23 +77,17 @@ dns_active24_rm() {
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
#################### Private functions below ##################################
|
|
||||||
#_acme-challenge.www.domain.com
|
|
||||||
#returns
|
|
||||||
# _sub_domain=_acme-challenge.www
|
|
||||||
# _domain=domain.com
|
|
||||||
# _domain_id=sdjkglgdfewsdfg
|
|
||||||
_get_root() {
|
_get_root() {
|
||||||
domain=$1
|
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
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
i=2
|
|
||||||
p=1
|
|
||||||
while true; do
|
while true; do
|
||||||
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
|
||||||
_debug "h" "$h"
|
_debug "h" "$h"
|
||||||
if [ -z "$h" ]; then
|
if [ -z "$h" ]; then
|
||||||
#not valid
|
#not valid
|
||||||
@@ -87,7 +95,7 @@ _get_root() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if _contains "$response" "\"$h\"" >/dev/null; then
|
if _contains "$response" "\"$h\"" >/dev/null; then
|
||||||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
|
||||||
_domain=$h
|
_domain=$h
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
@@ -97,21 +105,98 @@ _get_root() {
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
_active24_rest() {
|
_active24_init() {
|
||||||
m=$1
|
Active24_ApiKey="${Active24_ApiKey:-$(_readaccountconf_mutable Active24_ApiKey)}"
|
||||||
ep="$2"
|
Active24_ApiSecret="${Active24_ApiSecret:-$(_readaccountconf_mutable Active24_ApiSecret)}"
|
||||||
data="$3"
|
#Active24_ServiceId="${Active24_ServiceId:-$(_readaccountconf_mutable Active24_ServiceId)}"
|
||||||
_debug "$ep"
|
|
||||||
|
|
||||||
export _H1="Authorization: Bearer $ACTIVE24_Token"
|
if [ -z "$Active24_ApiKey" ] || [ -z "$Active24_ApiSecret" ]; then
|
||||||
|
Active24_ApiKey=""
|
||||||
if [ "$m" != "GET" ]; then
|
Active24_ApiSecret=""
|
||||||
_debug "data" "$data"
|
_err "You don't specify Active24 api key and ApiSecret yet."
|
||||||
response="$(_post "$data" "$ACTIVE24_Api/$ep" "" "$m" "application/json")"
|
_err "Please create your key and try again."
|
||||||
else
|
return 1
|
||||||
response="$(_get "$ACTIVE24_Api/$ep")"
|
|
||||||
fi
|
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
|
if [ "$?" != "0" ]; then
|
||||||
_err "error $ep"
|
_err "error $ep"
|
||||||
return 1
|
return 1
|
||||||
@@ -119,23 +204,3 @@ _active24_rest() {
|
|||||||
_debug2 response "$response"
|
_debug2 response "$response"
|
||||||
return 0
|
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"
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
#
|
dns_ad_info='AlwaysData.com
|
||||||
#AD_API_KEY="sdfsdfsdfljlbjkljlkjsdfoiwje"
|
Site: AlwaysData.com
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_ad
|
||||||
#This is the Alwaysdata api wrapper for acme.sh
|
Options:
|
||||||
#
|
AD_API_KEY API Key
|
||||||
#Author: Paul Koppen
|
Issues: github.com/acmesh-official/acme.sh/pull/503
|
||||||
#Report Bugs here: https://github.com/wpk-/acme.sh
|
Author: Paul Koppen
|
||||||
|
'
|
||||||
|
|
||||||
AD_API_URL="https://$AD_API_KEY:@api.alwaysdata.com/v1"
|
AD_API_URL="https://$AD_API_KEY:@api.alwaysdata.com/v1"
|
||||||
|
|
||||||
@@ -94,7 +95,7 @@ _get_root() {
|
|||||||
if _ad_rest GET "domain/"; then
|
if _ad_rest GET "domain/"; then
|
||||||
response="$(echo "$response" | tr -d "\n" | sed 's/{/\n&/g')"
|
response="$(echo "$response" | tr -d "\n" | sed 's/{/\n&/g')"
|
||||||
while true; do
|
while true; do
|
||||||
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
|
||||||
_debug h "$h"
|
_debug h "$h"
|
||||||
if [ -z "$h" ]; then
|
if [ -z "$h" ]; then
|
||||||
#not valid
|
#not valid
|
||||||
@@ -105,7 +106,7 @@ _get_root() {
|
|||||||
if [ "$hostedzone" ]; then
|
if [ "$hostedzone" ]; then
|
||||||
_domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o "\"id\":\s*[0-9]+" | _head_n 1 | cut -d : -f 2 | tr -d \ )
|
_domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o "\"id\":\s*[0-9]+" | _head_n 1 | cut -d : -f 2 | tr -d \ )
|
||||||
if [ "$_domain_id" ]; then
|
if [ "$_domain_id" ]; then
|
||||||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
|
||||||
_domain=$h
|
_domain=$h
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -1,27 +1,27 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
dns_ali_info='AlibabaCloud.com
|
||||||
|
Domains: Aliyun.com
|
||||||
|
Site: AlibabaCloud.com
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_ali
|
||||||
|
Options:
|
||||||
|
Ali_Key API Key
|
||||||
|
Ali_Secret API Secret
|
||||||
|
'
|
||||||
|
|
||||||
Ali_API="https://alidns.aliyuncs.com/"
|
# NOTICE:
|
||||||
|
# This file is referenced by Alibaba Cloud Services deploy hooks
|
||||||
|
# https://github.com/acmesh-official/acme.sh/pull/5205#issuecomment-2357867276
|
||||||
|
# Be careful when modifying this file, especially when making breaking changes for common functions
|
||||||
|
|
||||||
#Ali_Key="LTqIA87hOKdjevsf5"
|
Ali_DNS_API="https://alidns.aliyuncs.com/"
|
||||||
#Ali_Secret="0p5EYueFNq501xnCPzKNbx6K51qPH2"
|
|
||||||
|
|
||||||
#Usage: dns_ali_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
#Usage: dns_ali_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
dns_ali_add() {
|
dns_ali_add() {
|
||||||
fulldomain=$1
|
fulldomain=$1
|
||||||
txtvalue=$2
|
txtvalue=$2
|
||||||
|
|
||||||
Ali_Key="${Ali_Key:-$(_readaccountconf_mutable Ali_Key)}"
|
_prepare_ali_credentials || return 1
|
||||||
Ali_Secret="${Ali_Secret:-$(_readaccountconf_mutable Ali_Secret)}"
|
|
||||||
if [ -z "$Ali_Key" ] || [ -z "$Ali_Secret" ]; then
|
|
||||||
Ali_Key=""
|
|
||||||
Ali_Secret=""
|
|
||||||
_err "You don't specify aliyun api key and secret yet."
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
#save the api key and secret to the account conf file.
|
|
||||||
_saveaccountconf_mutable Ali_Key "$Ali_Key"
|
|
||||||
_saveaccountconf_mutable Ali_Secret "$Ali_Secret"
|
|
||||||
|
|
||||||
_debug "First detect the root zone"
|
_debug "First detect the root zone"
|
||||||
if ! _get_root "$fulldomain"; then
|
if ! _get_root "$fulldomain"; then
|
||||||
@@ -46,14 +46,75 @@ dns_ali_rm() {
|
|||||||
_clean
|
_clean
|
||||||
}
|
}
|
||||||
|
|
||||||
#################### Private functions below ##################################
|
#################### Alibaba Cloud common functions below ####################
|
||||||
|
|
||||||
|
_prepare_ali_credentials() {
|
||||||
|
Ali_Key="${Ali_Key:-$(_readaccountconf_mutable Ali_Key)}"
|
||||||
|
Ali_Secret="${Ali_Secret:-$(_readaccountconf_mutable Ali_Secret)}"
|
||||||
|
if [ -z "$Ali_Key" ] || [ -z "$Ali_Secret" ]; then
|
||||||
|
Ali_Key=""
|
||||||
|
Ali_Secret=""
|
||||||
|
_err "You don't specify aliyun api key and secret yet."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
#save the api key and secret to the account conf file.
|
||||||
|
_saveaccountconf_mutable Ali_Key "$Ali_Key"
|
||||||
|
_saveaccountconf_mutable Ali_Secret "$Ali_Secret"
|
||||||
|
}
|
||||||
|
|
||||||
|
# act ign mtd
|
||||||
|
_ali_rest() {
|
||||||
|
act="$1"
|
||||||
|
ign="$2"
|
||||||
|
mtd="${3:-GET}"
|
||||||
|
|
||||||
|
signature=$(printf "%s" "$mtd&%2F&$(printf "%s" "$query" | _url_encode upper-hex)" | _hmac "sha1" "$(printf "%s" "$Ali_Secret&" | _hex_dump | tr -d " ")" | _base64)
|
||||||
|
signature=$(printf "%s" "$signature" | _url_encode upper-hex)
|
||||||
|
url="$endpoint?Signature=$signature"
|
||||||
|
|
||||||
|
if [ "$mtd" = "GET" ]; then
|
||||||
|
url="$url&$query"
|
||||||
|
response="$(_get "$url")"
|
||||||
|
else
|
||||||
|
response="$(_post "$query" "$url" "" "$mtd" "application/x-www-form-urlencoded")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
_ret="$?"
|
||||||
|
_debug2 response "$response"
|
||||||
|
if [ "$_ret" != "0" ]; then
|
||||||
|
_err "Error <$act>"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$ign" ]; then
|
||||||
|
message="$(echo "$response" | _egrep_o "\"Message\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")"
|
||||||
|
if [ "$message" ]; then
|
||||||
|
_err "$message"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_ali_nonce() {
|
||||||
|
if [ "$ACME_OPENSSL_BIN" ]; then
|
||||||
|
"$ACME_OPENSSL_BIN" rand -hex 16 2>/dev/null && return 0
|
||||||
|
fi
|
||||||
|
printf "%s" "$(date +%s)$$$(date +%N)" | _digest sha256 hex | cut -c 1-32
|
||||||
|
}
|
||||||
|
|
||||||
|
_ali_timestamp() {
|
||||||
|
date -u +"%Y-%m-%dT%H%%3A%M%%3A%SZ"
|
||||||
|
}
|
||||||
|
|
||||||
|
#################### Private functions below ####################
|
||||||
|
|
||||||
_get_root() {
|
_get_root() {
|
||||||
domain=$1
|
domain=$1
|
||||||
i=2
|
i=1
|
||||||
p=1
|
p=1
|
||||||
while true; do
|
while true; do
|
||||||
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
|
||||||
if [ -z "$h" ]; then
|
if [ -z "$h" ]; then
|
||||||
#not valid
|
#not valid
|
||||||
return 1
|
return 1
|
||||||
@@ -65,7 +126,7 @@ _get_root() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if _contains "$response" "PageNumber"; then
|
if _contains "$response" "PageNumber"; then
|
||||||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
|
||||||
_debug _sub_domain "$_sub_domain"
|
_debug _sub_domain "$_sub_domain"
|
||||||
_domain="$h"
|
_domain="$h"
|
||||||
_debug _domain "$_domain"
|
_debug _domain "$_domain"
|
||||||
@@ -77,52 +138,10 @@ _get_root() {
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
_ali_rest() {
|
|
||||||
signature=$(printf "%s" "GET&%2F&$(_ali_urlencode "$query")" | _hmac "sha1" "$(printf "%s" "$Ali_Secret&" | _hex_dump | tr -d " ")" | _base64)
|
|
||||||
signature=$(_ali_urlencode "$signature")
|
|
||||||
url="$Ali_API?$query&Signature=$signature"
|
|
||||||
|
|
||||||
if ! response="$(_get "$url")"; then
|
|
||||||
_err "Error <$1>"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
_debug2 response "$response"
|
|
||||||
if [ -z "$2" ]; then
|
|
||||||
message="$(echo "$response" | _egrep_o "\"Message\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")"
|
|
||||||
if [ "$message" ]; then
|
|
||||||
_err "$message"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
_ali_urlencode() {
|
|
||||||
_str="$1"
|
|
||||||
_str_len=${#_str}
|
|
||||||
_u_i=1
|
|
||||||
while [ "$_u_i" -le "$_str_len" ]; do
|
|
||||||
_str_c="$(printf "%s" "$_str" | cut -c "$_u_i")"
|
|
||||||
case $_str_c in [a-zA-Z0-9.~_-])
|
|
||||||
printf "%s" "$_str_c"
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
printf "%%%02X" "'$_str_c"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
_u_i="$(_math "$_u_i" + 1)"
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
_ali_nonce() {
|
|
||||||
#_head_n 1 </dev/urandom | _digest "sha256" hex | cut -c 1-31
|
|
||||||
#Not so good...
|
|
||||||
date +"%s%N"
|
|
||||||
}
|
|
||||||
|
|
||||||
_check_exist_query() {
|
_check_exist_query() {
|
||||||
_qdomain="$1"
|
_qdomain="$1"
|
||||||
_qsubdomain="$2"
|
_qsubdomain="$2"
|
||||||
|
endpoint=$Ali_DNS_API
|
||||||
query=''
|
query=''
|
||||||
query=$query'AccessKeyId='$Ali_Key
|
query=$query'AccessKeyId='$Ali_Key
|
||||||
query=$query'&Action=DescribeDomainRecords'
|
query=$query'&Action=DescribeDomainRecords'
|
||||||
@@ -132,12 +151,13 @@ _check_exist_query() {
|
|||||||
query=$query'&SignatureMethod=HMAC-SHA1'
|
query=$query'&SignatureMethod=HMAC-SHA1'
|
||||||
query=$query"&SignatureNonce=$(_ali_nonce)"
|
query=$query"&SignatureNonce=$(_ali_nonce)"
|
||||||
query=$query'&SignatureVersion=1.0'
|
query=$query'&SignatureVersion=1.0'
|
||||||
query=$query'&Timestamp='$(_timestamp)
|
query=$query'&Timestamp='$(_ali_timestamp)
|
||||||
query=$query'&TypeKeyWord=TXT'
|
query=$query'&TypeKeyWord=TXT'
|
||||||
query=$query'&Version=2015-01-09'
|
query=$query'&Version=2015-01-09'
|
||||||
}
|
}
|
||||||
|
|
||||||
_add_record_query() {
|
_add_record_query() {
|
||||||
|
endpoint=$Ali_DNS_API
|
||||||
query=''
|
query=''
|
||||||
query=$query'AccessKeyId='$Ali_Key
|
query=$query'AccessKeyId='$Ali_Key
|
||||||
query=$query'&Action=AddDomainRecord'
|
query=$query'&Action=AddDomainRecord'
|
||||||
@@ -147,13 +167,14 @@ _add_record_query() {
|
|||||||
query=$query'&SignatureMethod=HMAC-SHA1'
|
query=$query'&SignatureMethod=HMAC-SHA1'
|
||||||
query=$query"&SignatureNonce=$(_ali_nonce)"
|
query=$query"&SignatureNonce=$(_ali_nonce)"
|
||||||
query=$query'&SignatureVersion=1.0'
|
query=$query'&SignatureVersion=1.0'
|
||||||
query=$query'&Timestamp='$(_timestamp)
|
query=$query'&Timestamp='$(_ali_timestamp)
|
||||||
query=$query'&Type=TXT'
|
query=$query'&Type=TXT'
|
||||||
query=$query'&Value='$3
|
query=$query'&Value='$3
|
||||||
query=$query'&Version=2015-01-09'
|
query=$query'&Version=2015-01-09'
|
||||||
}
|
}
|
||||||
|
|
||||||
_delete_record_query() {
|
_delete_record_query() {
|
||||||
|
endpoint=$Ali_DNS_API
|
||||||
query=''
|
query=''
|
||||||
query=$query'AccessKeyId='$Ali_Key
|
query=$query'AccessKeyId='$Ali_Key
|
||||||
query=$query'&Action=DeleteDomainRecord'
|
query=$query'&Action=DeleteDomainRecord'
|
||||||
@@ -162,11 +183,12 @@ _delete_record_query() {
|
|||||||
query=$query'&SignatureMethod=HMAC-SHA1'
|
query=$query'&SignatureMethod=HMAC-SHA1'
|
||||||
query=$query"&SignatureNonce=$(_ali_nonce)"
|
query=$query"&SignatureNonce=$(_ali_nonce)"
|
||||||
query=$query'&SignatureVersion=1.0'
|
query=$query'&SignatureVersion=1.0'
|
||||||
query=$query'&Timestamp='$(_timestamp)
|
query=$query'&Timestamp='$(_ali_timestamp)
|
||||||
query=$query'&Version=2015-01-09'
|
query=$query'&Version=2015-01-09'
|
||||||
}
|
}
|
||||||
|
|
||||||
_describe_records_query() {
|
_describe_records_query() {
|
||||||
|
endpoint=$Ali_DNS_API
|
||||||
query=''
|
query=''
|
||||||
query=$query'AccessKeyId='$Ali_Key
|
query=$query'AccessKeyId='$Ali_Key
|
||||||
query=$query'&Action=DescribeDomainRecords'
|
query=$query'&Action=DescribeDomainRecords'
|
||||||
@@ -175,7 +197,7 @@ _describe_records_query() {
|
|||||||
query=$query'&SignatureMethod=HMAC-SHA1'
|
query=$query'&SignatureMethod=HMAC-SHA1'
|
||||||
query=$query"&SignatureNonce=$(_ali_nonce)"
|
query=$query"&SignatureNonce=$(_ali_nonce)"
|
||||||
query=$query'&SignatureVersion=1.0'
|
query=$query'&SignatureVersion=1.0'
|
||||||
query=$query'&Timestamp='$(_timestamp)
|
query=$query'&Timestamp='$(_ali_timestamp)
|
||||||
query=$query'&Version=2015-01-09'
|
query=$query'&Version=2015-01-09'
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,7 +219,3 @@ _clean() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_timestamp() {
|
|
||||||
date -u +"%Y-%m-%dT%H%%3A%M%%3A%SZ"
|
|
||||||
}
|
|
||||||
|
|||||||
185
dnsapi/dns_alviy.sh
Normal file
185
dnsapi/dns_alviy.sh
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
dns_alviy_info='Alviy.com
|
||||||
|
Site: Alviy.com
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_alviy
|
||||||
|
Options:
|
||||||
|
Alviy_token API token. Get it from the https://cloud.alviy.com/token
|
||||||
|
Issues: github.com/acmesh-official/acme.sh/issues/5115
|
||||||
|
'
|
||||||
|
|
||||||
|
Alviy_Api="https://cloud.alviy.com/api/v1"
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#Usage: dns_alviy_add _acme-challenge.www.domain.com "content"
|
||||||
|
dns_alviy_add() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
Alviy_token="${Alviy_token:-$(_readaccountconf_mutable Alviy_token)}"
|
||||||
|
if [ -z "$Alviy_token" ]; then
|
||||||
|
Alviy_token=""
|
||||||
|
_err "Please specify Alviy token."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
#save the api key and email to the account conf file.
|
||||||
|
_saveaccountconf_mutable Alviy_token "$Alviy_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"
|
||||||
|
|
||||||
|
_debug "Getting existing records"
|
||||||
|
if _alviy_txt_exists "$_domain" "$fulldomain" "$txtvalue"; then
|
||||||
|
_info "This record already exists, skipping"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
_add_data="{\"content\":\"$txtvalue\",\"type\":\"TXT\"}"
|
||||||
|
_debug2 _add_data "$_add_data"
|
||||||
|
_info "Adding record"
|
||||||
|
if _alviy_rest POST "zone/$_domain/domain/$fulldomain/" "$_add_data"; then
|
||||||
|
_debug "Checking updated records of '${fulldomain}'"
|
||||||
|
|
||||||
|
if ! _alviy_txt_exists "$_domain" "$fulldomain" "$txtvalue"; then
|
||||||
|
_err "TXT record '${txtvalue}' for '${fulldomain}', value wasn't set!"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
else
|
||||||
|
_err "Add txt record error, value '${txtvalue}' for '${fulldomain}' was not set."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_sleep 10
|
||||||
|
_info "Added TXT record '${txtvalue}' for '${fulldomain}'."
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#fulldomain
|
||||||
|
dns_alviy_rm() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
Alviy_token="${Alviy_token:-$(_readaccountconf_mutable Alviy_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"
|
||||||
|
|
||||||
|
if ! _alviy_txt_exists "$_domain" "$fulldomain" "$txtvalue"; then
|
||||||
|
_info "The record does not exist, skip"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
_add_data=""
|
||||||
|
uuid=$(echo "$response" | tr "{" "\n" | grep "$txtvalue" | tr "," "\n" | grep uuid | cut -d \" -f4)
|
||||||
|
# delete record
|
||||||
|
_debug "Delete TXT record for '${fulldomain}'"
|
||||||
|
if ! _alviy_rest DELETE "zone/$_domain/record/$uuid" "{\"confirm\":1}"; then
|
||||||
|
_err "Cannot delete empty TXT record for '$fulldomain'"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_info "The record '$fulldomain'='$txtvalue' deleted"
|
||||||
|
}
|
||||||
|
|
||||||
|
#################### Private functions below ##################################
|
||||||
|
#_acme-challenge.www.domain.com
|
||||||
|
#returns
|
||||||
|
# _sub_domain=_acme-challenge.www
|
||||||
|
# _domain=domain.com
|
||||||
|
_get_root() {
|
||||||
|
domain=$1
|
||||||
|
i=3
|
||||||
|
a="init"
|
||||||
|
while [ -n "$a" ]; do
|
||||||
|
a=$(printf "%s" "$domain" | cut -d . -f $i-)
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
n=$((i - 3))
|
||||||
|
h=$(printf "%s" "$domain" | cut -d . -f $n-)
|
||||||
|
if [ -z "$h" ]; then
|
||||||
|
#not valid
|
||||||
|
_alviy_rest GET "zone/$domain/"
|
||||||
|
_debug "can't get host from $domain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! _alviy_rest GET "zone/$h/"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if _contains "$response" '"code":"NOT_FOUND"'; then
|
||||||
|
_debug "$h not found"
|
||||||
|
else
|
||||||
|
s=$((n - 1))
|
||||||
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f -$s)
|
||||||
|
_domain="$h"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
_alviy_txt_exists() {
|
||||||
|
zone=$1
|
||||||
|
domain=$2
|
||||||
|
content_data=$3
|
||||||
|
_debug "Getting existing records"
|
||||||
|
|
||||||
|
if ! _alviy_rest GET "zone/$zone/domain/$domain/TXT/"; then
|
||||||
|
_info "The record does not exist"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! _contains "$response" "$3"; then
|
||||||
|
_info "The record has other value"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
# GOOD code return - TRUE function
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
_alviy_rest() {
|
||||||
|
method=$1
|
||||||
|
path="$2"
|
||||||
|
content_data="$3"
|
||||||
|
_debug "$path"
|
||||||
|
|
||||||
|
export _H1="Authorization: Bearer $Alviy_token"
|
||||||
|
export _H2="Content-Type: application/json"
|
||||||
|
|
||||||
|
if [ "$content_data" ] || [ "$method" = "DELETE" ]; then
|
||||||
|
_debug "data ($method): " "$content_data"
|
||||||
|
response="$(_post "$content_data" "$Alviy_Api/$path" "" "$method")"
|
||||||
|
else
|
||||||
|
response="$(_get "$Alviy_Api/$path")"
|
||||||
|
fi
|
||||||
|
_code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")"
|
||||||
|
if [ "$_code" = "401" ]; then
|
||||||
|
_err "It seems that your api key or secret is not correct."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$_code" != "200" ]; then
|
||||||
|
_err "API call error ($method): $path Response code $_code"
|
||||||
|
fi
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
_err "error on rest call ($method): $path. Response:"
|
||||||
|
_err "$response"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug2 response "$response"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
@@ -1,9 +1,12 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
# Anexia CloudDNS acme.sh hook
|
dns_anx_info='Anexia.com CloudDNS
|
||||||
# Author: MA
|
Site: Anexia.com
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_anx
|
||||||
#ANX_Token="xxxx"
|
Options:
|
||||||
|
ANX_Token API Token
|
||||||
|
Issues: github.com/acmesh-official/acme.sh/issues/3238
|
||||||
|
'
|
||||||
|
|
||||||
ANX_API='https://engine.anexia-it.com/api/clouddns/v1'
|
ANX_API='https://engine.anexia-it.com/api/clouddns/v1'
|
||||||
|
|
||||||
@@ -127,18 +130,17 @@ _get_root() {
|
|||||||
i=1
|
i=1
|
||||||
p=1
|
p=1
|
||||||
|
|
||||||
_anx_rest GET "zone.json"
|
|
||||||
|
|
||||||
while true; do
|
while true; do
|
||||||
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
|
||||||
_debug h "$h"
|
_debug h "$h"
|
||||||
if [ -z "$h" ]; then
|
if [ -z "$h" ]; then
|
||||||
#not valid
|
#not valid
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
_anx_rest GET "zone.json/${h}"
|
||||||
if _contains "$response" "\"name\":\"$h\""; then
|
if _contains "$response" "\"name\":\"$h\""; then
|
||||||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
|
||||||
_domain=$h
|
_domain=$h
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|||||||
177
dnsapi/dns_artfiles.sh
Normal file
177
dnsapi/dns_artfiles.sh
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
dns_artfiles_info='ArtFiles.de
|
||||||
|
Site: ArtFiles.de
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_artfiles
|
||||||
|
Options:
|
||||||
|
AF_API_USERNAME API Username
|
||||||
|
AF_API_PASSWORD API Password
|
||||||
|
Issues: github.com/acmesh-official/acme.sh/issues/4718
|
||||||
|
Author: Martin Arndt <https://troublezone.net/>
|
||||||
|
'
|
||||||
|
|
||||||
|
########## API configuration ###################################################
|
||||||
|
|
||||||
|
AF_API_SUCCESS='status":"OK'
|
||||||
|
AF_URL_DCP='https://dcp.c.artfiles.de/api/'
|
||||||
|
AF_URL_DNS=${AF_URL_DCP}'dns/{*}_dns.html?domain='
|
||||||
|
AF_URL_DOMAINS=${AF_URL_DCP}'domain/get_domains.html'
|
||||||
|
|
||||||
|
########## Public functions ####################################################
|
||||||
|
|
||||||
|
# Adds a new TXT record for given ACME challenge value & domain.
|
||||||
|
# Usage: dns_artfiles_add _acme-challenge.www.example.com "ACME challenge value"
|
||||||
|
dns_artfiles_add() {
|
||||||
|
domain="$1"
|
||||||
|
txtValue="$2"
|
||||||
|
_info 'Using ArtFiles.de DNS addition API…'
|
||||||
|
_debug 'Domain' "$domain"
|
||||||
|
_debug 'txtValue' "$txtValue"
|
||||||
|
|
||||||
|
_set_credentials
|
||||||
|
_saveaccountconf_mutable 'AF_API_USERNAME' "$AF_API_USERNAME"
|
||||||
|
_saveaccountconf_mutable 'AF_API_PASSWORD' "$AF_API_PASSWORD"
|
||||||
|
|
||||||
|
_set_headers
|
||||||
|
_get_zone "$domain"
|
||||||
|
_dns 'GET'
|
||||||
|
if ! _contains "$response" 'TXT'; then
|
||||||
|
_err 'Retrieving TXT records failed.'
|
||||||
|
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_clean_records
|
||||||
|
_dns 'SET' "$(printf -- '%s\n_acme-challenge "%s"' "$response" "$txtValue")"
|
||||||
|
if ! _contains "$response" "$AF_API_SUCCESS"; then
|
||||||
|
_err 'Adding ACME challenge value failed.'
|
||||||
|
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Removes the existing TXT record for given ACME challenge value & domain.
|
||||||
|
# Usage: dns_artfiles_rm _acme-challenge.www.example.com "ACME challenge value"
|
||||||
|
dns_artfiles_rm() {
|
||||||
|
domain="$1"
|
||||||
|
txtValue="$2"
|
||||||
|
_info 'Using ArtFiles.de DNS removal API…'
|
||||||
|
_debug 'Domain' "$domain"
|
||||||
|
_debug 'txtValue' "$txtValue"
|
||||||
|
|
||||||
|
_set_credentials
|
||||||
|
_set_headers
|
||||||
|
_get_zone "$domain"
|
||||||
|
if ! _dns 'GET'; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! _contains "$response" "$txtValue"; then
|
||||||
|
_err 'Retrieved TXT records are missing given ACME challenge value.'
|
||||||
|
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_clean_records
|
||||||
|
response="$(printf -- '%s' "$response" | sed '/_acme-challenge "'"$txtValue"'"/d')"
|
||||||
|
_dns 'SET' "$response"
|
||||||
|
if ! _contains "$response" "$AF_API_SUCCESS"; then
|
||||||
|
_err 'Removing ACME challenge value failed.'
|
||||||
|
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
########## Private functions ###################################################
|
||||||
|
|
||||||
|
# Cleans awful TXT records response of ArtFiles's API & pretty prints it.
|
||||||
|
# Usage: _clean_records
|
||||||
|
_clean_records() {
|
||||||
|
_info 'Cleaning TXT records…'
|
||||||
|
# Extract TXT part, strip trailing quote sign (ACME.sh API guidelines forbid
|
||||||
|
# usage of SED's GNU extensions, hence couldn't omit it via regex), strip '\'
|
||||||
|
# from '\"' & turn '\n' into real LF characters.
|
||||||
|
# Yup, awful API to use - but that's all we got to get this working, so… ;)
|
||||||
|
_debug2 'Raw ' "$response"
|
||||||
|
response="$(printf -- '%s' "$response" | sed 's/^.*TXT":"\([^}]*\).*$/\1/;s/,".*$//;s/.$//;s/\\"/"/g;s/\\n/\n/g')"
|
||||||
|
_debug2 'Clean' "$response"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Executes an HTTP GET or POST request for getting or setting DNS records,
|
||||||
|
# containing given payload upon POST.
|
||||||
|
# Usage: _dns [GET | SET] [payload]
|
||||||
|
_dns() {
|
||||||
|
_info 'Executing HTTP request…'
|
||||||
|
action="$1"
|
||||||
|
payload="$(printf -- '%s' "$2" | _url_encode)"
|
||||||
|
url="$(printf -- '%s%s' "$AF_URL_DNS" "$domain" | sed 's/{\*}/'"$(printf -- '%s' "$action" | _lower_case)"'/')"
|
||||||
|
|
||||||
|
if [ "$action" = 'SET' ]; then
|
||||||
|
_debug2 'Payload' "$payload"
|
||||||
|
response="$(_post '' "$url&TXT=$payload" '' 'POST' 'application/x-www-form-urlencoded')"
|
||||||
|
else
|
||||||
|
response="$(_get "$url" '' 10)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! _contains "$response" "$AF_API_SUCCESS"; then
|
||||||
|
_err "DNS API error: $response"
|
||||||
|
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug 'Response' "$response"
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Gets the root domain zone for given domain.
|
||||||
|
# Usage: _get_zone _acme-challenge.www.example.com
|
||||||
|
_get_zone() {
|
||||||
|
fqdn="$1"
|
||||||
|
domains="$(_get "$AF_URL_DOMAINS" '' 10)"
|
||||||
|
_info 'Getting domain zone…'
|
||||||
|
_debug2 'FQDN' "$fqdn"
|
||||||
|
_debug2 'Domains' "$domains"
|
||||||
|
|
||||||
|
while _contains "$fqdn" "."; do
|
||||||
|
if _contains "$domains" "$fqdn"; then
|
||||||
|
domain="$fqdn"
|
||||||
|
_info "Found root domain zone: $domain"
|
||||||
|
break
|
||||||
|
else
|
||||||
|
fqdn="${fqdn#*.}"
|
||||||
|
_debug2 'FQDN' "$fqdn"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "$domain" = "$fqdn" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
_err 'Couldn'\''t find root domain zone.'
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Sets the credentials for accessing ArtFiles's API
|
||||||
|
# Usage: _set_credentials
|
||||||
|
_set_credentials() {
|
||||||
|
_info 'Setting credentials…'
|
||||||
|
AF_API_USERNAME="${AF_API_USERNAME:-$(_readaccountconf_mutable AF_API_USERNAME)}"
|
||||||
|
AF_API_PASSWORD="${AF_API_PASSWORD:-$(_readaccountconf_mutable AF_API_PASSWORD)}"
|
||||||
|
if [ -z "$AF_API_USERNAME" ] || [ -z "$AF_API_PASSWORD" ]; then
|
||||||
|
_err 'Missing ArtFiles.de username and/or password.'
|
||||||
|
_err 'Please ensure both are set via export command & try again.'
|
||||||
|
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Adds the HTTP Authorization & Content-Type headers to a follow-up request.
|
||||||
|
# Usage: _set_headers
|
||||||
|
_set_headers() {
|
||||||
|
_info 'Setting headers…'
|
||||||
|
encoded="$(printf -- '%s:%s' "$AF_API_USERNAME" "$AF_API_PASSWORD" | _base64)"
|
||||||
|
export _H1="Authorization: Basic $encoded"
|
||||||
|
export _H2='Content-Type: application/json'
|
||||||
|
}
|
||||||
@@ -1,11 +1,16 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
# Arvan_Token="Apikey xxxx"
|
dns_arvan_info='ArvanCloud.ir
|
||||||
|
Site: ArvanCloud.ir
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_arvan
|
||||||
|
Options:
|
||||||
|
Arvan_Token API Token
|
||||||
|
Issues: github.com/acmesh-official/acme.sh/issues/2796
|
||||||
|
Author: Vahid Fardi
|
||||||
|
'
|
||||||
|
|
||||||
ARVAN_API_URL="https://napi.arvancloud.ir/cdn/4.0/domains"
|
ARVAN_API_URL="https://napi.arvancloud.ir/cdn/4.0/domains"
|
||||||
# Author: Vahid Fardi
|
|
||||||
# Report Bugs here: https://github.com/Neilpang/acme.sh
|
|
||||||
#
|
|
||||||
######## Public functions #####################
|
######## Public functions #####################
|
||||||
|
|
||||||
#Usage: dns_arvan_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
#Usage: dns_arvan_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
@@ -102,7 +107,7 @@ _get_root() {
|
|||||||
i=2
|
i=2
|
||||||
p=1
|
p=1
|
||||||
while true; do
|
while true; do
|
||||||
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
|
||||||
_debug h "$h"
|
_debug h "$h"
|
||||||
if [ -z "$h" ]; then
|
if [ -z "$h" ]; then
|
||||||
#not valid
|
#not valid
|
||||||
@@ -115,7 +120,7 @@ _get_root() {
|
|||||||
if _contains "$response" "\"domain\":\"$h\""; then
|
if _contains "$response" "\"domain\":\"$h\""; then
|
||||||
_domain_id=$(echo "$response" | cut -d : -f 3 | cut -d , -f 1 | tr -d \")
|
_domain_id=$(echo "$response" | cut -d : -f 3 | cut -d , -f 1 | tr -d \")
|
||||||
if [ "$_domain_id" ]; then
|
if [ "$_domain_id" ]; then
|
||||||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
|
||||||
_domain=$h
|
_domain=$h
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -1,9 +1,15 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
#
|
dns_aurora_info='versio.nl AuroraDNS
|
||||||
#AURORA_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
|
Domains: pcextreme.nl
|
||||||
#
|
Site: versio.nl
|
||||||
#AURORA_Secret="sdfsdfsdfljlbjkljlkjsdfoiwje"
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_aurora
|
||||||
|
Options:
|
||||||
|
AURORA_Key API Key
|
||||||
|
AURORA_Secret API Secret
|
||||||
|
Issues: github.com/acmesh-official/acme.sh/issues/3459
|
||||||
|
Author: Jasper Zonneveld
|
||||||
|
'
|
||||||
|
|
||||||
AURORA_Api="https://api.auroradns.eu"
|
AURORA_Api="https://api.auroradns.eu"
|
||||||
|
|
||||||
@@ -111,7 +117,7 @@ _get_root() {
|
|||||||
p=1
|
p=1
|
||||||
|
|
||||||
while true; do
|
while true; do
|
||||||
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
|
||||||
_debug h "$h"
|
_debug h "$h"
|
||||||
if [ -z "$h" ]; then
|
if [ -z "$h" ]; then
|
||||||
#not valid
|
#not valid
|
||||||
@@ -126,7 +132,7 @@ _get_root() {
|
|||||||
_domain_id=$(echo "$response" | _normalizeJson | tr -d "{}" | tr "," "\n" | grep "\"id\": *\"" | cut -d : -f 2 | tr -d \" | _head_n 1 | tr -d " ")
|
_domain_id=$(echo "$response" | _normalizeJson | tr -d "{}" | tr "," "\n" | grep "\"id\": *\"" | cut -d : -f 2 | tr -d \" | _head_n 1 | tr -d " ")
|
||||||
_debug _domain_id "$_domain_id"
|
_debug _domain_id "$_domain_id"
|
||||||
if [ "$_domain_id" ]; then
|
if [ "$_domain_id" ]; then
|
||||||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
|
||||||
_domain=$h
|
_domain=$h
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -1,16 +1,15 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
# -*- mode: sh; tab-width: 2; indent-tabs-mode: s; coding: utf-8 -*-
|
# shellcheck disable=SC2034
|
||||||
|
dns_autodns_info='InternetX autoDNS
|
||||||
# This is the InternetX autoDNS xml api wrapper for acme.sh
|
InternetX autoDNS XML API
|
||||||
# Author: auerswald@gmail.com
|
Site: InternetX.com/autodns/
|
||||||
# Created: 2018-01-14
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_autodns
|
||||||
#
|
Options:
|
||||||
# export AUTODNS_USER="username"
|
AUTODNS_USER Username
|
||||||
# export AUTODNS_PASSWORD="password"
|
AUTODNS_PASSWORD Password
|
||||||
# export AUTODNS_CONTEXT="context"
|
AUTODNS_CONTEXT Context
|
||||||
#
|
Author: <auerswald@gmail.com>
|
||||||
# Usage:
|
'
|
||||||
# acme.sh --issue --dns dns_autodns -d example.com
|
|
||||||
|
|
||||||
AUTODNS_API="https://gateway.autodns.com"
|
AUTODNS_API="https://gateway.autodns.com"
|
||||||
|
|
||||||
@@ -111,7 +110,7 @@ _get_autodns_zone() {
|
|||||||
p=1
|
p=1
|
||||||
|
|
||||||
while true; do
|
while true; do
|
||||||
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
|
||||||
_debug h "$h"
|
_debug h "$h"
|
||||||
|
|
||||||
if [ -z "$h" ]; then
|
if [ -z "$h" ]; then
|
||||||
@@ -129,7 +128,7 @@ _get_autodns_zone() {
|
|||||||
if _contains "$autodns_response" "<summary>1</summary>" >/dev/null; then
|
if _contains "$autodns_response" "<summary>1</summary>" >/dev/null; then
|
||||||
_zone="$(echo "$autodns_response" | _egrep_o '<name>[^<]*</name>' | cut -d '>' -f 2 | cut -d '<' -f 1)"
|
_zone="$(echo "$autodns_response" | _egrep_o '<name>[^<]*</name>' | cut -d '>' -f 2 | cut -d '<' -f 1)"
|
||||||
_system_ns="$(echo "$autodns_response" | _egrep_o '<system_ns>[^<]*</system_ns>' | cut -d '>' -f 2 | cut -d '<' -f 1)"
|
_system_ns="$(echo "$autodns_response" | _egrep_o '<system_ns>[^<]*</system_ns>' | cut -d '>' -f 2 | cut -d '<' -f 1)"
|
||||||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
dns_aws_info='Amazon AWS Route53 domain API
|
||||||
|
Site: docs.aws.amazon.com/route53/
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_aws
|
||||||
|
Options:
|
||||||
|
AWS_ACCESS_KEY_ID API Key ID
|
||||||
|
AWS_SECRET_ACCESS_KEY API Secret
|
||||||
|
'
|
||||||
|
|
||||||
#
|
# All `_sleep` commands are included to avoid Route53 throttling, see
|
||||||
#AWS_ACCESS_KEY_ID="sdfsdfsdfljlbjkljlkjsdfoiwje"
|
# https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/DNSLimitations.html#limits-api-requests
|
||||||
#
|
|
||||||
#AWS_SECRET_ACCESS_KEY="xxxxxxx"
|
|
||||||
|
|
||||||
#This is the Amazon Route53 api wrapper for acme.sh
|
|
||||||
#All `_sleep` commands are included to avoid Route53 throttling, see
|
|
||||||
#https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/DNSLimitations.html#limits-api-requests
|
|
||||||
|
|
||||||
AWS_HOST="route53.amazonaws.com"
|
AWS_HOST="route53.amazonaws.com"
|
||||||
AWS_URL="https://$AWS_HOST"
|
AWS_URL="https://$AWS_HOST"
|
||||||
@@ -145,7 +147,6 @@ dns_aws_rm() {
|
|||||||
fi
|
fi
|
||||||
_sleep 1
|
_sleep 1
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#################### Private functions below ##################################
|
#################### Private functions below ##################################
|
||||||
@@ -157,10 +158,10 @@ _get_root() {
|
|||||||
|
|
||||||
# iterate over names (a.b.c.d -> b.c.d -> c.d -> d)
|
# iterate over names (a.b.c.d -> b.c.d -> c.d -> d)
|
||||||
while true; do
|
while true; do
|
||||||
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100 | sed 's/\./\\./g')
|
||||||
_debug "Checking domain: $h"
|
_debug "Checking domain: $h"
|
||||||
if [ -z "$h" ]; then
|
if [ -z "$h" ]; then
|
||||||
_error "invalid domain"
|
_err "invalid domain"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -173,7 +174,7 @@ _get_root() {
|
|||||||
if [ "$hostedzone" ]; then
|
if [ "$hostedzone" ]; then
|
||||||
_domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o "<Id>.*<.Id>" | head -n 1 | _egrep_o ">.*<" | tr -d "<>")
|
_domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o "<Id>.*<.Id>" | head -n 1 | _egrep_o ">.*<" | tr -d "<>")
|
||||||
if [ "$_domain_id" ]; then
|
if [ "$_domain_id" ]; then
|
||||||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
|
||||||
_domain=$h
|
_domain=$h
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
@@ -207,24 +208,40 @@ _use_container_role() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_use_instance_role() {
|
_use_instance_role() {
|
||||||
_url="http://169.254.169.254/latest/meta-data/iam/security-credentials/"
|
_instance_role_name_url="http://169.254.169.254/latest/meta-data/iam/security-credentials/"
|
||||||
_debug "_url" "$_url"
|
|
||||||
if ! _get "$_url" true 1 | _head_n 1 | grep -Fq 200; then
|
if _get "$_instance_role_name_url" true 1 | _head_n 1 | grep -Fq 401; then
|
||||||
|
_debug "Using IMDSv2"
|
||||||
|
_token_url="http://169.254.169.254/latest/api/token"
|
||||||
|
export _H1="X-aws-ec2-metadata-token-ttl-seconds: 21600"
|
||||||
|
_token="$(_post "" "$_token_url" "" "PUT")"
|
||||||
|
_secure_debug3 "_token" "$_token"
|
||||||
|
if [ -z "$_token" ]; then
|
||||||
|
_debug "Unable to fetch IMDSv2 token from instance metadata"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
export _H1="X-aws-ec2-metadata-token: $_token"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! _get "$_instance_role_name_url" true 1 | _head_n 1 | grep -Fq 200; then
|
||||||
_debug "Unable to fetch IAM role from instance metadata"
|
_debug "Unable to fetch IAM role from instance metadata"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
_aws_role=$(_get "$_url" "" 1)
|
|
||||||
_debug "_aws_role" "$_aws_role"
|
_instance_role_name=$(_get "$_instance_role_name_url" "" 1)
|
||||||
_use_metadata "$_url$_aws_role"
|
_debug "_instance_role_name" "$_instance_role_name"
|
||||||
|
_use_metadata "$_instance_role_name_url$_instance_role_name" "$_token"
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_use_metadata() {
|
_use_metadata() {
|
||||||
|
export _H1="X-aws-ec2-metadata-token: $2"
|
||||||
_aws_creds="$(
|
_aws_creds="$(
|
||||||
_get "$1" "" 1 |
|
_get "$1" "" 1 |
|
||||||
_normalizeJson |
|
_normalizeJson |
|
||||||
tr '{,}' '\n' |
|
tr '{,}' '\n' |
|
||||||
while read -r _line; do
|
while read -r _line; do
|
||||||
_key="$(echo "${_line%%:*}" | tr -d '"')"
|
_key="$(echo "${_line%%:*}" | tr -d '\"')"
|
||||||
_value="${_line#*:}"
|
_value="${_line#*:}"
|
||||||
_debug3 "_key" "$_key"
|
_debug3 "_key" "$_key"
|
||||||
_secure_debug3 "_value" "$_value"
|
_secure_debug3 "_value" "$_value"
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
#
|
dns_azion_info='Azion.om
|
||||||
#AZION_Email=""
|
Site: Azion.com
|
||||||
#AZION_Password=""
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_azion
|
||||||
#
|
Options:
|
||||||
|
AZION_Email Email
|
||||||
|
AZION_Password Password
|
||||||
|
Issues: github.com/acmesh-official/acme.sh/issues/3555
|
||||||
|
'
|
||||||
|
|
||||||
AZION_Api="https://api.azionapi.net"
|
AZION_Api="https://api.azionapi.net"
|
||||||
|
|
||||||
@@ -96,7 +100,7 @@ _get_root() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
while true; do
|
while true; do
|
||||||
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
|
||||||
_debug h "$h"
|
_debug h "$h"
|
||||||
if [ -z "$h" ]; then
|
if [ -z "$h" ]; then
|
||||||
# not valid
|
# not valid
|
||||||
@@ -107,7 +111,7 @@ _get_root() {
|
|||||||
_domain_id=$(echo "$response" | tr '{' "\n" | grep "\"domain\":\"$h\"" | _egrep_o "\"id\":[0-9]*" | _head_n 1 | cut -d : -f 2 | tr -d \")
|
_domain_id=$(echo "$response" | tr '{' "\n" | grep "\"domain\":\"$h\"" | _egrep_o "\"id\":[0-9]*" | _head_n 1 | cut -d : -f 2 | tr -d \")
|
||||||
_debug _domain_id "$_domain_id"
|
_debug _domain_id "$_domain_id"
|
||||||
if [ "$_domain_id" ]; then
|
if [ "$_domain_id" ]; then
|
||||||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
|
||||||
_domain=$h
|
_domain=$h
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -1,13 +1,25 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
dns_azure_info='Azure
|
||||||
|
Site: Azure.microsoft.com
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_azure
|
||||||
|
Options:
|
||||||
|
AZUREDNS_SUBSCRIPTIONID Subscription ID
|
||||||
|
AZUREDNS_TENANTID Tenant ID
|
||||||
|
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 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"
|
wiki=https://github.com/acmesh-official/acme.sh/wiki/How-to-use-Azure-DNS
|
||||||
|
|
||||||
######## Public functions #####################
|
######## Public functions #####################
|
||||||
|
|
||||||
# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
# Used to add txt record
|
# Used to add txt record
|
||||||
#
|
#
|
||||||
# Ref: https://docs.microsoft.com/en-us/rest/api/dns/recordsets/createorupdate
|
# Ref: https://learn.microsoft.com/en-us/rest/api/dns/record-sets/create-or-update?view=rest-dns-2018-05-01&tabs=HTTP
|
||||||
#
|
#
|
||||||
|
|
||||||
dns_azure_add() {
|
dns_azure_add() {
|
||||||
@@ -20,6 +32,7 @@ dns_azure_add() {
|
|||||||
AZUREDNS_TENANTID=""
|
AZUREDNS_TENANTID=""
|
||||||
AZUREDNS_APPID=""
|
AZUREDNS_APPID=""
|
||||||
AZUREDNS_CLIENTSECRET=""
|
AZUREDNS_CLIENTSECRET=""
|
||||||
|
AZUREDNS_BEARERTOKEN=""
|
||||||
_err "You didn't specify the Azure Subscription ID"
|
_err "You didn't specify the Azure Subscription ID"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
@@ -34,37 +47,45 @@ dns_azure_add() {
|
|||||||
_saveaccountconf_mutable AZUREDNS_TENANTID ""
|
_saveaccountconf_mutable AZUREDNS_TENANTID ""
|
||||||
_saveaccountconf_mutable AZUREDNS_APPID ""
|
_saveaccountconf_mutable AZUREDNS_APPID ""
|
||||||
_saveaccountconf_mutable AZUREDNS_CLIENTSECRET ""
|
_saveaccountconf_mutable AZUREDNS_CLIENTSECRET ""
|
||||||
|
_saveaccountconf_mutable AZUREDNS_BEARERTOKEN ""
|
||||||
else
|
else
|
||||||
_info "You didn't ask to use Azure managed identity, checking service principal credentials"
|
_info "You didn't ask to use Azure managed identity, checking service principal credentials or provided bearer token"
|
||||||
AZUREDNS_TENANTID="${AZUREDNS_TENANTID:-$(_readaccountconf_mutable AZUREDNS_TENANTID)}"
|
AZUREDNS_TENANTID="${AZUREDNS_TENANTID:-$(_readaccountconf_mutable AZUREDNS_TENANTID)}"
|
||||||
AZUREDNS_APPID="${AZUREDNS_APPID:-$(_readaccountconf_mutable AZUREDNS_APPID)}"
|
AZUREDNS_APPID="${AZUREDNS_APPID:-$(_readaccountconf_mutable AZUREDNS_APPID)}"
|
||||||
AZUREDNS_CLIENTSECRET="${AZUREDNS_CLIENTSECRET:-$(_readaccountconf_mutable AZUREDNS_CLIENTSECRET)}"
|
AZUREDNS_CLIENTSECRET="${AZUREDNS_CLIENTSECRET:-$(_readaccountconf_mutable AZUREDNS_CLIENTSECRET)}"
|
||||||
|
AZUREDNS_BEARERTOKEN="${AZUREDNS_BEARERTOKEN:-$(_readaccountconf_mutable AZUREDNS_BEARERTOKEN)}"
|
||||||
|
if [ -z "$AZUREDNS_BEARERTOKEN" ]; then
|
||||||
|
if [ -z "$AZUREDNS_TENANTID" ]; then
|
||||||
|
AZUREDNS_SUBSCRIPTIONID=""
|
||||||
|
AZUREDNS_TENANTID=""
|
||||||
|
AZUREDNS_APPID=""
|
||||||
|
AZUREDNS_CLIENTSECRET=""
|
||||||
|
AZUREDNS_BEARERTOKEN=""
|
||||||
|
_err "You didn't specify the Azure Tenant ID "
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
if [ -z "$AZUREDNS_TENANTID" ]; then
|
if [ -z "$AZUREDNS_APPID" ]; then
|
||||||
AZUREDNS_SUBSCRIPTIONID=""
|
AZUREDNS_SUBSCRIPTIONID=""
|
||||||
AZUREDNS_TENANTID=""
|
AZUREDNS_TENANTID=""
|
||||||
AZUREDNS_APPID=""
|
AZUREDNS_APPID=""
|
||||||
AZUREDNS_CLIENTSECRET=""
|
AZUREDNS_CLIENTSECRET=""
|
||||||
_err "You didn't specify the Azure Tenant ID "
|
AZUREDNS_BEARERTOKEN=""
|
||||||
return 1
|
_err "You didn't specify the Azure App ID"
|
||||||
fi
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
if [ -z "$AZUREDNS_APPID" ]; then
|
if [ -z "$AZUREDNS_CLIENTSECRET" ]; then
|
||||||
AZUREDNS_SUBSCRIPTIONID=""
|
AZUREDNS_SUBSCRIPTIONID=""
|
||||||
AZUREDNS_TENANTID=""
|
AZUREDNS_TENANTID=""
|
||||||
AZUREDNS_APPID=""
|
AZUREDNS_APPID=""
|
||||||
AZUREDNS_CLIENTSECRET=""
|
AZUREDNS_CLIENTSECRET=""
|
||||||
_err "You didn't specify the Azure App ID"
|
AZUREDNS_BEARERTOKEN=""
|
||||||
return 1
|
_err "You didn't specify the Azure Client Secret"
|
||||||
fi
|
return 1
|
||||||
|
fi
|
||||||
if [ -z "$AZUREDNS_CLIENTSECRET" ]; then
|
else
|
||||||
AZUREDNS_SUBSCRIPTIONID=""
|
_info "Using provided bearer token"
|
||||||
AZUREDNS_TENANTID=""
|
|
||||||
AZUREDNS_APPID=""
|
|
||||||
AZUREDNS_CLIENTSECRET=""
|
|
||||||
_err "You didn't specify the Azure Client Secret"
|
|
||||||
return 1
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
#save account details to account conf file, don't opt in for azure manages identity check.
|
#save account details to account conf file, don't opt in for azure manages identity check.
|
||||||
@@ -72,9 +93,14 @@ dns_azure_add() {
|
|||||||
_saveaccountconf_mutable AZUREDNS_TENANTID "$AZUREDNS_TENANTID"
|
_saveaccountconf_mutable AZUREDNS_TENANTID "$AZUREDNS_TENANTID"
|
||||||
_saveaccountconf_mutable AZUREDNS_APPID "$AZUREDNS_APPID"
|
_saveaccountconf_mutable AZUREDNS_APPID "$AZUREDNS_APPID"
|
||||||
_saveaccountconf_mutable AZUREDNS_CLIENTSECRET "$AZUREDNS_CLIENTSECRET"
|
_saveaccountconf_mutable AZUREDNS_CLIENTSECRET "$AZUREDNS_CLIENTSECRET"
|
||||||
|
_saveaccountconf_mutable AZUREDNS_BEARERTOKEN "$AZUREDNS_BEARERTOKEN"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
accesstoken=$(_azure_getaccess_token "$AZUREDNS_MANAGEDIDENTITY" "$AZUREDNS_TENANTID" "$AZUREDNS_APPID" "$AZUREDNS_CLIENTSECRET")
|
if [ -z "$AZUREDNS_BEARERTOKEN" ]; then
|
||||||
|
accesstoken=$(_azure_getaccess_token "$AZUREDNS_MANAGEDIDENTITY" "$AZUREDNS_TENANTID" "$AZUREDNS_APPID" "$AZUREDNS_CLIENTSECRET")
|
||||||
|
else
|
||||||
|
accesstoken=$(echo "$AZUREDNS_BEARERTOKEN" | sed "s/Bearer //g")
|
||||||
|
fi
|
||||||
|
|
||||||
if ! _get_root "$fulldomain" "$AZUREDNS_SUBSCRIPTIONID" "$accesstoken"; then
|
if ! _get_root "$fulldomain" "$AZUREDNS_SUBSCRIPTIONID" "$accesstoken"; then
|
||||||
_err "invalid domain"
|
_err "invalid domain"
|
||||||
@@ -124,7 +150,7 @@ dns_azure_add() {
|
|||||||
# Usage: fulldomain txtvalue
|
# Usage: fulldomain txtvalue
|
||||||
# Used to remove the txt record after validation
|
# Used to remove the txt record after validation
|
||||||
#
|
#
|
||||||
# Ref: https://docs.microsoft.com/en-us/rest/api/dns/recordsets/delete
|
# Ref: https://learn.microsoft.com/en-us/rest/api/dns/record-sets/delete?view=rest-dns-2018-05-01&tabs=HTTP
|
||||||
#
|
#
|
||||||
dns_azure_rm() {
|
dns_azure_rm() {
|
||||||
fulldomain=$1
|
fulldomain=$1
|
||||||
@@ -136,6 +162,7 @@ dns_azure_rm() {
|
|||||||
AZUREDNS_TENANTID=""
|
AZUREDNS_TENANTID=""
|
||||||
AZUREDNS_APPID=""
|
AZUREDNS_APPID=""
|
||||||
AZUREDNS_CLIENTSECRET=""
|
AZUREDNS_CLIENTSECRET=""
|
||||||
|
AZUREDNS_BEARERTOKEN=""
|
||||||
_err "You didn't specify the Azure Subscription ID "
|
_err "You didn't specify the Azure Subscription ID "
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
@@ -144,40 +171,51 @@ dns_azure_rm() {
|
|||||||
if [ "$AZUREDNS_MANAGEDIDENTITY" = true ]; then
|
if [ "$AZUREDNS_MANAGEDIDENTITY" = true ]; then
|
||||||
_info "Using Azure managed identity"
|
_info "Using Azure managed identity"
|
||||||
else
|
else
|
||||||
_info "You didn't ask to use Azure managed identity, checking service principal credentials"
|
_info "You didn't ask to use Azure managed identity, checking service principal credentials or provided bearer token"
|
||||||
AZUREDNS_TENANTID="${AZUREDNS_TENANTID:-$(_readaccountconf_mutable AZUREDNS_TENANTID)}"
|
AZUREDNS_TENANTID="${AZUREDNS_TENANTID:-$(_readaccountconf_mutable AZUREDNS_TENANTID)}"
|
||||||
AZUREDNS_APPID="${AZUREDNS_APPID:-$(_readaccountconf_mutable AZUREDNS_APPID)}"
|
AZUREDNS_APPID="${AZUREDNS_APPID:-$(_readaccountconf_mutable AZUREDNS_APPID)}"
|
||||||
AZUREDNS_CLIENTSECRET="${AZUREDNS_CLIENTSECRET:-$(_readaccountconf_mutable AZUREDNS_CLIENTSECRET)}"
|
AZUREDNS_CLIENTSECRET="${AZUREDNS_CLIENTSECRET:-$(_readaccountconf_mutable AZUREDNS_CLIENTSECRET)}"
|
||||||
|
AZUREDNS_BEARERTOKEN="${AZUREDNS_BEARERTOKEN:-$(_readaccountconf_mutable AZUREDNS_BEARERTOKEN)}"
|
||||||
|
if [ -z "$AZUREDNS_BEARERTOKEN" ]; then
|
||||||
|
if [ -z "$AZUREDNS_TENANTID" ]; then
|
||||||
|
AZUREDNS_SUBSCRIPTIONID=""
|
||||||
|
AZUREDNS_TENANTID=""
|
||||||
|
AZUREDNS_APPID=""
|
||||||
|
AZUREDNS_CLIENTSECRET=""
|
||||||
|
AZUREDNS_BEARERTOKEN=""
|
||||||
|
_err "You didn't specify the Azure Tenant ID "
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
if [ -z "$AZUREDNS_TENANTID" ]; then
|
if [ -z "$AZUREDNS_APPID" ]; then
|
||||||
AZUREDNS_SUBSCRIPTIONID=""
|
AZUREDNS_SUBSCRIPTIONID=""
|
||||||
AZUREDNS_TENANTID=""
|
AZUREDNS_TENANTID=""
|
||||||
AZUREDNS_APPID=""
|
AZUREDNS_APPID=""
|
||||||
AZUREDNS_CLIENTSECRET=""
|
AZUREDNS_CLIENTSECRET=""
|
||||||
_err "You didn't specify the Azure Tenant ID "
|
AZUREDNS_BEARERTOKEN=""
|
||||||
return 1
|
_err "You didn't specify the Azure App ID"
|
||||||
fi
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
if [ -z "$AZUREDNS_APPID" ]; then
|
if [ -z "$AZUREDNS_CLIENTSECRET" ]; then
|
||||||
AZUREDNS_SUBSCRIPTIONID=""
|
AZUREDNS_SUBSCRIPTIONID=""
|
||||||
AZUREDNS_TENANTID=""
|
AZUREDNS_TENANTID=""
|
||||||
AZUREDNS_APPID=""
|
AZUREDNS_APPID=""
|
||||||
AZUREDNS_CLIENTSECRET=""
|
AZUREDNS_CLIENTSECRET=""
|
||||||
_err "You didn't specify the Azure App ID"
|
AZUREDNS_BEARERTOKEN=""
|
||||||
return 1
|
_err "You didn't specify the Azure Client Secret"
|
||||||
fi
|
return 1
|
||||||
|
fi
|
||||||
if [ -z "$AZUREDNS_CLIENTSECRET" ]; then
|
else
|
||||||
AZUREDNS_SUBSCRIPTIONID=""
|
_info "Using provided bearer token"
|
||||||
AZUREDNS_TENANTID=""
|
|
||||||
AZUREDNS_APPID=""
|
|
||||||
AZUREDNS_CLIENTSECRET=""
|
|
||||||
_err "You didn't specify the Azure Client Secret"
|
|
||||||
return 1
|
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
accesstoken=$(_azure_getaccess_token "$AZUREDNS_MANAGEDIDENTITY" "$AZUREDNS_TENANTID" "$AZUREDNS_APPID" "$AZUREDNS_CLIENTSECRET")
|
if [ -z "$AZUREDNS_BEARERTOKEN" ]; then
|
||||||
|
accesstoken=$(_azure_getaccess_token "$AZUREDNS_MANAGEDIDENTITY" "$AZUREDNS_TENANTID" "$AZUREDNS_APPID" "$AZUREDNS_CLIENTSECRET")
|
||||||
|
else
|
||||||
|
accesstoken=$(echo "$AZUREDNS_BEARERTOKEN" | sed "s/Bearer //g")
|
||||||
|
fi
|
||||||
|
|
||||||
if ! _get_root "$fulldomain" "$AZUREDNS_SUBSCRIPTIONID" "$accesstoken"; then
|
if ! _get_root "$fulldomain" "$AZUREDNS_SUBSCRIPTIONID" "$accesstoken"; then
|
||||||
_err "invalid domain"
|
_err "invalid domain"
|
||||||
@@ -256,10 +294,10 @@ _azure_rest() {
|
|||||||
if [ "$_code" = "401" ]; then
|
if [ "$_code" = "401" ]; then
|
||||||
# we have an invalid access token set to expired
|
# we have an invalid access token set to expired
|
||||||
_saveaccountconf_mutable AZUREDNS_TOKENVALIDTO "0"
|
_saveaccountconf_mutable AZUREDNS_TOKENVALIDTO "0"
|
||||||
_err "access denied make sure your Azure settings are correct. See $WIKI"
|
_err "Access denied. Invalid access token. Make sure your Azure settings are correct. See: $wiki"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
# See https://docs.microsoft.com/en-us/azure/architecture/best-practices/retry-service-specific#general-rest-and-retry-guidelines for retryable HTTP codes
|
# See https://learn.microsoft.com/en-us/azure/architecture/best-practices/retry-service-specific#general-rest-and-retry-guidelines for retryable HTTP codes
|
||||||
if [ "$_ret" != "0" ] || [ -z "$_code" ] || [ "$_code" = "408" ] || [ "$_code" = "500" ] || [ "$_code" = "503" ] || [ "$_code" = "504" ]; then
|
if [ "$_ret" != "0" ] || [ -z "$_code" ] || [ "$_code" = "408" ] || [ "$_code" = "500" ] || [ "$_code" = "503" ] || [ "$_code" = "504" ]; then
|
||||||
_request_retry_times="$(_math "$_request_retry_times" + 1)"
|
_request_retry_times="$(_math "$_request_retry_times" + 1)"
|
||||||
_info "REST call error $_code retrying $ep in $_request_retry_times s"
|
_info "REST call error $_code retrying $ep in $_request_retry_times s"
|
||||||
@@ -277,14 +315,14 @@ _azure_rest() {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
## Ref: https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-protocols-oauth-service-to-service#request-an-access-token
|
## Ref: https://learn.microsoft.com/en-us/entra/identity-platform/v2-oauth2-client-creds-grant-flow#request-an-access-token
|
||||||
_azure_getaccess_token() {
|
_azure_getaccess_token() {
|
||||||
managedIdentity=$1
|
managedIdentity=$1
|
||||||
tenantID=$2
|
tenantID=$2
|
||||||
clientID=$3
|
clientID=$3
|
||||||
clientSecret=$4
|
clientSecret=$4
|
||||||
|
|
||||||
accesstoken="${AZUREDNS_BEARERTOKEN:-$(_readaccountconf_mutable AZUREDNS_BEARERTOKEN)}"
|
accesstoken="${AZUREDNS_ACCESSTOKEN:-$(_readaccountconf_mutable AZUREDNS_ACCESSTOKEN)}"
|
||||||
expires_on="${AZUREDNS_TOKENVALIDTO:-$(_readaccountconf_mutable AZUREDNS_TOKENVALIDTO)}"
|
expires_on="${AZUREDNS_TOKENVALIDTO:-$(_readaccountconf_mutable AZUREDNS_TOKENVALIDTO)}"
|
||||||
|
|
||||||
# can we reuse the bearer token?
|
# can we reuse the bearer token?
|
||||||
@@ -301,9 +339,18 @@ _azure_getaccess_token() {
|
|||||||
_debug "getting new bearer token"
|
_debug "getting new bearer token"
|
||||||
|
|
||||||
if [ "$managedIdentity" = true ]; then
|
if [ "$managedIdentity" = true ]; then
|
||||||
# https://docs.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/how-to-use-vm-token#get-a-token-using-http
|
# 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"
|
if [ -n "$IDENTITY_ENDPOINT" ]; then
|
||||||
response="$(_get http://169.254.169.254/metadata/identity/oauth2/token\?api-version=2018-02-01\&resource=https://management.azure.com/)"
|
# 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)"
|
response="$(echo "$response" | _normalizeJson)"
|
||||||
accesstoken=$(echo "$response" | _egrep_o "\"access_token\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \")
|
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 \")
|
expires_on=$(echo "$response" | _egrep_o "\"expires_on\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \")
|
||||||
@@ -321,14 +368,14 @@ _azure_getaccess_token() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -z "$accesstoken" ]; then
|
if [ -z "$accesstoken" ]; then
|
||||||
_err "no acccess token received. Check your Azure settings see $WIKI"
|
_err "No acccess token received. Check your Azure settings. See: $wiki"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
if [ "$_ret" != "0" ]; then
|
if [ "$_ret" != "0" ]; then
|
||||||
_err "error $response"
|
_err "error $response"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
_saveaccountconf_mutable AZUREDNS_BEARERTOKEN "$accesstoken"
|
_saveaccountconf_mutable AZUREDNS_ACCESSTOKEN "$accesstoken"
|
||||||
_saveaccountconf_mutable AZUREDNS_TOKENVALIDTO "$expires_on"
|
_saveaccountconf_mutable AZUREDNS_TOKENVALIDTO "$expires_on"
|
||||||
printf "%s" "$accesstoken"
|
printf "%s" "$accesstoken"
|
||||||
return 0
|
return 0
|
||||||
@@ -341,15 +388,18 @@ _get_root() {
|
|||||||
i=1
|
i=1
|
||||||
p=1
|
p=1
|
||||||
|
|
||||||
## Ref: https://docs.microsoft.com/en-us/rest/api/dns/zones/list
|
## Ref: https://learn.microsoft.com/en-us/rest/api/dns/zones/list?view=rest-dns-2018-05-01&tabs=HTTP
|
||||||
## returns up to 100 zones in one response therefore handling more results is not not implemented
|
## returns up to 100 zones in one response. Handling more results is not implemented
|
||||||
## (ZoneListResult with continuation token for the next page of results)
|
## (ZoneListResult with continuation token for the next page of results)
|
||||||
## Per https://docs.microsoft.com/en-us/azure/azure-subscription-service-limits#dns-limits you are limited to 100 Zone/subscriptions anyways
|
##
|
||||||
|
## TODO: handle more than 100 results, as per:
|
||||||
|
## https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/azure-subscription-service-limits#azure-dns-limits
|
||||||
|
## The new limit is 250 Public DNS zones per subscription, while the old limit was only 100
|
||||||
##
|
##
|
||||||
_azure_rest GET "https://management.azure.com/subscriptions/$subscriptionId/providers/Microsoft.Network/dnszones?\$top=500&api-version=2017-09-01" "" "$accesstoken"
|
_azure_rest GET "https://management.azure.com/subscriptions/$subscriptionId/providers/Microsoft.Network/dnszones?\$top=500&api-version=2017-09-01" "" "$accesstoken"
|
||||||
# Find matching domain name in Json response
|
# Find matching domain name in Json response
|
||||||
while true; do
|
while true; do
|
||||||
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
|
||||||
_debug2 "Checking domain: $h"
|
_debug2 "Checking domain: $h"
|
||||||
if [ -z "$h" ]; then
|
if [ -z "$h" ]; then
|
||||||
#not valid
|
#not valid
|
||||||
@@ -364,7 +414,7 @@ _get_root() {
|
|||||||
#create the record at the domain apex (@) if only the domain name was provided as --domain-alias
|
#create the record at the domain apex (@) if only the domain name was provided as --domain-alias
|
||||||
_sub_domain="@"
|
_sub_domain="@"
|
||||||
else
|
else
|
||||||
_sub_domain=$(echo "$domain" | cut -d . -f 1-$p)
|
_sub_domain=$(echo "$domain" | cut -d . -f 1-"$p")
|
||||||
fi
|
fi
|
||||||
_domain=$h
|
_domain=$h
|
||||||
return 0
|
return 0
|
||||||
|
|||||||
281
dnsapi/dns_beget.sh
Executable file
281
dnsapi/dns_beget.sh
Executable 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":.*}}$'
|
||||||
|
}
|
||||||
88
dnsapi/dns_bookmyname.sh
Normal file
88
dnsapi/dns_bookmyname.sh
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
dns_bookmyname_info='BookMyName.com
|
||||||
|
Site: BookMyName.com
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_bookmyname
|
||||||
|
Options:
|
||||||
|
BOOKMYNAME_USERNAME Username
|
||||||
|
BOOKMYNAME_PASSWORD Password
|
||||||
|
Issues: github.com/acmesh-official/acme.sh/issues/3209
|
||||||
|
Author: @Neilpang
|
||||||
|
'
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
# BookMyName urls:
|
||||||
|
# https://BOOKMYNAME_USERNAME:BOOKMYNAME_PASSWORD@www.bookmyname.com/dyndns/?hostname=_acme-challenge.domain.tld&type=txt&ttl=300&do=add&value="XXXXXXXX"'
|
||||||
|
# https://BOOKMYNAME_USERNAME:BOOKMYNAME_PASSWORD@www.bookmyname.com/dyndns/?hostname=_acme-challenge.domain.tld&type=txt&ttl=300&do=remove&value="XXXXXXXX"'
|
||||||
|
|
||||||
|
# Output:
|
||||||
|
#good: update done, cid 123456, domain id 456789, type txt, ip XXXXXXXX
|
||||||
|
#good: remove done 1, cid 123456, domain id 456789, ttl 300, type txt, ip XXXXXXXX
|
||||||
|
|
||||||
|
# Be careful, BMN DNS servers can be slow to pick up changes; using dnssleep is thus advised.
|
||||||
|
|
||||||
|
# Usage:
|
||||||
|
# export BOOKMYNAME_USERNAME="ABCDE-FREE"
|
||||||
|
# export BOOKMYNAME_PASSWORD="MyPassword"
|
||||||
|
# /usr/local/ssl/acme.sh/acme.sh --dns dns_bookmyname --dnssleep 600 --issue -d domain.tld
|
||||||
|
|
||||||
|
#Usage: dns_bookmyname_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
|
dns_bookmyname_add() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
_info "Using bookmyname"
|
||||||
|
_debug fulldomain "$fulldomain"
|
||||||
|
_debug txtvalue "$txtvalue"
|
||||||
|
|
||||||
|
BOOKMYNAME_USERNAME="${BOOKMYNAME_USERNAME:-$(_readaccountconf_mutable BOOKMYNAME_USERNAME)}"
|
||||||
|
BOOKMYNAME_PASSWORD="${BOOKMYNAME_PASSWORD:-$(_readaccountconf_mutable BOOKMYNAME_PASSWORD)}"
|
||||||
|
|
||||||
|
if [ -z "$BOOKMYNAME_USERNAME" ] || [ -z "$BOOKMYNAME_PASSWORD" ]; then
|
||||||
|
BOOKMYNAME_USERNAME=""
|
||||||
|
BOOKMYNAME_PASSWORD=""
|
||||||
|
_err "You didn't specify BookMyName username and password yet."
|
||||||
|
_err "Please specify them and try again."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
#save the credentials to the account conf file.
|
||||||
|
_saveaccountconf_mutable BOOKMYNAME_USERNAME "$BOOKMYNAME_USERNAME"
|
||||||
|
_saveaccountconf_mutable BOOKMYNAME_PASSWORD "$BOOKMYNAME_PASSWORD"
|
||||||
|
|
||||||
|
uri="https://${BOOKMYNAME_USERNAME}:${BOOKMYNAME_PASSWORD}@www.bookmyname.com/dyndns/"
|
||||||
|
data="?hostname=${fulldomain}&type=TXT&ttl=300&do=add&value=${txtvalue}"
|
||||||
|
result="$(_get "${uri}${data}")"
|
||||||
|
_debug "Result: $result"
|
||||||
|
|
||||||
|
if ! _startswith "$result" 'good: update done, cid '; then
|
||||||
|
_err "Can't add $fulldomain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#Usage: fulldomain txtvalue
|
||||||
|
#Remove the txt record after validation.
|
||||||
|
dns_bookmyname_rm() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
_info "Using bookmyname"
|
||||||
|
_debug fulldomain "$fulldomain"
|
||||||
|
_debug txtvalue "$txtvalue"
|
||||||
|
|
||||||
|
BOOKMYNAME_USERNAME="${BOOKMYNAME_USERNAME:-$(_readaccountconf_mutable BOOKMYNAME_USERNAME)}"
|
||||||
|
BOOKMYNAME_PASSWORD="${BOOKMYNAME_PASSWORD:-$(_readaccountconf_mutable BOOKMYNAME_PASSWORD)}"
|
||||||
|
|
||||||
|
uri="https://${BOOKMYNAME_USERNAME}:${BOOKMYNAME_PASSWORD}@www.bookmyname.com/dyndns/"
|
||||||
|
data="?hostname=${fulldomain}&type=TXT&ttl=300&do=remove&value=${txtvalue}"
|
||||||
|
result="$(_get "${uri}${data}")"
|
||||||
|
_debug "Result: $result"
|
||||||
|
|
||||||
|
if ! _startswith "$result" 'good: remove done 1, cid '; then
|
||||||
|
_info "Can't remove $fulldomain"
|
||||||
|
fi
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#################### Private functions below ##################################
|
||||||
@@ -1,16 +1,13 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
## Will be called by acme.sh to add the TXT record via the Bunny DNS API.
|
dns_bunny_info='Bunny.net
|
||||||
## returns 0 means success, otherwise error.
|
Site: Bunny.net/dns/
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_bunny
|
||||||
## Author: nosilver4u <nosilver4u at ewww.io>
|
Options:
|
||||||
## GitHub: https://github.com/nosilver4u/acme.sh
|
BUNNY_API_KEY API Key
|
||||||
|
Issues: github.com/acmesh-official/acme.sh/issues/4296
|
||||||
##
|
Author: <nosilver4u@ewww.io>
|
||||||
## Environment Variables Required:
|
'
|
||||||
##
|
|
||||||
## BUNNY_API_KEY="75310dc4-ca77-9ac3-9a19-f6355db573b49ce92ae1-2655-3ebd-61ac-3a3ae34834cc"
|
|
||||||
##
|
|
||||||
|
|
||||||
##################### Public functions #####################
|
##################### Public functions #####################
|
||||||
|
|
||||||
@@ -199,7 +196,7 @@ _get_base_domain() {
|
|||||||
_debug2 domain_list "$domain_list"
|
_debug2 domain_list "$domain_list"
|
||||||
|
|
||||||
i=1
|
i=1
|
||||||
while [ $i -gt 0 ]; do
|
while [ "$i" -gt 0 ]; do
|
||||||
## get next longest domain
|
## get next longest domain
|
||||||
_domain=$(printf "%s" "$fulldomain" | cut -d . -f "$i"-"$MAX_DOM")
|
_domain=$(printf "%s" "$fulldomain" | cut -d . -f "$i"-"$MAX_DOM")
|
||||||
## check we got something back from our cut (or are we at the end)
|
## check we got something back from our cut (or are we at the end)
|
||||||
@@ -211,7 +208,7 @@ _get_base_domain() {
|
|||||||
## check if it exists
|
## check if it exists
|
||||||
if [ -n "$found" ]; then
|
if [ -n "$found" ]; then
|
||||||
## exists - exit loop returning the parts
|
## exists - exit loop returning the parts
|
||||||
sub_point=$(_math $i - 1)
|
sub_point=$(_math "$i" - 1)
|
||||||
_sub_domain=$(printf "%s" "$fulldomain" | cut -d . -f 1-"$sub_point")
|
_sub_domain=$(printf "%s" "$fulldomain" | cut -d . -f 1-"$sub_point")
|
||||||
_domain_id="$(echo "$found" | _egrep_o "Id\"\s*\:\s*\"*[0-9]+" | _egrep_o "[0-9]+")"
|
_domain_id="$(echo "$found" | _egrep_o "Id\"\s*\:\s*\"*[0-9]+" | _egrep_o "[0-9]+")"
|
||||||
_debug _domain_id "$_domain_id"
|
_debug _domain_id "$_domain_id"
|
||||||
@@ -221,11 +218,11 @@ _get_base_domain() {
|
|||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
## increment cut point $i
|
## increment cut point $i
|
||||||
i=$(_math $i + 1)
|
i=$(_math "$i" + 1)
|
||||||
done
|
done
|
||||||
|
|
||||||
if [ -z "$found" ]; then
|
if [ -z "$found" ]; then
|
||||||
page=$(_math $page + 1)
|
page=$(_math "$page" + 1)
|
||||||
nextpage="https://api.bunny.net/dnszone?page=$page"
|
nextpage="https://api.bunny.net/dnszone?page=$page"
|
||||||
## Find the next page if we don't have a match.
|
## Find the next page if we don't have a match.
|
||||||
hasnextpage="$(echo "$domain_list" | _egrep_o "\"HasMoreItems\"\s*:\s*true")"
|
hasnextpage="$(echo "$domain_list" | _egrep_o "\"HasMoreItems\"\s*:\s*true")"
|
||||||
|
|||||||
@@ -1,13 +1,16 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
#
|
dns_cf_info='CloudFlare
|
||||||
#CF_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
|
Site: CloudFlare.com
|
||||||
#
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_cf
|
||||||
#CF_Email="xxxx@sss.com"
|
Options:
|
||||||
|
CF_Key API Key
|
||||||
#CF_Token="xxxx"
|
CF_Email Your account email
|
||||||
#CF_Account_ID="xxxx"
|
OptionsAlt:
|
||||||
#CF_Zone_ID="xxxx"
|
CF_Token API Token
|
||||||
|
CF_Account_ID Account ID
|
||||||
|
CF_Zone_ID Zone ID. Optional.
|
||||||
|
'
|
||||||
|
|
||||||
CF_Api="https://api.cloudflare.com/client/v4"
|
CF_Api="https://api.cloudflare.com/client/v4"
|
||||||
|
|
||||||
@@ -89,7 +92,9 @@ dns_cf_add() {
|
|||||||
if _contains "$response" "$txtvalue"; then
|
if _contains "$response" "$txtvalue"; then
|
||||||
_info "Added, OK"
|
_info "Added, OK"
|
||||||
return 0
|
return 0
|
||||||
elif _contains "$response" "The record already exists"; then
|
elif _contains "$response" "The record already exists" ||
|
||||||
|
_contains "$response" "An identical record already exists." ||
|
||||||
|
_contains "$response" '"code":81058'; then
|
||||||
_info "Already exists, OK"
|
_info "Already exists, OK"
|
||||||
return 0
|
return 0
|
||||||
else
|
else
|
||||||
@@ -183,7 +188,7 @@ _get_root() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
while true; do
|
while true; do
|
||||||
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
|
||||||
_debug h "$h"
|
_debug h "$h"
|
||||||
if [ -z "$h" ]; then
|
if [ -z "$h" ]; then
|
||||||
#not valid
|
#not valid
|
||||||
@@ -203,7 +208,7 @@ _get_root() {
|
|||||||
if _contains "$response" "\"name\":\"$h\"" || _contains "$response" '"total_count":1'; then
|
if _contains "$response" "\"name\":\"$h\"" || _contains "$response" '"total_count":1'; then
|
||||||
_domain_id=$(echo "$response" | _egrep_o "\[.\"id\": *\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \" | tr -d " ")
|
_domain_id=$(echo "$response" | _egrep_o "\[.\"id\": *\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \" | tr -d " ")
|
||||||
if [ "$_domain_id" ]; then
|
if [ "$_domain_id" ]; then
|
||||||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
|
||||||
_domain=$h
|
_domain=$h
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -1,10 +1,15 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
# Author: Radek Sprta <sprta@vshosting.cz>
|
dns_clouddns_info='vshosting.cz CloudDNS
|
||||||
|
Site: github.com/vshosting/clouddns
|
||||||
#CLOUDDNS_EMAIL=XXXXX
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_clouddns
|
||||||
#CLOUDDNS_PASSWORD="YYYYYYYYY"
|
Options:
|
||||||
#CLOUDDNS_CLIENT_ID=XXXXX
|
CLOUDDNS_EMAIL Email
|
||||||
|
CLOUDDNS_PASSWORD Password
|
||||||
|
CLOUDDNS_CLIENT_ID Client ID
|
||||||
|
Issues: github.com/acmesh-official/acme.sh/issues/2699
|
||||||
|
Author: Radek Sprta <sprta@vshosting.cz>
|
||||||
|
'
|
||||||
|
|
||||||
CLOUDDNS_API='https://admin.vshosting.cloud/clouddns'
|
CLOUDDNS_API='https://admin.vshosting.cloud/clouddns'
|
||||||
CLOUDDNS_LOGIN_API='https://admin.vshosting.cloud/api/public/auth/login'
|
CLOUDDNS_LOGIN_API='https://admin.vshosting.cloud/api/public/auth/login'
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
dns_cloudns_info='ClouDNS.net
|
||||||
|
Site: ClouDNS.net
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_cloudns
|
||||||
|
Options:
|
||||||
|
CLOUDNS_AUTH_ID Regular auth ID
|
||||||
|
CLOUDNS_SUB_AUTH_ID Sub auth ID
|
||||||
|
CLOUDNS_AUTH_PASSWORD Auth Password
|
||||||
|
Author: Boyan Peychev <boyan@cloudns.net>
|
||||||
|
'
|
||||||
|
|
||||||
# Author: Boyan Peychev <boyan at cloudns dot net>
|
|
||||||
# Repository: https://github.com/ClouDNS/acme.sh/
|
|
||||||
# Editor: I Komang Suryadana
|
|
||||||
|
|
||||||
#CLOUDNS_AUTH_ID=XXXXX
|
|
||||||
#CLOUDNS_SUB_AUTH_ID=XXXXX
|
|
||||||
#CLOUDNS_AUTH_PASSWORD="YYYYYYYYY"
|
|
||||||
CLOUDNS_API="https://api.cloudns.net"
|
CLOUDNS_API="https://api.cloudns.net"
|
||||||
DOMAIN_TYPE=
|
DOMAIN_TYPE=
|
||||||
DOMAIN_MASTER=
|
DOMAIN_MASTER=
|
||||||
@@ -161,7 +164,7 @@ _dns_cloudns_get_zone_info() {
|
|||||||
_dns_cloudns_get_zone_name() {
|
_dns_cloudns_get_zone_name() {
|
||||||
i=2
|
i=2
|
||||||
while true; do
|
while true; do
|
||||||
zoneForCheck=$(printf "%s" "$1" | cut -d . -f $i-100)
|
zoneForCheck=$(printf "%s" "$1" | cut -d . -f "$i"-100)
|
||||||
|
|
||||||
if [ -z "$zoneForCheck" ]; then
|
if [ -z "$zoneForCheck" ]; then
|
||||||
return 1
|
return 1
|
||||||
@@ -194,10 +197,11 @@ _dns_cloudns_http_api_call() {
|
|||||||
auth_user="auth-id=$CLOUDNS_AUTH_ID"
|
auth_user="auth-id=$CLOUDNS_AUTH_ID"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
encoded_password=$(echo "$CLOUDNS_AUTH_PASSWORD" | tr -d "\n\r" | _url_encode)
|
||||||
if [ -z "$2" ]; then
|
if [ -z "$2" ]; then
|
||||||
data="$auth_user&auth-password=$CLOUDNS_AUTH_PASSWORD"
|
data="$auth_user&auth-password=$encoded_password"
|
||||||
else
|
else
|
||||||
data="$auth_user&auth-password=$CLOUDNS_AUTH_PASSWORD&$2"
|
data="$auth_user&auth-password=$encoded_password&$2"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
response="$(_get "$CLOUDNS_API/$method?$data")"
|
response="$(_get "$CLOUDNS_API/$method?$data")"
|
||||||
|
|||||||
@@ -1,7 +1,14 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
# DNS API for acme.sh for Core-Networks (https://beta.api.core-networks.de/doc/).
|
dns_cn_info='Core-Networks.de
|
||||||
# created by 5ll and francis
|
Site: beta.api.Core-Networks.de/doc/
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_cn
|
||||||
|
Options:
|
||||||
|
CN_User User
|
||||||
|
CN_Password Password
|
||||||
|
Issues: github.com/acmesh-official/acme.sh/issues/2142
|
||||||
|
Author: 5ll, francis
|
||||||
|
'
|
||||||
|
|
||||||
CN_API="https://beta.api.core-networks.de"
|
CN_API="https://beta.api.core-networks.de"
|
||||||
|
|
||||||
@@ -124,7 +131,7 @@ _cn_get_root() {
|
|||||||
p=1
|
p=1
|
||||||
while true; do
|
while true; do
|
||||||
|
|
||||||
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
|
||||||
_debug h "$h"
|
_debug h "$h"
|
||||||
_debug _H1 "${_H1}"
|
_debug _H1 "${_H1}"
|
||||||
|
|
||||||
@@ -142,7 +149,7 @@ _cn_get_root() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if _contains "$_cn_zonelist" "\"name\":\"$h\"" >/dev/null; then
|
if _contains "$_cn_zonelist" "\"name\":\"$h\"" >/dev/null; then
|
||||||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
|
||||||
_domain=$h
|
_domain=$h
|
||||||
return 0
|
return 0
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -1,4 +1,15 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
dns_conoha_info='ConoHa.jp
|
||||||
|
Domains: ConoHa.io
|
||||||
|
Site: ConoHa.jp
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_conoha
|
||||||
|
Options:
|
||||||
|
CONOHA_Username Username
|
||||||
|
CONOHA_Password Password
|
||||||
|
CONOHA_TenantId TenantId
|
||||||
|
CONOHA_IdentityServiceApi Identity Service API. E.g. "https://identity.xxxx.conoha.io/v2.0"
|
||||||
|
'
|
||||||
|
|
||||||
CONOHA_DNS_EP_PREFIX_REGEXP="https://dns-service\."
|
CONOHA_DNS_EP_PREFIX_REGEXP="https://dns-service\."
|
||||||
|
|
||||||
@@ -226,7 +237,7 @@ _get_root() {
|
|||||||
i=2
|
i=2
|
||||||
p=1
|
p=1
|
||||||
while true; do
|
while true; do
|
||||||
h=$(printf "%s" "$domain" | cut -d . -f $i-100).
|
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100).
|
||||||
_debug h "$h"
|
_debug h "$h"
|
||||||
if [ -z "$h" ]; then
|
if [ -z "$h" ]; then
|
||||||
#not valid
|
#not valid
|
||||||
@@ -240,7 +251,7 @@ _get_root() {
|
|||||||
if _contains "$response" "\"name\":\"$h\"" >/dev/null; then
|
if _contains "$response" "\"name\":\"$h\"" >/dev/null; then
|
||||||
_domain_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | head -n 1 | cut -d : -f 2 | tr -d \")
|
_domain_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | head -n 1 | cut -d : -f 2 | tr -d \")
|
||||||
if [ "$_domain_id" ]; then
|
if [ "$_domain_id" ]; then
|
||||||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
|
||||||
_domain=$h
|
_domain=$h
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -1,10 +1,16 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
# Author: Wout Decre <wout@canodus.be>
|
dns_constellix_info='Constellix.com
|
||||||
|
Site: Constellix.com
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_constellix
|
||||||
|
Options:
|
||||||
|
CONSTELLIX_Key API Key
|
||||||
|
CONSTELLIX_Secret API Secret
|
||||||
|
Issues: github.com/acmesh-official/acme.sh/issues/2724
|
||||||
|
Author: Wout Decre <wout@canodus.be>
|
||||||
|
'
|
||||||
|
|
||||||
CONSTELLIX_Api="https://api.dns.constellix.com/v1"
|
CONSTELLIX_Api="https://api.dns.constellix.com/v1"
|
||||||
#CONSTELLIX_Key="XXX"
|
|
||||||
#CONSTELLIX_Secret="XXX"
|
|
||||||
|
|
||||||
######## Public functions #####################
|
######## Public functions #####################
|
||||||
|
|
||||||
@@ -111,12 +117,12 @@ dns_constellix_rm() {
|
|||||||
#################### Private functions below ##################################
|
#################### Private functions below ##################################
|
||||||
|
|
||||||
_get_root() {
|
_get_root() {
|
||||||
domain=$1
|
domain=$(echo "$1" | _lower_case)
|
||||||
i=2
|
i=2
|
||||||
p=1
|
p=1
|
||||||
_debug "Detecting root zone"
|
_debug "Detecting root zone"
|
||||||
while true; do
|
while true; do
|
||||||
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
|
||||||
if [ -z "$h" ]; then
|
if [ -z "$h" ]; then
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
@@ -128,7 +134,7 @@ _get_root() {
|
|||||||
if _contains "$response" "\"name\":\"$h\""; then
|
if _contains "$response" "\"name\":\"$h\""; then
|
||||||
_domain_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":[0-9]*" | cut -d ':' -f 2)
|
_domain_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":[0-9]*" | cut -d ':' -f 2)
|
||||||
if [ "$_domain_id" ]; then
|
if [ "$_domain_id" ]; then
|
||||||
_sub_domain=$(printf "%s" "$domain" | cut -d '.' -f 1-$p)
|
_sub_domain=$(printf "%s" "$domain" | cut -d '.' -f 1-"$p")
|
||||||
_domain="$h"
|
_domain="$h"
|
||||||
|
|
||||||
_debug _domain_id "$_domain_id"
|
_debug _domain_id "$_domain_id"
|
||||||
@@ -150,6 +156,9 @@ _constellix_rest() {
|
|||||||
data="$3"
|
data="$3"
|
||||||
_debug "$ep"
|
_debug "$ep"
|
||||||
|
|
||||||
|
# Prevent rate limit
|
||||||
|
_sleep 2
|
||||||
|
|
||||||
rdate=$(date +"%s")"000"
|
rdate=$(date +"%s")"000"
|
||||||
hmac=$(printf "%s" "$rdate" | _hmac sha1 "$(printf "%s" "$CONSTELLIX_Secret" | _hex_dump | tr -d ' ')" | _base64)
|
hmac=$(printf "%s" "$rdate" | _hmac sha1 "$(printf "%s" "$CONSTELLIX_Secret" | _hex_dump | tr -d ' ')" | _base64)
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
#
|
# shellcheck disable=SC2034
|
||||||
#Author: Bjarne Saltbaek
|
dns_cpanel_info='cPanel Server API
|
||||||
#Report Bugs here: https://github.com/acmesh-official/acme.sh/issues/3732
|
Manage DNS via cPanel Dashboard.
|
||||||
#
|
Site: cPanel.net
|
||||||
#
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_cpanel
|
||||||
|
Options:
|
||||||
|
cPanel_Username Username
|
||||||
|
cPanel_Apitoken API Token
|
||||||
|
cPanel_Hostname Server URL. E.g. "https://hostname:port"
|
||||||
|
Issues: github.com/acmesh-official/acme.sh/issues/3732
|
||||||
|
Author: Bjarne Saltbaek
|
||||||
|
'
|
||||||
|
|
||||||
######## Public functions #####################
|
######## Public functions #####################
|
||||||
#
|
|
||||||
# Export CPANEL username,api token and hostname in the following variables
|
|
||||||
#
|
|
||||||
# cPanel_Username=username
|
|
||||||
# cPanel_Apitoken=apitoken
|
|
||||||
# cPanel_Hostname=hostname
|
|
||||||
#
|
|
||||||
# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
|
||||||
|
|
||||||
# Used to add txt record
|
# Used to add txt record
|
||||||
dns_cpanel_add() {
|
dns_cpanel_add() {
|
||||||
|
|||||||
@@ -1,15 +1,21 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
#Script to use with curanet.dk, scannet.dk, wannafind.dk, dandomain.dk DNS management.
|
dns_curanet_info='Curanet.dk
|
||||||
#Requires api credentials with scope: dns
|
Domains: scannet.dk wannafind.dk dandomain.dk
|
||||||
#Author: Peter L. Hansen <peter@r12.dk>
|
Site: Curanet.dk
|
||||||
#Version 1.0
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_curanet
|
||||||
|
Options:
|
||||||
|
CURANET_AUTHCLIENTID Auth ClientID. Requires scope dns
|
||||||
|
CURANET_AUTHSECRET Auth Secret
|
||||||
|
Issues: github.com/acmesh-official/acme.sh/issues/3933
|
||||||
|
Author: Peter L. Hansen <peter@r12.dk>
|
||||||
|
'
|
||||||
|
|
||||||
CURANET_REST_URL="https://api.curanet.dk/dns/v1/Domains"
|
CURANET_REST_URL="https://api.curanet.dk/dns/v1/Domains"
|
||||||
CURANET_AUTH_URL="https://apiauth.dk.team.blue/auth/realms/Curanet/protocol/openid-connect/token"
|
CURANET_AUTH_URL="https://apiauth.dk.team.blue/auth/realms/Curanet/protocol/openid-connect/token"
|
||||||
CURANET_ACCESS_TOKEN=""
|
CURANET_ACCESS_TOKEN=""
|
||||||
|
|
||||||
######## Public functions #####################
|
######## Public functions ####################
|
||||||
|
|
||||||
#Usage: dns_curanet_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
#Usage: dns_curanet_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
dns_curanet_add() {
|
dns_curanet_add() {
|
||||||
@@ -136,7 +142,7 @@ _get_root() {
|
|||||||
i=1
|
i=1
|
||||||
|
|
||||||
while true; do
|
while true; do
|
||||||
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
|
||||||
_debug h "$h"
|
_debug h "$h"
|
||||||
if [ -z "$h" ]; then
|
if [ -z "$h" ]; then
|
||||||
#not valid
|
#not valid
|
||||||
@@ -148,7 +154,7 @@ _get_root() {
|
|||||||
export _H3="Authorization: Bearer $CURANET_ACCESS_TOKEN"
|
export _H3="Authorization: Bearer $CURANET_ACCESS_TOKEN"
|
||||||
response="$(_get "$CURANET_REST_URL/$h/Records" "" "")"
|
response="$(_get "$CURANET_REST_URL/$h/Records" "" "")"
|
||||||
|
|
||||||
if [ ! "$(echo "$response" | _egrep_o "Entity not found")" ]; then
|
if [ ! "$(echo "$response" | _egrep_o "Entity not found|Bad Request")" ]; then
|
||||||
_domain=$h
|
_domain=$h
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -1,21 +1,15 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
########
|
dns_cyon_info='cyon.ch
|
||||||
# Custom cyon.ch DNS API for use with [acme.sh](https://github.com/acmesh-official/acme.sh)
|
Site: cyon.ch
|
||||||
#
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_cyon
|
||||||
# Usage: acme.sh --issue --dns dns_cyon -d www.domain.com
|
Options:
|
||||||
#
|
CY_Username Username
|
||||||
# Dependencies:
|
CY_Password API Token
|
||||||
# -------------
|
CY_OTP_Secret OTP token. Only required if using 2FA
|
||||||
# - oathtool (When using 2 Factor Authentication)
|
Issues: github.com/noplanman/cyon-api/issues
|
||||||
#
|
Author: Armando Lüscher <armando@noplanman.ch>
|
||||||
# Issues:
|
'
|
||||||
# -------
|
|
||||||
# Any issues / questions / suggestions can be posted here:
|
|
||||||
# https://github.com/noplanman/cyon-api/issues
|
|
||||||
#
|
|
||||||
# Author: Armando Lüscher <armando@noplanman.ch>
|
|
||||||
########
|
|
||||||
|
|
||||||
dns_cyon_add() {
|
dns_cyon_add() {
|
||||||
_cyon_load_credentials &&
|
_cyon_load_credentials &&
|
||||||
@@ -221,10 +215,8 @@ _cyon_change_domain_env() {
|
|||||||
|
|
||||||
if ! _cyon_check_if_2fa_missed "${domain_env_response}"; then return 1; fi
|
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.
|
# 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 " $(printf "%s" "${domain_env_response}" | _cyon_get_response_message)"
|
||||||
_err ""
|
_err ""
|
||||||
return 1
|
return 1
|
||||||
@@ -238,7 +230,7 @@ _cyon_add_txt() {
|
|||||||
_info " - Adding DNS TXT entry..."
|
_info " - Adding DNS TXT entry..."
|
||||||
|
|
||||||
add_txt_url="https://my.cyon.ch/domain/dnseditor/add-record-async"
|
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")"
|
add_txt_response="$(_post "$add_txt_data" "$add_txt_url")"
|
||||||
_debug add_txt_response "${add_txt_response}"
|
_debug add_txt_response "${add_txt_response}"
|
||||||
@@ -247,9 +239,10 @@ _cyon_add_txt() {
|
|||||||
|
|
||||||
add_txt_message="$(printf "%s" "${add_txt_response}" | _cyon_get_response_message)"
|
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_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.
|
# 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 " ${add_txt_message}"
|
||||||
_err ""
|
_err ""
|
||||||
return 1
|
return 1
|
||||||
@@ -311,13 +304,21 @@ _cyon_get_response_message() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_cyon_get_response_status() {
|
_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() {
|
_cyon_get_response_success() {
|
||||||
_egrep_o '"onSuccess":"[^"]*"' | cut -d : -f 2 | tr -d '"'
|
_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() {
|
_cyon_check_if_2fa_missed() {
|
||||||
# Did we miss the 2FA?
|
# Did we miss the 2FA?
|
||||||
if test "${1#*multi_factor_form}" != "${1}"; then
|
if test "${1#*multi_factor_form}" != "${1}"; then
|
||||||
|
|||||||
@@ -1,31 +1,14 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
# -*- mode: sh; tab-width: 2; indent-tabs-mode: s; coding: utf-8 -*-
|
# shellcheck disable=SC2034
|
||||||
# vim: et ts=2 sw=2
|
dns_da_info='DirectAdmin Server API
|
||||||
#
|
Site: DirectAdmin.com/api.php
|
||||||
# DirectAdmin 1.41.0 API
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_da
|
||||||
# The DirectAdmin interface has it's own Let's encrypt functionality, but this
|
Options:
|
||||||
# script can be used to generate certificates for names which are not hosted on
|
DA_Api API Server URL. E.g. "https://remoteUser:remotePassword@da.domain.tld:8443"
|
||||||
# DirectAdmin
|
DA_Api_Insecure Insecure TLS. 0: check for cert validity, 1: always accept
|
||||||
#
|
Issues: github.com/TigerP/acme.sh/issues
|
||||||
# User must provide login data and URL to DirectAdmin incl. port.
|
'
|
||||||
# You can create login key, by using the Login Keys function
|
|
||||||
# ( https://da.example.com:8443/CMD_LOGIN_KEYS ), which only has access to
|
|
||||||
# - CMD_API_DNS_CONTROL
|
|
||||||
# - CMD_API_SHOW_DOMAINS
|
|
||||||
#
|
|
||||||
# See also https://www.directadmin.com/api.php and
|
|
||||||
# https://www.directadmin.com/features.php?id=1298
|
|
||||||
#
|
|
||||||
# Report bugs to https://github.com/TigerP/acme.sh/issues
|
|
||||||
#
|
|
||||||
# Values to export:
|
|
||||||
# export DA_Api="https://remoteUser:remotePassword@da.example.com:8443"
|
|
||||||
# export DA_Api_Insecure=1
|
|
||||||
#
|
|
||||||
# Set DA_Api_Insecure to 1 for insecure and 0 for secure -> difference is
|
|
||||||
# whether ssl cert is checked for validity (0) or whether it is just accepted
|
|
||||||
# (1)
|
|
||||||
#
|
|
||||||
######## Public functions #####################
|
######## Public functions #####################
|
||||||
|
|
||||||
# Usage: dns_myapi_add _acme-challenge.www.example.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
# Usage: dns_myapi_add _acme-challenge.www.example.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
@@ -78,7 +61,7 @@ _get_root() {
|
|||||||
# response will contain "list[]=example.com&list[]=example.org"
|
# response will contain "list[]=example.com&list[]=example.org"
|
||||||
_da_api CMD_API_SHOW_DOMAINS "" "${domain}"
|
_da_api CMD_API_SHOW_DOMAINS "" "${domain}"
|
||||||
while true; do
|
while true; do
|
||||||
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
|
||||||
_debug h "$h"
|
_debug h "$h"
|
||||||
if [ -z "$h" ]; then
|
if [ -z "$h" ]; then
|
||||||
# not valid
|
# not valid
|
||||||
@@ -86,7 +69,7 @@ _get_root() {
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
if _contains "$response" "$h" >/dev/null; then
|
if _contains "$response" "$h" >/dev/null; then
|
||||||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
|
||||||
_domain=$h
|
_domain=$h
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -1,16 +1,13 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
#Created by RaidenII, to use DuckDNS's API to add/remove text records
|
dns_ddnss_info='DDNSS.de
|
||||||
#modified by helbgd @ 03/13/2018 to support ddnss.de
|
Site: DDNSS.de
|
||||||
#modified by mod242 @ 04/24/2018 to support different ddnss domains
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_ddnss
|
||||||
#Please note: the Wildcard Feature must be turned on for the Host record
|
Options:
|
||||||
#and the checkbox for TXT needs to be enabled
|
DDNSS_Token API Token
|
||||||
|
Issues: github.com/acmesh-official/acme.sh/issues/2230
|
||||||
# Pass credentials before "acme.sh --issue --dns dns_ddnss ..."
|
Author: @helbgd, @mod242
|
||||||
# --
|
'
|
||||||
# export DDNSS_Token="aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
|
|
||||||
# --
|
|
||||||
#
|
|
||||||
|
|
||||||
DDNSS_DNS_API="https://ddnss.de/upd.php"
|
DDNSS_DNS_API="https://ddnss.de/upd.php"
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
#
|
# shellcheck disable=SC2034
|
||||||
# deSEC.io Domain API
|
dns_desec_info='deSEC.io
|
||||||
#
|
Site: desec.readthedocs.io/en/latest/
|
||||||
# Author: Zheng Qian
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_desec
|
||||||
#
|
Options:
|
||||||
# deSEC API doc
|
DDNSS_Token API Token
|
||||||
# https://desec.readthedocs.io/en/latest/
|
Issues: github.com/acmesh-official/acme.sh/issues/2180
|
||||||
|
Author: Zheng Qian
|
||||||
|
'
|
||||||
|
|
||||||
REST_API="https://desec.io/api/v1/domains"
|
REST_API="https://desec.io/api/v1/domains"
|
||||||
|
|
||||||
@@ -174,7 +176,7 @@ _get_root() {
|
|||||||
i=2
|
i=2
|
||||||
p=1
|
p=1
|
||||||
while true; do
|
while true; do
|
||||||
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
|
||||||
_debug h "$h"
|
_debug h "$h"
|
||||||
if [ -z "$h" ]; then
|
if [ -z "$h" ]; then
|
||||||
#not valid
|
#not valid
|
||||||
@@ -186,7 +188,7 @@ _get_root() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if _contains "$response" "\"name\":\"$h\"" >/dev/null; then
|
if _contains "$response" "\"name\":\"$h\"" >/dev/null; then
|
||||||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
|
||||||
_domain=$h
|
_domain=$h
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -1,18 +1,15 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
########################################################################
|
dns_df_info='DynDnsFree.de
|
||||||
# https://dyndnsfree.de hook script for acme.sh
|
Domains: dynup.de
|
||||||
#
|
Site: DynDnsFree.de
|
||||||
# Environment variables:
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_df
|
||||||
#
|
Options:
|
||||||
# - $DF_user (your dyndnsfree.de username)
|
DF_user Username
|
||||||
# - $DF_password (your dyndnsfree.de password)
|
DF_password Password
|
||||||
#
|
Issues: github.com/acmesh-official/acme.sh/issues/2897
|
||||||
# Author: Thilo Gass <thilo.gass@gmail.com>
|
Author: Thilo Gass <thilo.gass@gmail.com>
|
||||||
# Git repo: https://github.com/ThiloGa/acme.sh
|
'
|
||||||
|
|
||||||
#-- dns_df_add() - Add TXT record --------------------------------------
|
|
||||||
# Usage: dns_df_add _acme-challenge.subdomain.domain.com "XyZ123..."
|
|
||||||
|
|
||||||
dyndnsfree_api="https://dynup.de/acme.php"
|
dyndnsfree_api="https://dynup.de/acme.php"
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,12 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
## Will be called by acme.sh to add the txt record to your api system.
|
dns_dgon_info='DigitalOcean.com
|
||||||
## returns 0 means success, otherwise error.
|
Site: DigitalOcean.com/help/api/
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_dgon
|
||||||
## Author: thewer <github at thewer.com>
|
Options:
|
||||||
## GitHub: https://github.com/gitwer/acme.sh
|
DO_API_KEY API Key
|
||||||
|
Author: <github@thewer.com>
|
||||||
##
|
'
|
||||||
## Environment Variables Required:
|
|
||||||
##
|
|
||||||
## DO_API_KEY="75310dc4ca779ac39a19f6355db573b49ce92ae126553ebd61ac3a3ae34834cc"
|
|
||||||
##
|
|
||||||
|
|
||||||
##################### Public functions #####################
|
##################### Public functions #####################
|
||||||
|
|
||||||
@@ -207,7 +203,7 @@ _get_base_domain() {
|
|||||||
_debug2 domain_list "$domain_list"
|
_debug2 domain_list "$domain_list"
|
||||||
|
|
||||||
i=1
|
i=1
|
||||||
while [ $i -gt 0 ]; do
|
while [ "$i" -gt 0 ]; do
|
||||||
## get next longest domain
|
## get next longest domain
|
||||||
_domain=$(printf "%s" "$fulldomain" | cut -d . -f "$i"-"$MAX_DOM")
|
_domain=$(printf "%s" "$fulldomain" | cut -d . -f "$i"-"$MAX_DOM")
|
||||||
## check we got something back from our cut (or are we at the end)
|
## check we got something back from our cut (or are we at the end)
|
||||||
@@ -219,14 +215,14 @@ _get_base_domain() {
|
|||||||
## check if it exists
|
## check if it exists
|
||||||
if [ -n "$found" ]; then
|
if [ -n "$found" ]; then
|
||||||
## exists - exit loop returning the parts
|
## exists - exit loop returning the parts
|
||||||
sub_point=$(_math $i - 1)
|
sub_point=$(_math "$i" - 1)
|
||||||
_sub_domain=$(printf "%s" "$fulldomain" | cut -d . -f 1-"$sub_point")
|
_sub_domain=$(printf "%s" "$fulldomain" | cut -d . -f 1-"$sub_point")
|
||||||
_debug _domain "$_domain"
|
_debug _domain "$_domain"
|
||||||
_debug _sub_domain "$_sub_domain"
|
_debug _sub_domain "$_sub_domain"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
## increment cut point $i
|
## increment cut point $i
|
||||||
i=$(_math $i + 1)
|
i=$(_math "$i" + 1)
|
||||||
done
|
done
|
||||||
|
|
||||||
if [ -z "$found" ]; then
|
if [ -z "$found" ]; then
|
||||||
|
|||||||
188
dnsapi/dns_dnsexit.sh
Normal file
188
dnsapi/dns_dnsexit.sh
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
dns_dnsexit_info='DNSExit.com
|
||||||
|
Site: DNSExit.com
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_dnsexit
|
||||||
|
Options:
|
||||||
|
DNSEXIT_API_KEY API Key
|
||||||
|
DNSEXIT_AUTH_USER Username
|
||||||
|
DNSEXIT_AUTH_PASS Password
|
||||||
|
Issues: github.com/acmesh-official/acme.sh/issues/4719
|
||||||
|
Author: Samuel Jimenez
|
||||||
|
'
|
||||||
|
|
||||||
|
DNSEXIT_API_URL="https://api.dnsexit.com/dns/"
|
||||||
|
DNSEXIT_HOSTS_URL="https://update.dnsexit.com/ipupdate/hosts.jsp"
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
#Usage: dns_dnsexit_add _acme-challenge.*.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
|
dns_dnsexit_add() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
_info "Using DNSExit.com"
|
||||||
|
_debug fulldomain "$fulldomain"
|
||||||
|
_debug txtvalue "$txtvalue"
|
||||||
|
|
||||||
|
_debug 'Load account auth'
|
||||||
|
if ! get_account_info; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug 'First detect the root zone'
|
||||||
|
if ! _get_root "$fulldomain"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug _sub_domain "$_sub_domain"
|
||||||
|
_debug _domain "$_domain"
|
||||||
|
|
||||||
|
if ! _dnsexit_rest "{\"domain\":\"$_domain\",\"add\":{\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"content\":\"$txtvalue\",\"ttl\":0,\"overwrite\":false}}"; then
|
||||||
|
_err "$response"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug2 _response "$response"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#Usage: fulldomain txtvalue
|
||||||
|
#Remove the txt record after validation.
|
||||||
|
dns_dnsexit_rm() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
_info "Using DNSExit.com"
|
||||||
|
_debug fulldomain "$fulldomain"
|
||||||
|
_debug txtvalue "$txtvalue"
|
||||||
|
|
||||||
|
_debug 'Load account auth'
|
||||||
|
if ! get_account_info; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug 'First detect the root zone'
|
||||||
|
if ! _get_root "$fulldomain"; then
|
||||||
|
_err "$response"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug _sub_domain "$_sub_domain"
|
||||||
|
_debug _domain "$_domain"
|
||||||
|
|
||||||
|
if ! _dnsexit_rest "{\"domain\":\"$_domain\",\"delete\":{\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"content\":\"$txtvalue\"}}"; then
|
||||||
|
_err "$response"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug2 _response "$response"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#################### Private functions below ##################################
|
||||||
|
#_acme-challenge.www.domain.com
|
||||||
|
#returns
|
||||||
|
# _sub_domain=_acme-challenge.www
|
||||||
|
# _domain=domain.com
|
||||||
|
_get_root() {
|
||||||
|
domain=$1
|
||||||
|
i=1
|
||||||
|
while true; do
|
||||||
|
_domain=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
|
||||||
|
_debug h "$_domain"
|
||||||
|
if [ -z "$_domain" ]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug login "$DNSEXIT_AUTH_USER"
|
||||||
|
_debug password "$DNSEXIT_AUTH_PASS"
|
||||||
|
_debug domain "$_domain"
|
||||||
|
|
||||||
|
_dnsexit_http "login=$DNSEXIT_AUTH_USER&password=$DNSEXIT_AUTH_PASS&domain=$_domain"
|
||||||
|
|
||||||
|
if _contains "$response" "0=$_domain"; then
|
||||||
|
_sub_domain="$(echo "$fulldomain" | sed "s/\\.$_domain\$//")"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
_debug "Go to next level of $_domain"
|
||||||
|
fi
|
||||||
|
i=$(_math "$i" + 1)
|
||||||
|
done
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
_dnsexit_rest() {
|
||||||
|
m=POST
|
||||||
|
ep=""
|
||||||
|
data="$1"
|
||||||
|
_debug _dnsexit_rest "$ep"
|
||||||
|
_debug data "$data"
|
||||||
|
|
||||||
|
api_key_trimmed=$(echo "$DNSEXIT_API_KEY" | tr -d '"')
|
||||||
|
|
||||||
|
export _H1="apikey: $api_key_trimmed"
|
||||||
|
export _H2='Content-Type: application/json'
|
||||||
|
|
||||||
|
if [ "$m" != "GET" ]; then
|
||||||
|
_debug data "$data"
|
||||||
|
response="$(_post "$data" "$DNSEXIT_API_URL/$ep" "" "$m")"
|
||||||
|
else
|
||||||
|
response="$(_get "$DNSEXIT_API_URL/$ep")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
_err "Error $ep"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug2 response "$response"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
_dnsexit_http() {
|
||||||
|
m=GET
|
||||||
|
param="$1"
|
||||||
|
_debug param "$param"
|
||||||
|
_debug get "$DNSEXIT_HOSTS_URL?$param"
|
||||||
|
|
||||||
|
response="$(_get "$DNSEXIT_HOSTS_URL?$param")"
|
||||||
|
|
||||||
|
_debug response "$response"
|
||||||
|
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
_err "Error $param"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug2 response "$response"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
get_account_info() {
|
||||||
|
|
||||||
|
DNSEXIT_API_KEY="${DNSEXIT_API_KEY:-$(_readaccountconf_mutable DNSEXIT_API_KEY)}"
|
||||||
|
if test -z "$DNSEXIT_API_KEY"; then
|
||||||
|
DNSEXIT_API_KEY=''
|
||||||
|
_err 'DNSEXIT_API_KEY was not exported'
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_saveaccountconf_mutable DNSEXIT_API_KEY "$DNSEXIT_API_KEY"
|
||||||
|
|
||||||
|
DNSEXIT_AUTH_USER="${DNSEXIT_AUTH_USER:-$(_readaccountconf_mutable DNSEXIT_AUTH_USER)}"
|
||||||
|
if test -z "$DNSEXIT_AUTH_USER"; then
|
||||||
|
DNSEXIT_AUTH_USER=""
|
||||||
|
_err 'DNSEXIT_AUTH_USER was not exported'
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_saveaccountconf_mutable DNSEXIT_AUTH_USER "$DNSEXIT_AUTH_USER"
|
||||||
|
|
||||||
|
DNSEXIT_AUTH_PASS="${DNSEXIT_AUTH_PASS:-$(_readaccountconf_mutable DNSEXIT_AUTH_PASS)}"
|
||||||
|
if test -z "$DNSEXIT_AUTH_PASS"; then
|
||||||
|
DNSEXIT_AUTH_PASS=""
|
||||||
|
_err 'DNSEXIT_AUTH_PASS was not exported'
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_saveaccountconf_mutable DNSEXIT_AUTH_PASS "$DNSEXIT_AUTH_PASS"
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
@@ -1,15 +1,14 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
# dnsHome.de API for acme.sh
|
dns_dnshome_info='dnsHome.de
|
||||||
#
|
Site: dnsHome.de
|
||||||
# This Script adds the necessary TXT record to a Subdomain
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_dnshome
|
||||||
#
|
Options:
|
||||||
# Author dnsHome.de (https://github.com/dnsHome-de)
|
DNSHOME_Subdomain Subdomain
|
||||||
#
|
DNSHOME_SubdomainPassword Subdomain Password
|
||||||
# Report Bugs to https://github.com/acmesh-official/acme.sh/issues/3819
|
Issues: github.com/acmesh-official/acme.sh/issues/3819
|
||||||
#
|
Author: @dnsHome-de
|
||||||
# export DNSHOME_Subdomain=""
|
'
|
||||||
# export DNSHOME_SubdomainPassword=""
|
|
||||||
|
|
||||||
# Usage: add subdomain.ddnsdomain.tld "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
# Usage: add subdomain.ddnsdomain.tld "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
# Used to add txt record
|
# Used to add txt record
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
# DNSimple domain api
|
dns_dnsimple_info='DNSimple.com
|
||||||
# https://github.com/pho3nixf1re/acme.sh/issues
|
Site: DNSimple.com
|
||||||
#
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_dnsimple
|
||||||
# This is your oauth token which can be acquired on the account page. Please
|
Options:
|
||||||
# note that this must be an _account_ token and not a _user_ token.
|
DNSimple_OAUTH_TOKEN OAuth Token
|
||||||
# https://dnsimple.com/a/<your account id>/account/access_tokens
|
Issues: github.com/pho3nixf1re/acme.sh/issues
|
||||||
# DNSimple_OAUTH_TOKEN="sdfsdfsdfljlbjkljlkjsdfoiwje"
|
'
|
||||||
|
|
||||||
DNSimple_API="https://api.dnsimple.com/v2"
|
DNSimple_API="https://api.dnsimple.com/v2"
|
||||||
|
|
||||||
@@ -92,7 +92,7 @@ _get_root() {
|
|||||||
i=2
|
i=2
|
||||||
previous=1
|
previous=1
|
||||||
while true; do
|
while true; do
|
||||||
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
|
||||||
if [ -z "$h" ]; then
|
if [ -z "$h" ]; then
|
||||||
# not valid
|
# not valid
|
||||||
return 1
|
return 1
|
||||||
@@ -105,7 +105,7 @@ _get_root() {
|
|||||||
if _contains "$response" 'not found'; then
|
if _contains "$response" 'not found'; then
|
||||||
_debug "$h not found"
|
_debug "$h not found"
|
||||||
else
|
else
|
||||||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$previous)
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$previous")
|
||||||
_domain="$h"
|
_domain="$h"
|
||||||
|
|
||||||
_debug _domain "$_domain"
|
_debug _domain "$_domain"
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
dns_dnsservices_info='DNS.Services
|
||||||
|
Site: DNS.Services
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_dnsservices
|
||||||
|
Options:
|
||||||
|
DnsServices_Username Username
|
||||||
|
DnsServices_Password Password
|
||||||
|
Issues: github.com/acmesh-official/acme.sh/issues/4152
|
||||||
|
Author: Bjarke Bruun <bbruun@gmail.com>
|
||||||
|
'
|
||||||
|
|
||||||
#This file name is "dns_dnsservices.sh"
|
|
||||||
#Script for Danish DNS registra and DNS hosting provider https://dns.services
|
|
||||||
|
|
||||||
#Author: Bjarke Bruun <bbruun@gmail.com>
|
|
||||||
#Report Bugs here: https://github.com/acmesh-official/acme.sh/issues/4152
|
|
||||||
|
|
||||||
# Global variable to connect to the DNS.Services API
|
|
||||||
DNSServices_API=https://dns.services/api
|
DNSServices_API=https://dns.services/api
|
||||||
|
|
||||||
######## Public functions #####################
|
######## Public functions #####################
|
||||||
|
|||||||
148
dnsapi/dns_do.sh
148
dnsapi/dns_do.sh
@@ -1,148 +0,0 @@
|
|||||||
#!/usr/bin/env sh
|
|
||||||
|
|
||||||
# DNS API for Domain-Offensive / Resellerinterface / Domainrobot
|
|
||||||
|
|
||||||
# Report bugs at https://github.com/seidler2547/acme.sh/issues
|
|
||||||
|
|
||||||
# set these environment variables to match your customer ID and password:
|
|
||||||
# DO_PID="KD-1234567"
|
|
||||||
# DO_PW="cdfkjl3n2"
|
|
||||||
|
|
||||||
DO_URL="https://soap.resellerinterface.de/"
|
|
||||||
|
|
||||||
######## Public functions #####################
|
|
||||||
|
|
||||||
#Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
|
||||||
dns_do_add() {
|
|
||||||
fulldomain=$1
|
|
||||||
txtvalue=$2
|
|
||||||
if _dns_do_authenticate; then
|
|
||||||
_info "Adding TXT record to ${_domain} as ${fulldomain}"
|
|
||||||
_dns_do_soap createRR origin "${_domain}" name "${fulldomain}" type TXT data "${txtvalue}" ttl 300
|
|
||||||
if _contains "${response}" '>success<'; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
_err "Could not create resource record, check logs"
|
|
||||||
fi
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
#fulldomain
|
|
||||||
dns_do_rm() {
|
|
||||||
fulldomain=$1
|
|
||||||
if _dns_do_authenticate; then
|
|
||||||
if _dns_do_list_rrs; then
|
|
||||||
_dns_do_had_error=0
|
|
||||||
for _rrid in ${_rr_list}; do
|
|
||||||
_info "Deleting resource record $_rrid for $_domain"
|
|
||||||
_dns_do_soap deleteRR origin "${_domain}" rrid "${_rrid}"
|
|
||||||
if ! _contains "${response}" '>success<'; then
|
|
||||||
_dns_do_had_error=1
|
|
||||||
_err "Could not delete resource record for ${_domain}, id ${_rrid}"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
return $_dns_do_had_error
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
#################### Private functions below ##################################
|
|
||||||
_dns_do_authenticate() {
|
|
||||||
_info "Authenticating as ${DO_PID}"
|
|
||||||
_dns_do_soap authPartner partner "${DO_PID}" password "${DO_PW}"
|
|
||||||
if _contains "${response}" '>success<'; then
|
|
||||||
_get_root "$fulldomain"
|
|
||||||
_debug "_domain $_domain"
|
|
||||||
return 0
|
|
||||||
else
|
|
||||||
_err "Authentication failed, are DO_PID and DO_PW set correctly?"
|
|
||||||
fi
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
_dns_do_list_rrs() {
|
|
||||||
_dns_do_soap getRRList origin "${_domain}"
|
|
||||||
if ! _contains "${response}" 'SOAP-ENC:Array'; then
|
|
||||||
_err "getRRList origin ${_domain} failed"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
_rr_list="$(echo "${response}" |
|
|
||||||
tr -d "\n\r\t" |
|
|
||||||
sed -e 's/<item xsi:type="ns2:Map">/\n/g' |
|
|
||||||
grep ">$(_regexcape "$fulldomain")</value>" |
|
|
||||||
sed -e 's/<\/item>/\n/g' |
|
|
||||||
grep '>id</key><value' |
|
|
||||||
_egrep_o '>[0-9]{1,16}<' |
|
|
||||||
tr -d '><')"
|
|
||||||
[ "${_rr_list}" ]
|
|
||||||
}
|
|
||||||
|
|
||||||
_dns_do_soap() {
|
|
||||||
func="$1"
|
|
||||||
shift
|
|
||||||
# put the parameters to xml
|
|
||||||
body="<tns:${func} xmlns:tns=\"${DO_URL}\">"
|
|
||||||
while [ "$1" ]; do
|
|
||||||
_k="$1"
|
|
||||||
shift
|
|
||||||
_v="$1"
|
|
||||||
shift
|
|
||||||
body="$body<$_k>$_v</$_k>"
|
|
||||||
done
|
|
||||||
body="$body</tns:${func}>"
|
|
||||||
_debug2 "SOAP request ${body}"
|
|
||||||
|
|
||||||
# build SOAP XML
|
|
||||||
_xml='<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
|
|
||||||
<env:Body>'"$body"'</env:Body>
|
|
||||||
</env:Envelope>'
|
|
||||||
|
|
||||||
# set SOAP headers
|
|
||||||
export _H1="SOAPAction: ${DO_URL}#${func}"
|
|
||||||
|
|
||||||
if ! response="$(_post "${_xml}" "${DO_URL}")"; then
|
|
||||||
_err "Error <$1>"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
_debug2 "SOAP response $response"
|
|
||||||
|
|
||||||
# retrieve cookie header
|
|
||||||
_H2="$(_egrep_o 'Cookie: [^;]+' <"$HTTP_HEADER" | _head_n 1)"
|
|
||||||
export _H2
|
|
||||||
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
_get_root() {
|
|
||||||
domain=$1
|
|
||||||
i=1
|
|
||||||
|
|
||||||
_dns_do_soap getDomainList
|
|
||||||
_all_domains="$(echo "${response}" |
|
|
||||||
tr -d "\n\r\t " |
|
|
||||||
_egrep_o 'domain</key><value[^>]+>[^<]+' |
|
|
||||||
sed -e 's/^domain<\/key><value[^>]*>//g')"
|
|
||||||
|
|
||||||
while true; do
|
|
||||||
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
|
||||||
if [ -z "$h" ]; then
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if _contains "${_all_domains}" "^$(_regexcape "$h")\$"; then
|
|
||||||
_domain="$h"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
i=$(_math $i + 1)
|
|
||||||
done
|
|
||||||
_debug "$domain not found"
|
|
||||||
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
_regexcape() {
|
|
||||||
echo "$1" | sed -e 's/\([]\.$*^[]\)/\\\1/g'
|
|
||||||
}
|
|
||||||
@@ -1,14 +1,16 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
dns_doapi_info='Domain-Offensive do.de
|
||||||
|
Official LetsEncrypt API for do.de / Domain-Offensive.
|
||||||
|
This API is also available to private customers/individuals.
|
||||||
|
Site: do.de
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_doapi
|
||||||
|
Options:
|
||||||
|
DO_LETOKEN LetsEncrypt Token
|
||||||
|
Issues: github.com/acmesh-official/acme.sh/issues/2057
|
||||||
|
'
|
||||||
|
|
||||||
# Official Let's Encrypt API for do.de / Domain-Offensive
|
DO_API="https://my.do.de/api/letsencrypt"
|
||||||
#
|
|
||||||
# This is different from the dns_do adapter, because dns_do is only usable for enterprise customers
|
|
||||||
# This API is also available to private customers/individuals
|
|
||||||
#
|
|
||||||
# Provide the required LetsEncrypt token like this:
|
|
||||||
# DO_LETOKEN="FmD408PdqT1E269gUK57"
|
|
||||||
|
|
||||||
DO_API="https://www.do.de/api/letsencrypt"
|
|
||||||
|
|
||||||
######## Public functions #####################
|
######## Public functions #####################
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,13 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
dns_domeneshop_info='DomeneShop.no
|
||||||
|
Site: DomeneShop.no
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_domeneshop
|
||||||
|
Options:
|
||||||
|
DOMENESHOP_Token Token
|
||||||
|
DOMENESHOP_Secret Secret
|
||||||
|
Issues: github.com/acmesh-official/acme.sh/issues/2457
|
||||||
|
'
|
||||||
|
|
||||||
DOMENESHOP_Api_Endpoint="https://api.domeneshop.no/v0"
|
DOMENESHOP_Api_Endpoint="https://api.domeneshop.no/v0"
|
||||||
|
|
||||||
@@ -84,7 +93,7 @@ _get_domainid() {
|
|||||||
i=2
|
i=2
|
||||||
p=1
|
p=1
|
||||||
while true; do
|
while true; do
|
||||||
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
|
||||||
_debug "h" "$h"
|
_debug "h" "$h"
|
||||||
if [ -z "$h" ]; then
|
if [ -z "$h" ]; then
|
||||||
#not valid
|
#not valid
|
||||||
@@ -93,7 +102,7 @@ _get_domainid() {
|
|||||||
|
|
||||||
if _contains "$response" "\"$h\"" >/dev/null; then
|
if _contains "$response" "\"$h\"" >/dev/null; then
|
||||||
# We have found the domain name.
|
# We have found the domain name.
|
||||||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
|
||||||
_domain=$h
|
_domain=$h
|
||||||
_domainid=$(printf "%s" "$response" | _egrep_o "[^{]*\"domain\":\"$_domain\"[^}]*" | _egrep_o "\"id\":[0-9]+" | cut -d : -f 2)
|
_domainid=$(printf "%s" "$response" | _egrep_o "[^{]*\"domain\":\"$_domain\"[^}]*" | _egrep_o "\"id\":[0-9]+" | cut -d : -f 2)
|
||||||
return 0
|
return 0
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
# Dnspod.cn Domain api
|
dns_dp_info='DNSPod.cn
|
||||||
#
|
Site: DNSPod.cn
|
||||||
#DP_Id="1234"
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_dp
|
||||||
#
|
Options:
|
||||||
#DP_Key="sADDsdasdgdsf"
|
DP_Id Id
|
||||||
|
DP_Key Key
|
||||||
|
'
|
||||||
|
|
||||||
REST_API="https://dnsapi.cn"
|
REST_API="https://dnsapi.cn"
|
||||||
|
|
||||||
@@ -107,7 +109,7 @@ _get_root() {
|
|||||||
i=2
|
i=2
|
||||||
p=1
|
p=1
|
||||||
while true; do
|
while true; do
|
||||||
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
|
||||||
if [ -z "$h" ]; then
|
if [ -z "$h" ]; then
|
||||||
#not valid
|
#not valid
|
||||||
return 1
|
return 1
|
||||||
@@ -121,7 +123,7 @@ _get_root() {
|
|||||||
_domain_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")
|
_domain_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")
|
||||||
_debug _domain_id "$_domain_id"
|
_debug _domain_id "$_domain_id"
|
||||||
if [ "$_domain_id" ]; then
|
if [ "$_domain_id" ]; then
|
||||||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
|
||||||
_debug _sub_domain "$_sub_domain"
|
_debug _sub_domain "$_sub_domain"
|
||||||
_domain="$h"
|
_domain="$h"
|
||||||
_debug _domain "$_domain"
|
_debug _domain "$_domain"
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
# Dnspod.com Domain api
|
dns_dpi_info='DNSPod.com
|
||||||
#
|
Site: DNSPod.com
|
||||||
#DPI_Id="1234"
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_dpi
|
||||||
#
|
Options:
|
||||||
#DPI_Key="sADDsdasdgdsf"
|
DPI_Id Id
|
||||||
|
DPI_Key Key
|
||||||
|
'
|
||||||
|
|
||||||
REST_API="https://api.dnspod.com"
|
REST_API="https://api.dnspod.com"
|
||||||
|
|
||||||
@@ -107,7 +109,7 @@ _get_root() {
|
|||||||
i=2
|
i=2
|
||||||
p=1
|
p=1
|
||||||
while true; do
|
while true; do
|
||||||
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
|
||||||
if [ -z "$h" ]; then
|
if [ -z "$h" ]; then
|
||||||
#not valid
|
#not valid
|
||||||
return 1
|
return 1
|
||||||
@@ -121,7 +123,7 @@ _get_root() {
|
|||||||
_domain_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")
|
_domain_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")
|
||||||
_debug _domain_id "$_domain_id"
|
_debug _domain_id "$_domain_id"
|
||||||
if [ "$_domain_id" ]; then
|
if [ "$_domain_id" ]; then
|
||||||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
|
||||||
_debug _sub_domain "$_sub_domain"
|
_debug _sub_domain "$_sub_domain"
|
||||||
_domain="$h"
|
_domain="$h"
|
||||||
_debug _domain "$_domain"
|
_debug _domain "$_domain"
|
||||||
|
|||||||
@@ -1,10 +1,14 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
dns_dreamhost_info='DreamHost.com
|
||||||
|
Site: DreamHost.com
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_dreamhost
|
||||||
|
Options:
|
||||||
|
DH_API_KEY API Key
|
||||||
|
Issues: github.com/RhinoLance/acme.sh
|
||||||
|
Author: RhinoLance
|
||||||
|
'
|
||||||
|
|
||||||
#Author: RhinoLance
|
|
||||||
#Report Bugs here: https://github.com/RhinoLance/acme.sh
|
|
||||||
#
|
|
||||||
|
|
||||||
#define the api endpoint
|
|
||||||
DH_API_ENDPOINT="https://api.dreamhost.com/"
|
DH_API_ENDPOINT="https://api.dreamhost.com/"
|
||||||
querystring=""
|
querystring=""
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
#Created by RaidenII, to use DuckDNS's API to add/remove text records
|
dns_duckdns_info='DuckDNS.org
|
||||||
#06/27/2017
|
Site: www.DuckDNS.org
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_duckdns
|
||||||
# Pass credentials before "acme.sh --issue --dns dns_duckdns ..."
|
Options:
|
||||||
# --
|
DuckDNS_Token API Token
|
||||||
# export DuckDNS_Token="aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
|
Author: @RaidenII
|
||||||
# --
|
'
|
||||||
#
|
|
||||||
# Due to the fact that DuckDNS uses StartSSL as cert provider, --insecure may need to be used with acme.sh
|
|
||||||
|
|
||||||
DuckDNS_API="https://www.duckdns.org/update"
|
DuckDNS_API="https://www.duckdns.org/update"
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,13 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
#DD_API_User="xxxxx"
|
dns_durabledns_info='DurableDNS.com
|
||||||
#DD_API_Key="xxxxxx"
|
Site: DurableDNS.com
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_durabledns
|
||||||
|
Options:
|
||||||
|
DD_API_User API User
|
||||||
|
DD_API_Key API Key
|
||||||
|
Issues: github.com/acmesh-official/acme.sh/issues/2281
|
||||||
|
'
|
||||||
|
|
||||||
_DD_BASE="https://durabledns.com/services/dns"
|
_DD_BASE="https://durabledns.com/services/dns"
|
||||||
|
|
||||||
@@ -104,7 +110,7 @@ _get_root() {
|
|||||||
i=1
|
i=1
|
||||||
p=1
|
p=1
|
||||||
while true; do
|
while true; do
|
||||||
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
|
||||||
_debug h "$h"
|
_debug h "$h"
|
||||||
if [ -z "$h" ]; then
|
if [ -z "$h" ]; then
|
||||||
#not valid
|
#not valid
|
||||||
@@ -112,7 +118,7 @@ _get_root() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if _contains "$response" ">$h.</origin>"; then
|
if _contains "$response" ">$h.</origin>"; then
|
||||||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
|
||||||
_domain=$h
|
_domain=$h
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -1,10 +1,16 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
#
|
# shellcheck disable=SC2034
|
||||||
# Dyn.com Domain API
|
dns_dyn_info='Dyn.com
|
||||||
#
|
Domains: dynect.net
|
||||||
# Author: Gerd Naschenweng
|
Site: Dyn.com
|
||||||
# https://github.com/magicdude4eva
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_dyn
|
||||||
#
|
Options:
|
||||||
|
DYN_Customer Customer
|
||||||
|
DYN_Username API Username
|
||||||
|
DYN_Password Secret
|
||||||
|
Author: Gerd Naschenweng <@magicdude4eva>
|
||||||
|
'
|
||||||
|
|
||||||
# Dyn Managed DNS API
|
# Dyn Managed DNS API
|
||||||
# https://help.dyn.com/dns-api-knowledge-base/
|
# https://help.dyn.com/dns-api-knowledge-base/
|
||||||
#
|
#
|
||||||
@@ -20,13 +26,6 @@
|
|||||||
# ZoneRemoveNode
|
# ZoneRemoveNode
|
||||||
# ZonePublish
|
# ZonePublish
|
||||||
# --
|
# --
|
||||||
#
|
|
||||||
# Pass credentials before "acme.sh --issue --dns dns_dyn ..."
|
|
||||||
# --
|
|
||||||
# export DYN_Customer="customer"
|
|
||||||
# export DYN_Username="apiuser"
|
|
||||||
# export DYN_Password="secret"
|
|
||||||
# --
|
|
||||||
|
|
||||||
DYN_API="https://api.dynect.net/REST"
|
DYN_API="https://api.dynect.net/REST"
|
||||||
|
|
||||||
|
|||||||
@@ -1,20 +1,21 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
dns_dynu_info='Dynu.com
|
||||||
|
Site: Dynu.com
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_dynu
|
||||||
|
Options:
|
||||||
|
Dynu_ClientId Client ID
|
||||||
|
Dynu_Secret Secret
|
||||||
|
Issues: github.com/shar0119/acme.sh
|
||||||
|
Author: Dynu Systems Inc
|
||||||
|
'
|
||||||
|
|
||||||
#Client ID
|
|
||||||
#Dynu_ClientId="0b71cae7-a099-4f6b-8ddf-94571cdb760d"
|
|
||||||
#
|
|
||||||
#Secret
|
|
||||||
#Dynu_Secret="aCUEY4BDCV45KI8CSIC3sp2LKQ9"
|
|
||||||
#
|
|
||||||
#Token
|
#Token
|
||||||
Dynu_Token=""
|
Dynu_Token=""
|
||||||
#
|
#
|
||||||
#Endpoint
|
#Endpoint
|
||||||
Dynu_EndPoint="https://api.dynu.com/v2"
|
Dynu_EndPoint="https://api.dynu.com/v2"
|
||||||
#
|
|
||||||
#Author: Dynu Systems, Inc.
|
|
||||||
#Report Bugs here: https://github.com/shar0119/acme.sh
|
|
||||||
#
|
|
||||||
######## Public functions #####################
|
######## Public functions #####################
|
||||||
|
|
||||||
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
@@ -125,7 +126,7 @@ _get_root() {
|
|||||||
i=2
|
i=2
|
||||||
p=1
|
p=1
|
||||||
while true; do
|
while true; do
|
||||||
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
|
||||||
_debug h "$h"
|
_debug h "$h"
|
||||||
if [ -z "$h" ]; then
|
if [ -z "$h" ]; then
|
||||||
#not valid
|
#not valid
|
||||||
@@ -139,7 +140,7 @@ _get_root() {
|
|||||||
if _contains "$response" "\"domainName\":\"$h\"" >/dev/null; then
|
if _contains "$response" "\"domainName\":\"$h\"" >/dev/null; then
|
||||||
dnsId=$(printf "%s" "$response" | tr -d "{}" | cut -d , -f 2 | cut -d : -f 2)
|
dnsId=$(printf "%s" "$response" | tr -d "{}" | cut -d , -f 2 | cut -d : -f 2)
|
||||||
_domain_name=$h
|
_domain_name=$h
|
||||||
_node=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
_node=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
p=$i
|
p=$i
|
||||||
|
|||||||
@@ -1,16 +1,23 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
#Author StefanAbl
|
# shellcheck disable=SC2034
|
||||||
#Usage specify a private keyfile to use with dynv6 'export KEY="path/to/keyfile"'
|
dns_dynv6_info='DynV6.com
|
||||||
#or use the HTTP REST API by by specifying a token 'export DYNV6_TOKEN="value"
|
Site: DynV6.com
|
||||||
#if no keyfile is specified, you will be asked if you want to create one in /home/$USER/.ssh/dynv6 and /home/$USER/.ssh/dynv6.pub
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_dynv6
|
||||||
|
Options:
|
||||||
|
DYNV6_TOKEN REST API token. Get from https://DynV6.com/keys
|
||||||
|
OptionsAlt:
|
||||||
|
KEY Path to SSH private key file. E.g. "/root/.ssh/dynv6"
|
||||||
|
Issues: github.com/acmesh-official/acme.sh/issues/2702
|
||||||
|
Author: @StefanAbl
|
||||||
|
'
|
||||||
|
|
||||||
dynv6_api="https://dynv6.com/api/v2"
|
dynv6_api="https://dynv6.com/api/v2"
|
||||||
######## Public functions #####################
|
######## Public functions #####################
|
||||||
# Please Read this guide first: https://github.com/Neilpang/acme.sh/wiki/DNS-API-Dev-Guide
|
# Please Read this guide first: https://github.com/Neilpang/acme.sh/wiki/DNS-API-Dev-Guide
|
||||||
#Usage: dns_dynv6_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
#Usage: dns_dynv6_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
dns_dynv6_add() {
|
dns_dynv6_add() {
|
||||||
fulldomain=$1
|
fulldomain="$(echo "$1" | _lower_case)"
|
||||||
txtvalue=$2
|
txtvalue="$2"
|
||||||
_info "Using dynv6 api"
|
_info "Using dynv6 api"
|
||||||
_debug fulldomain "$fulldomain"
|
_debug fulldomain "$fulldomain"
|
||||||
_debug txtvalue "$txtvalue"
|
_debug txtvalue "$txtvalue"
|
||||||
@@ -36,15 +43,14 @@ dns_dynv6_add() {
|
|||||||
_err "Something went wrong! it does not seem like the record was added successfully"
|
_err "Something went wrong! it does not seem like the record was added successfully"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
return 1
|
|
||||||
fi
|
fi
|
||||||
return 1
|
|
||||||
}
|
}
|
||||||
#Usage: fulldomain txtvalue
|
#Usage: fulldomain txtvalue
|
||||||
#Remove the txt record after validation.
|
#Remove the txt record after validation.
|
||||||
dns_dynv6_rm() {
|
dns_dynv6_rm() {
|
||||||
fulldomain=$1
|
fulldomain="$(echo "$1" | _lower_case)"
|
||||||
txtvalue=$2
|
txtvalue="$2"
|
||||||
_info "Using dynv6 API"
|
_info "Using dynv6 API"
|
||||||
_debug fulldomain "$fulldomain"
|
_debug fulldomain "$fulldomain"
|
||||||
_debug txtvalue "$txtvalue"
|
_debug txtvalue "$txtvalue"
|
||||||
@@ -199,7 +205,7 @@ _get_zone_id() {
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
zone_id="$(echo "$response" | tr '}' '\n' | grep "$selected" | tr ',' '\n' | grep id | tr -d '"')"
|
zone_id="$(echo "$response" | tr '}' '\n' | grep "$selected" | tr ',' '\n' | grep '"id":' | tr -d '"')"
|
||||||
_zone_id="${zone_id#id:}"
|
_zone_id="${zone_id#id:}"
|
||||||
_debug "zone id: $_zone_id"
|
_debug "zone id: $_zone_id"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
dns_easydns_info='easyDNS.net
|
||||||
|
Site: easyDNS.net
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_easydns
|
||||||
|
Options:
|
||||||
|
EASYDNS_Token API Token
|
||||||
|
EASYDNS_Key API Key
|
||||||
|
Issues: github.com/acmesh-official/acme.sh/issues/2647
|
||||||
|
Author: @Neilpang, wurzelpanzer <wurzelpanzer@maximolider.net>
|
||||||
|
'
|
||||||
|
|
||||||
#######################################################
|
|
||||||
#
|
|
||||||
# easyDNS REST API for acme.sh by Neilpang based on dns_cf.sh
|
|
||||||
#
|
|
||||||
# API Documentation: https://sandbox.rest.easydns.net:3001/
|
# API Documentation: https://sandbox.rest.easydns.net:3001/
|
||||||
#
|
|
||||||
# Author: wurzelpanzer [wurzelpanzer@maximolider.net]
|
|
||||||
# Report Bugs here: https://github.com/acmesh-official/acme.sh/issues/2647
|
|
||||||
#
|
|
||||||
#################### Public functions #################
|
#################### Public functions #################
|
||||||
|
|
||||||
#EASYDNS_Key="xxxxxxxxxxxxxxxxxxxxxxxx"
|
#EASYDNS_Key="xxxxxxxxxxxxxxxxxxxxxxxx"
|
||||||
@@ -118,7 +121,7 @@ _get_root() {
|
|||||||
i=1
|
i=1
|
||||||
p=1
|
p=1
|
||||||
while true; do
|
while true; do
|
||||||
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
|
||||||
_debug h "$h"
|
_debug h "$h"
|
||||||
if [ -z "$h" ]; then
|
if [ -z "$h" ]; then
|
||||||
#not valid
|
#not valid
|
||||||
@@ -130,7 +133,7 @@ _get_root() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if _contains "$response" "\"status\":200"; then
|
if _contains "$response" "\"status\":200"; then
|
||||||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
|
||||||
_domain=$h
|
_domain=$h
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|||||||
163
dnsapi/dns_edgecenter.sh
Normal file
163
dnsapi/dns_edgecenter.sh
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
dns_edgecenter_info='EdgeCenter.ru
|
||||||
|
Site: EdgeCenter.ru
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_edgecenter
|
||||||
|
Options:
|
||||||
|
EDGECENTER_API_KEY API Key
|
||||||
|
Issues: github.com/acmesh-official/acme.sh/issues/6313
|
||||||
|
Author: Konstantin Ruchev <konstantin.ruchev@edgecenter.ru>
|
||||||
|
'
|
||||||
|
|
||||||
|
EDGECENTER_API="https://api.edgecenter.ru"
|
||||||
|
DOMAIN_TYPE=
|
||||||
|
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
|
||||||
|
}
|
||||||
@@ -1,4 +1,15 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
dns_edgedns_info='Akamai.com Edge DNS
|
||||||
|
Site: techdocs.Akamai.com/edge-dns/reference/edge-dns-api
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_edgedns
|
||||||
|
Options: Specify individual credentials
|
||||||
|
AKAMAI_HOST Host
|
||||||
|
AKAMAI_ACCESS_TOKEN Access token
|
||||||
|
AKAMAI_CLIENT_TOKEN Client token
|
||||||
|
AKAMAI_CLIENT_SECRET Client secret
|
||||||
|
Issues: github.com/acmesh-official/acme.sh/issues/3157
|
||||||
|
'
|
||||||
|
|
||||||
# Akamai Edge DNS v2 API
|
# Akamai Edge DNS v2 API
|
||||||
# User must provide Open Edgegrid API credentials to the EdgeDNS installation. The remote user in EdgeDNS must have CRUD access to
|
# User must provide Open Edgegrid API credentials to the EdgeDNS installation. The remote user in EdgeDNS must have CRUD access to
|
||||||
@@ -6,18 +17,10 @@
|
|||||||
|
|
||||||
# Report bugs to https://control.akamai.com/apps/support-ui/#/contact-support
|
# Report bugs to https://control.akamai.com/apps/support-ui/#/contact-support
|
||||||
|
|
||||||
# Values to export:
|
|
||||||
# --EITHER--
|
|
||||||
# *** TBD. NOT IMPLEMENTED YET ***
|
# *** TBD. NOT IMPLEMENTED YET ***
|
||||||
# specify Edgegrid credentials file and section
|
# Specify Edgegrid credentials file and section.
|
||||||
# AKAMAI_EDGERC=<full file path>
|
# AKAMAI_EDGERC Edge RC. Full file path
|
||||||
# AKAMAI_EDGERC_SECTION="default"
|
# AKAMAI_EDGERC_SECTION Edge RC Section. E.g. "default"
|
||||||
## --OR--
|
|
||||||
# specify indiviual credentials
|
|
||||||
# export AKAMAI_HOST = <host>
|
|
||||||
# export AKAMAI_ACCESS_TOKEN = <access token>
|
|
||||||
# export AKAMAI_CLIENT_TOKEN = <client token>
|
|
||||||
# export AKAMAI_CLIENT_SECRET = <client secret>
|
|
||||||
|
|
||||||
ACME_EDGEDNS_VERSION="0.1.0"
|
ACME_EDGEDNS_VERSION="0.1.0"
|
||||||
|
|
||||||
|
|||||||
139
dnsapi/dns_efficientip.sh
Executable file
139
dnsapi/dns_efficientip.sh
Executable file
@@ -0,0 +1,139 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
dns_efficientip_info='efficientip.com
|
||||||
|
Site: https://efficientip.com/
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_efficientip
|
||||||
|
Options:
|
||||||
|
EfficientIP_Creds HTTP Basic Authentication credentials. E.g. "username:password"
|
||||||
|
EfficientIP_Server EfficientIP SOLIDserver Management IP address or FQDN.
|
||||||
|
EfficientIP_DNS_Name Name of the DNS smart or server hosting the zone. Optional.
|
||||||
|
EfficientIP_View Name of the DNS view hosting the zone. Optional.
|
||||||
|
OptionsAlt:
|
||||||
|
EfficientIP_Token_Key Alternative API token key, prefered over basic authentication.
|
||||||
|
EfficientIP_Token_Secret Alternative API token secret, required when using a token key.
|
||||||
|
EfficientIP_Server EfficientIP SOLIDserver Management IP address or FQDN.
|
||||||
|
EfficientIP_DNS_Name Name of the DNS smart or server hosting the zone. Optional.
|
||||||
|
EfficientIP_View Name of the DNS view hosting the zone. Optional.
|
||||||
|
Issues: github.com/acmesh-official/acme.sh/issues/6325
|
||||||
|
Author: EfficientIP-Labs <contact@efficientip.com>
|
||||||
|
'
|
||||||
|
|
||||||
|
dns_efficientip_add() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
_info "Using EfficientIP API"
|
||||||
|
_debug fulldomain "$fulldomain"
|
||||||
|
_debug txtvalue "$txtvalue"
|
||||||
|
|
||||||
|
if { [ -z "${EfficientIP_Creds}" ] && { [ -z "${EfficientIP_Token_Key}" ] || [ -z "${EfficientIP_Token_Secret}" ]; }; } || [ -z "${EfficientIP_Server}" ]; then
|
||||||
|
EfficientIP_Creds=""
|
||||||
|
EfficientIP_Token_Key=""
|
||||||
|
EfficientIP_Token_Secret=""
|
||||||
|
EfficientIP_Server=""
|
||||||
|
_err "You didn't specify any EfficientIP credentials or token or server (EfficientIP_Creds; EfficientIP_Token_Key; EfficientIP_Token_Secret; EfficientIP_Server)."
|
||||||
|
_err "Please set them via EXPORT EfficientIP_Creds=username:password or EXPORT EfficientIP_server=ip/hostname"
|
||||||
|
_err "or if you want to use Token instead EXPORT EfficientIP_Token_Key=yourkey"
|
||||||
|
_err "and EXPORT EfficientIP_Token_Secret=yoursecret"
|
||||||
|
_err "then try again."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "${EfficientIP_DNS_Name}" ]; then
|
||||||
|
EfficientIP_DNS_Name=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
EfficientIP_DNSNameEncoded=$(printf "%b" "${EfficientIP_DNS_Name}" | _url_encode)
|
||||||
|
|
||||||
|
if [ -z "${EfficientIP_View}" ]; then
|
||||||
|
EfficientIP_View=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
EfficientIP_ViewEncoded=$(printf "%b" "${EfficientIP_View}" | _url_encode)
|
||||||
|
|
||||||
|
_saveaccountconf EfficientIP_Creds "${EfficientIP_Creds}"
|
||||||
|
_saveaccountconf EfficientIP_Token_Key "${EfficientIP_Token_Key}"
|
||||||
|
_saveaccountconf EfficientIP_Token_Secret "${EfficientIP_Token_Secret}"
|
||||||
|
_saveaccountconf EfficientIP_Server "${EfficientIP_Server}"
|
||||||
|
_saveaccountconf EfficientIP_DNS_Name "${EfficientIP_DNS_Name}"
|
||||||
|
_saveaccountconf EfficientIP_View "${EfficientIP_View}"
|
||||||
|
|
||||||
|
export _H1="Accept-Language:en-US"
|
||||||
|
baseurlnObject="https://${EfficientIP_Server}/rest/dns_rr_add?rr_type=TXT&rr_ttl=300&rr_name=${fulldomain}&rr_value1=${txtvalue}"
|
||||||
|
|
||||||
|
if [ "${EfficientIP_DNSNameEncoded}" != "" ]; then
|
||||||
|
baseurlnObject="${baseurlnObject}&dns_name=${EfficientIP_DNSNameEncoded}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${EfficientIP_ViewEncoded}" != "" ]; then
|
||||||
|
baseurlnObject="${baseurlnObject}&dnsview_name=${EfficientIP_ViewEncoded}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "${EfficientIP_Token_Secret}" ] || [ -z "${EfficientIP_Token_Key}" ]; then
|
||||||
|
EfficientIP_CredsEncoded=$(printf "%b" "${EfficientIP_Creds}" | _base64)
|
||||||
|
export _H2="Authorization: Basic ${EfficientIP_CredsEncoded}"
|
||||||
|
else
|
||||||
|
TS=$(date +%s)
|
||||||
|
Sig=$(printf "%b\n$TS\nPOST\n$baseurlnObject" "${EfficientIP_Token_Secret}" | _digest sha3-256 hex)
|
||||||
|
EfficientIP_CredsEncoded=$(printf "%b:%b" "${EfficientIP_Token_Key}" "$Sig")
|
||||||
|
export _H2="Authorization: SDS ${EfficientIP_CredsEncoded}"
|
||||||
|
export _H3="X-SDS-TS: ${TS}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
result="$(_post "" "${baseurlnObject}" "" "POST")"
|
||||||
|
|
||||||
|
if [ "$(echo "${result}" | _egrep_o "ret_oid")" ]; then
|
||||||
|
_info "DNS record successfully created"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
_err "Error creating DNS record"
|
||||||
|
_err "${result}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
dns_efficientip_rm() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
_info "Using EfficientIP API"
|
||||||
|
_debug fulldomain "${fulldomain}"
|
||||||
|
_debug txtvalue "${txtvalue}"
|
||||||
|
|
||||||
|
EfficientIP_ViewEncoded=$(printf "%b" "${EfficientIP_View}" | _url_encode)
|
||||||
|
EfficientIP_DNSNameEncoded=$(printf "%b" "${EfficientIP_DNS_Name}" | _url_encode)
|
||||||
|
EfficientIP_CredsEncoded=$(printf "%b" "${EfficientIP_Creds}" | _base64)
|
||||||
|
|
||||||
|
export _H1="Accept-Language:en-US"
|
||||||
|
|
||||||
|
baseurlnObject="https://${EfficientIP_Server}/rest/dns_rr_delete?rr_type=TXT&rr_name=$fulldomain&rr_value1=$txtvalue"
|
||||||
|
if [ "${EfficientIP_DNSNameEncoded}" != "" ]; then
|
||||||
|
baseurlnObject="${baseurlnObject}&dns_name=${EfficientIP_DNSNameEncoded}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${EfficientIP_ViewEncoded}" != "" ]; then
|
||||||
|
baseurlnObject="${baseurlnObject}&dnsview_name=${EfficientIP_ViewEncoded}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$EfficientIP_Token_Secret" ] || [ -z "$EfficientIP_Token_Key" ]; then
|
||||||
|
EfficientIP_CredsEncoded=$(printf "%b" "${EfficientIP_Creds}" | _base64)
|
||||||
|
export _H2="Authorization: Basic $EfficientIP_CredsEncoded"
|
||||||
|
else
|
||||||
|
TS=$(date +%s)
|
||||||
|
Sig=$(printf "%b\n$TS\nDELETE\n${baseurlnObject}" "${EfficientIP_Token_Secret}" | _digest sha3-256 hex)
|
||||||
|
EfficientIP_CredsEncoded=$(printf "%b:%b" "${EfficientIP_Token_Key}" "$Sig")
|
||||||
|
export _H2="Authorization: SDS ${EfficientIP_CredsEncoded}"
|
||||||
|
export _H3="X-SDS-TS: $TS"
|
||||||
|
fi
|
||||||
|
|
||||||
|
result="$(_post "" "${baseurlnObject}" "" "DELETE")"
|
||||||
|
|
||||||
|
if [ "$(echo "${result}" | _egrep_o "ret_oid")" ]; then
|
||||||
|
_info "DNS Record successfully deleted"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
_err "Error deleting DNS record"
|
||||||
|
_err "${result}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
@@ -1,18 +1,14 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
#This is the euserv.eu api wrapper for acme.sh
|
dns_euserv_info='EUserv.com
|
||||||
#
|
Domains: EUserv.eu
|
||||||
#Author: Michael Brueckner
|
Site: EUserv.com
|
||||||
#Report Bugs: https://www.github.com/initit/acme.sh or mbr@initit.de
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_euserv
|
||||||
|
Options:
|
||||||
#
|
EUSERV_Username Username
|
||||||
#EUSERV_Username="username"
|
EUSERV_Password Password
|
||||||
#
|
Author: Michael Brueckner
|
||||||
#EUSERV_Password="password"
|
'
|
||||||
#
|
|
||||||
# Dependencies:
|
|
||||||
# -------------
|
|
||||||
# - none -
|
|
||||||
|
|
||||||
EUSERV_Api="https://api.euserv.net"
|
EUSERV_Api="https://api.euserv.net"
|
||||||
|
|
||||||
@@ -155,7 +151,7 @@ _get_root() {
|
|||||||
response="$_euserv_domain_orders"
|
response="$_euserv_domain_orders"
|
||||||
|
|
||||||
while true; do
|
while true; do
|
||||||
h=$(echo "$domain" | cut -d . -f $i-100)
|
h=$(echo "$domain" | cut -d . -f "$i"-100)
|
||||||
_debug h "$h"
|
_debug h "$h"
|
||||||
if [ -z "$h" ]; then
|
if [ -z "$h" ]; then
|
||||||
#not valid
|
#not valid
|
||||||
@@ -163,7 +159,7 @@ _get_root() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if _contains "$response" "$h"; then
|
if _contains "$response" "$h"; then
|
||||||
_sub_domain=$(echo "$domain" | cut -d . -f 1-$p)
|
_sub_domain=$(echo "$domain" | cut -d . -f 1-"$p")
|
||||||
_domain="$h"
|
_domain="$h"
|
||||||
if ! _euserv_get_domain_id "$_domain"; then
|
if ! _euserv_get_domain_id "$_domain"; then
|
||||||
_err "invalid domain"
|
_err "invalid domain"
|
||||||
|
|||||||
234
dnsapi/dns_exoscale.sh
Executable file → Normal file
234
dnsapi/dns_exoscale.sh
Executable file → Normal file
@@ -1,8 +1,16 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
dns_exoscale_info='Exoscale.com
|
||||||
|
Site: Exoscale.com
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_exoscale
|
||||||
|
Options:
|
||||||
|
EXOSCALE_API_KEY API Key
|
||||||
|
EXOSCALE_SECRET_KEY API Secret key
|
||||||
|
'
|
||||||
|
|
||||||
EXOSCALE_API=https://api.exoscale.com/dns/v1
|
EXOSCALE_API="https://api-ch-gva-2.exoscale.com/v2"
|
||||||
|
|
||||||
######## Public functions #####################
|
######## Public functions ########
|
||||||
|
|
||||||
# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
# Used to add txt record
|
# Used to add txt record
|
||||||
@@ -10,159 +18,197 @@ dns_exoscale_add() {
|
|||||||
fulldomain=$1
|
fulldomain=$1
|
||||||
txtvalue=$2
|
txtvalue=$2
|
||||||
|
|
||||||
if ! _checkAuth; then
|
_debug "Using Exoscale DNS v2 API"
|
||||||
|
_debug fulldomain "$fulldomain"
|
||||||
|
_debug txtvalue "$txtvalue"
|
||||||
|
|
||||||
|
if ! _check_auth; then
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
_debug "First detect the root zone"
|
root_domain_id=$(_get_root_domain_id "$fulldomain")
|
||||||
if ! _get_root "$fulldomain"; then
|
if [ -z "$root_domain_id" ]; then
|
||||||
_err "invalid domain"
|
_err "Unable to determine root domain ID for $fulldomain"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
_debug root_domain_id "$root_domain_id"
|
||||||
|
|
||||||
_debug _sub_domain "$_sub_domain"
|
# Always get the subdomain part first
|
||||||
_debug _domain "$_domain"
|
sub_domain=$(_get_sub_domain "$fulldomain" "$root_domain_id")
|
||||||
|
_debug sub_domain "$sub_domain"
|
||||||
|
|
||||||
_info "Adding record"
|
# Build the record name properly
|
||||||
if _exoscale_rest POST "domains/$_domain_id/records" "{\"record\":{\"name\":\"$_sub_domain\",\"record_type\":\"TXT\",\"content\":\"$txtvalue\",\"ttl\":120}}" "$_domain_token"; then
|
if [ -z "$sub_domain" ]; then
|
||||||
if _contains "$response" "$txtvalue"; then
|
record_name="_acme-challenge"
|
||||||
_info "Added, OK"
|
else
|
||||||
return 0
|
record_name="_acme-challenge.$sub_domain"
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
_err "Add txt record error."
|
|
||||||
return 1
|
|
||||||
|
|
||||||
|
payload=$(printf '{"name":"%s","type":"TXT","content":"%s","ttl":120}' "$record_name" "$txtvalue")
|
||||||
|
_debug payload "$payload"
|
||||||
|
|
||||||
|
response=$(_exoscale_rest POST "/dns-domain/${root_domain_id}/record" "$payload")
|
||||||
|
if _contains "$response" "\"id\""; then
|
||||||
|
_info "TXT record added successfully."
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
_err "Error adding TXT record: $response"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Usage: fulldomain txtvalue
|
|
||||||
# Used to remove the txt record after validation
|
|
||||||
dns_exoscale_rm() {
|
dns_exoscale_rm() {
|
||||||
fulldomain=$1
|
fulldomain=$1
|
||||||
txtvalue=$2
|
|
||||||
|
|
||||||
if ! _checkAuth; then
|
_debug "Using Exoscale DNS v2 API for removal"
|
||||||
|
_debug fulldomain "$fulldomain"
|
||||||
|
|
||||||
|
if ! _check_auth; then
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
_debug "First detect the root zone"
|
root_domain_id=$(_get_root_domain_id "$fulldomain")
|
||||||
if ! _get_root "$fulldomain"; then
|
if [ -z "$root_domain_id" ]; then
|
||||||
_err "invalid domain"
|
_err "Unable to determine root domain ID for $fulldomain"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
_debug _sub_domain "$_sub_domain"
|
record_name="_acme-challenge"
|
||||||
_debug _domain "$_domain"
|
sub_domain=$(_get_sub_domain "$fulldomain" "$root_domain_id")
|
||||||
|
if [ -n "$sub_domain" ]; then
|
||||||
_debug "Getting txt records"
|
record_name="_acme-challenge.$sub_domain"
|
||||||
_exoscale_rest GET "domains/${_domain_id}/records?type=TXT&name=$_sub_domain" "" "$_domain_token"
|
|
||||||
if _contains "$response" "\"name\":\"$_sub_domain\"" >/dev/null; then
|
|
||||||
_record_id=$(echo "$response" | tr '{' "\n" | grep "\"content\":\"$txtvalue\"" | _egrep_o "\"id\":[^,]+" | _head_n 1 | cut -d : -f 2 | tr -d \")
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -z "$_record_id" ]; then
|
record_id=$(_find_record_id "$root_domain_id" "$record_name")
|
||||||
_err "Can not get record id to remove."
|
if [ -z "$record_id" ]; then
|
||||||
|
_err "TXT record not found for deletion."
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
_debug "Deleting record $_record_id"
|
response=$(_exoscale_rest DELETE "/dns-domain/$root_domain_id/record/$record_id")
|
||||||
|
if _contains "$response" "\"state\":\"success\""; then
|
||||||
if ! _exoscale_rest DELETE "domains/$_domain_id/records/$_record_id" "" "$_domain_token"; then
|
_info "TXT record deleted successfully."
|
||||||
_err "Delete record error."
|
return 0
|
||||||
|
else
|
||||||
|
_err "Error deleting TXT record: $response"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
return 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#################### Private functions below ##################################
|
######## Private helpers ########
|
||||||
|
|
||||||
_checkAuth() {
|
_check_auth() {
|
||||||
EXOSCALE_API_KEY="${EXOSCALE_API_KEY:-$(_readaccountconf_mutable EXOSCALE_API_KEY)}"
|
EXOSCALE_API_KEY="${EXOSCALE_API_KEY:-$(_readaccountconf_mutable EXOSCALE_API_KEY)}"
|
||||||
EXOSCALE_SECRET_KEY="${EXOSCALE_SECRET_KEY:-$(_readaccountconf_mutable EXOSCALE_SECRET_KEY)}"
|
EXOSCALE_SECRET_KEY="${EXOSCALE_SECRET_KEY:-$(_readaccountconf_mutable EXOSCALE_SECRET_KEY)}"
|
||||||
|
|
||||||
if [ -z "$EXOSCALE_API_KEY" ] || [ -z "$EXOSCALE_SECRET_KEY" ]; then
|
if [ -z "$EXOSCALE_API_KEY" ] || [ -z "$EXOSCALE_SECRET_KEY" ]; then
|
||||||
EXOSCALE_API_KEY=""
|
_err "EXOSCALE_API_KEY and EXOSCALE_SECRET_KEY must be set."
|
||||||
EXOSCALE_SECRET_KEY=""
|
|
||||||
_err "You don't specify Exoscale application key and application secret yet."
|
|
||||||
_err "Please create you key and try again."
|
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
_saveaccountconf_mutable EXOSCALE_API_KEY "$EXOSCALE_API_KEY"
|
_saveaccountconf_mutable EXOSCALE_API_KEY "$EXOSCALE_API_KEY"
|
||||||
_saveaccountconf_mutable EXOSCALE_SECRET_KEY "$EXOSCALE_SECRET_KEY"
|
_saveaccountconf_mutable EXOSCALE_SECRET_KEY "$EXOSCALE_SECRET_KEY"
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
#_acme-challenge.www.domain.com
|
_get_root_domain_id() {
|
||||||
#returns
|
|
||||||
# _sub_domain=_acme-challenge.www
|
|
||||||
# _domain=domain.com
|
|
||||||
# _domain_id=sdjkglgdfewsdfg
|
|
||||||
# _domain_token=sdjkglgdfewsdfg
|
|
||||||
_get_root() {
|
|
||||||
|
|
||||||
if ! _exoscale_rest GET "domains"; then
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
domain=$1
|
domain=$1
|
||||||
i=2
|
i=1
|
||||||
p=1
|
|
||||||
while true; do
|
while true; do
|
||||||
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
candidate=$(printf "%s" "$domain" | cut -d . -f "${i}-100")
|
||||||
_debug h "$h"
|
[ -z "$candidate" ] && return 1
|
||||||
if [ -z "$h" ]; then
|
_debug "Trying root domain candidate: $candidate"
|
||||||
#not valid
|
domains=$(_exoscale_rest GET "/dns-domain")
|
||||||
return 1
|
# Extract from dns-domains array
|
||||||
fi
|
result=$(echo "$domains" | _egrep_o '"dns-domains":\[.*\]' | _egrep_o '\{"id":"[^"]*","created-at":"[^"]*","unicode-name":"[^"]*"\}' | while read -r item; do
|
||||||
|
name=$(echo "$item" | _egrep_o '"unicode-name":"[^"]*"' | cut -d'"' -f4)
|
||||||
if _contains "$response" "\"name\":\"$h\"" >/dev/null; then
|
id=$(echo "$item" | _egrep_o '"id":"[^"]*"' | cut -d'"' -f4)
|
||||||
_domain_id=$(echo "$response" | tr '{' "\n" | grep "\"name\":\"$h\"" | _egrep_o "\"id\":[^,]+" | _head_n 1 | cut -d : -f 2 | tr -d \")
|
if [ "$name" = "$candidate" ]; then
|
||||||
_domain_token=$(echo "$response" | tr '{' "\n" | grep "\"name\":\"$h\"" | _egrep_o "\"token\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \")
|
echo "$id"
|
||||||
if [ "$_domain_token" ] && [ "$_domain_id" ]; then
|
break
|
||||||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
|
||||||
_domain=$h
|
|
||||||
return 0
|
|
||||||
fi
|
fi
|
||||||
return 1
|
done)
|
||||||
|
if [ -n "$result" ]; then
|
||||||
|
echo "$result"
|
||||||
|
return 0
|
||||||
fi
|
fi
|
||||||
p=$i
|
|
||||||
i=$(_math "$i" + 1)
|
i=$(_math "$i" + 1)
|
||||||
done
|
done
|
||||||
return 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# returns response
|
_get_sub_domain() {
|
||||||
|
fulldomain=$1
|
||||||
|
root_id=$2
|
||||||
|
root_info=$(_exoscale_rest GET "/dns-domain/$root_id")
|
||||||
|
_debug root_info "$root_info"
|
||||||
|
root_name=$(echo "$root_info" | _egrep_o "\"unicode-name\":\"[^\"]*\"" | cut -d\" -f4)
|
||||||
|
sub=${fulldomain%%."$root_name"}
|
||||||
|
|
||||||
|
if [ "$sub" = "_acme-challenge" ]; then
|
||||||
|
echo ""
|
||||||
|
else
|
||||||
|
# Remove _acme-challenge. prefix to get the actual subdomain
|
||||||
|
echo "${sub#_acme-challenge.}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_find_record_id() {
|
||||||
|
root_id=$1
|
||||||
|
name=$2
|
||||||
|
records=$(_exoscale_rest GET "/dns-domain/$root_id/record")
|
||||||
|
|
||||||
|
# Convert search name to lowercase for case-insensitive matching
|
||||||
|
name_lower=$(echo "$name" | tr '[:upper:]' '[:lower:]')
|
||||||
|
|
||||||
|
echo "$records" | _egrep_o '\{[^}]*"name":"[^"]*"[^}]*\}' | while read -r record; do
|
||||||
|
record_name=$(echo "$record" | _egrep_o '"name":"[^"]*"' | cut -d'"' -f4)
|
||||||
|
record_name_lower=$(echo "$record_name" | tr '[:upper:]' '[:lower:]')
|
||||||
|
if [ "$record_name_lower" = "$name_lower" ]; then
|
||||||
|
echo "$record" | _egrep_o '"id":"[^"]*"' | _head_n 1 | cut -d'"' -f4
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
_exoscale_sign() {
|
||||||
|
k=$1
|
||||||
|
shift
|
||||||
|
hex_key=$(printf %b "$k" | _hex_dump | tr -d ' ')
|
||||||
|
printf %s "$@" | _hmac sha256 "$hex_key"
|
||||||
|
}
|
||||||
|
|
||||||
_exoscale_rest() {
|
_exoscale_rest() {
|
||||||
method=$1
|
method=$1
|
||||||
path="$2"
|
path=$2
|
||||||
data="$3"
|
data=$3
|
||||||
token="$4"
|
|
||||||
request_url="$EXOSCALE_API/$path"
|
url="${EXOSCALE_API}${path}"
|
||||||
_debug "$path"
|
expiration=$(_math "$(date +%s)" + 300) # 5m from now
|
||||||
|
|
||||||
|
# Build the message with the actual body or empty line
|
||||||
|
message=$(printf "%s %s\n%s\n\n\n%s" "$method" "/v2$path" "$data" "$expiration")
|
||||||
|
signature=$(_exoscale_sign "$EXOSCALE_SECRET_KEY" "$message" | _base64)
|
||||||
|
auth="EXO2-HMAC-SHA256 credential=${EXOSCALE_API_KEY},expires=${expiration},signature=${signature}"
|
||||||
|
|
||||||
|
_debug "API request: $method $url"
|
||||||
|
_debug "Signed message: [$message]"
|
||||||
|
_debug "Authorization header: [$auth]"
|
||||||
|
|
||||||
export _H1="Accept: application/json"
|
export _H1="Accept: application/json"
|
||||||
|
export _H2="Authorization: ${auth}"
|
||||||
if [ "$token" ]; then
|
|
||||||
export _H2="X-DNS-Domain-Token: $token"
|
|
||||||
else
|
|
||||||
export _H2="X-DNS-Token: $EXOSCALE_API_KEY:$EXOSCALE_SECRET_KEY"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$data" ] || [ "$method" = "DELETE" ]; then
|
if [ "$data" ] || [ "$method" = "DELETE" ]; then
|
||||||
export _H3="Content-Type: application/json"
|
export _H3="Content-Type: application/json"
|
||||||
_debug data "$data"
|
_debug data "$data"
|
||||||
response="$(_post "$data" "$request_url" "" "$method")"
|
response="$(_post "$data" "$url" "" "$method")"
|
||||||
else
|
else
|
||||||
response="$(_get "$request_url" "" "" "$method")"
|
response="$(_get "$url" "" "" "$method")"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$?" != "0" ]; then
|
# shellcheck disable=SC2181
|
||||||
_err "error $request_url"
|
if [ "$?" -ne 0 ]; then
|
||||||
|
_err "error $url"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
_debug2 response "$response"
|
_debug2 response "$response"
|
||||||
|
echo "$response"
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user